Files
docs/migration/DATAMIGRATION-README.md
T
masoodafar-web d7c32dab2a consolidate: move all project-level docs to totalDoc
- BackOffice/docs → totalDoc/backoffice/ (AUDIT + CHANGELOG)
- FrontOffice/docs → totalDoc/frontoffice/ (CHANGELOG + UI-UNIFICATION-PLAN)
- CMS/README.md → totalDoc/cms/CMS-README.md
- DataMigration/README.md → totalDoc/migration/DATAMIGRATION-README.md
- deployment/README.md → totalDoc/deployment/DEPLOYMENT-README.md
- Updated INDEX.md with new paths and sections
2026-02-18 21:43:59 +03:30

426 lines
12 KiB
Markdown

# FourSat Data Migration Tool
## نگاه کلی
این ابزار برای مهاجرت داده‌های دیتابیس از ساختار قدیمی (Production) به ساختار جدید (Stage) طراحی شده است.
**ویژگی‌ها:**
- ✅ Queue-based processing با retry logic
- ✅ Error handling - آیتم‌های ناموفق به صف retry می‌روند
- ✅ Logging کامل با Serilog (Console + File)
- ✅ قابلیت توقف/ادامه (Pause/Resume)
- ✅ Table name mapping (مثل Categorys → Categories)
- ✅ Batch processing برای کارایی بهتر
- ✅ Retry با Exponential Backoff
- ✅ Progress tracking
---
## ساختار پروژه
```
FourSat.DataMigration/
├── Program.cs # Entry point با Hosting
├── appsettings.json # تنظیمات (ConnectionStrings, Mappings)
├── Models/
│ ├── MigrationSettings.cs # تنظیمات migration
│ ├── TableMapping.cs # نگاشت table ها
│ └── MigrationQueueItem.cs # آیتم صف
├── Services/
│ ├── IMigrationService.cs # Interface
│ ├── MigrationService.cs # سرویس اصلی migration
│ ├── QueueManager.cs # مدیریت صف و retry
│ └── TableMigrator.cs # مهاجرت یک table
└── Logs/ # لاگ فایل‌ها (auto-created)
```
---
## تنظیمات (`appsettings.json`)
### 1. ConnectionStrings
```json
{
"SourceDatabase": "Server=185.252.31.42,2019;Database=Foursat;...",
"TargetDatabase": "Server=194.5.195.53,31433;Database=Foursat;..."
}
```
**⚠️ توجه**: حتماً Username و Password را وارد کنید!
### 2. MigrationSettings
- **BatchSize**: تعداد رکوردهای هر batch (پیشنهاد: 1000)
- **MaxRetryAttempts**: حداکثر تلاش مجدد (5 بار)
- **RetryDelaySeconds**: تأخیر بین retry ها (5 ثانیه)
- **MaxConcurrentTables**: تعداد table های همزمان (3 عدد)
- **EnableDetailedLogging**: لاگ جزئیات (true)
- **SkipEmptyTables**: نادیده گرفتن table های خالی (true)
### 3. TableMappings
نگاشت نام table قدیمی به جدید (33 جدول):
```json
{
"Categorys": "Categories",
"ClubFeatures": "ClubFeatures",
"ClubMembershipHistories": "ClubMembershipHistories",
"ClubMemberships": "ClubMemberships",
"CommissionPayoutHistories": "CommissionPayoutHistories",
"Contracts": "Contracts",
"FactorDetailss": "FactorDetails",
"NetworkMembershipHistories": "NetworkMembershipHistories",
"NetworkWeeklyBalances": "NetworkWeeklyBalances",
"OtpTokens": "OtpTokens",
"Packages": "Packages",
"ProductGalleryss": "ProductGalleries",
"ProductImagess": "ProductImages",
"Productss": "Products",
"PruductCategorys": "ProductCategories",
"PruductTags": "ProductTags",
"Roles": "Roles",
"SystemConfigurationHistories": "SystemConfigurationHistories",
"SystemConfigurations": "SystemConfigurations",
"Tags": "Tags",
"Transactionss": "Transactions",
"UserAddresss": "UserAddresses",
"UserCartss": "UserCarts",
"UserClubFeatures": "UserClubFeatures",
"UserCommissionPayouts": "UserCommissionPayouts",
"UserContracts": "UserContracts",
"UserOrders": "UserOrders",
"UserRoles": "UserRoles",
"Users": "Users",
"UserWalletChangeLogs": "UserWalletChangeLogs",
"UserWallets": "UserWallets",
"WeeklyCommissionPools": "WeeklyCommissionPools",
"WorkerExecutionLogs": "WorkerExecutionLogs"
}
```
**چگونه کار می‌کند:**
- اگر table در mapping باشد → از نام جدید استفاده می‌کند
- اگر در mapping نباشد → همان نام را استفاده می‌کند
- اگر table در target نباشد → Log می‌کند و skip می‌کند
---
## نحوه اجرا
### 1. ویرایش appsettings.json
```bash
cd /home/masoud/Apps/project/FourSat/DataMigration/FourSat.DataMigration
nano appsettings.json
```
**تغییرات لازم:**
-`SourceDatabase`: Username و Password را وارد کنید
-`TargetDatabase`: Username و Password را وارد کنید
-`TableMappings`: اگر mapping جدید دارید اضافه کنید
### 2. Build پروژه
```bash
dotnet build
```
### 3. اجرای Migration
```bash
dotnet run
```
### 4. مشاهده Logs
```bash
# Real-time console output
# یا
tail -f Logs/migration-20251206.txt
```
---
## جریان کار (Workflow)
```
1. خواندن تنظیمات از appsettings.json
2. اتصال به Source و Target databases
3. کشف تمام table های Source (CMS schema)
4. برای هر table:
├─ بررسی mapping (قدیمی → جدید)
├─ تعداد رکوردها را بخواند
├─ اگر خالی → skip (با log)
├─ اگر پر → افزودن به Queue
└─ Log: "Table X → Y: N records"
5. پردازش Queue:
├─ تا MaxConcurrentTables همزمان
├─ هر table در batch ها (BatchSize)
├─ اگر error → Retry (MaxRetryAttempts)
├─ اگر بعد از retry fail → Log + Skip
└─ پیشرفت را نمایش بده
6. گزارش نهایی:
├─ تعداد table های موفق
├─ تعداد table های ناموفق
├─ جمع رکوردهای migrate شده
└─ مدت زمان کل
```
---
## Retry Logic
### استراتژی:
1. **اولین تلاش**: بلافاصله
2. **تلاش 2**: بعد از 5 ثانیه
3. **تلاش 3**: بعد از 10 ثانیه (exponential backoff)
4. **تلاش 4**: بعد از 20 ثانیه
5. **تلاش 5**: بعد از 40 ثانیه
**اگر همه fail شوند:**
- Log error با جزئیات کامل
- Table را از queue حذف کن
- به table بعدی برو (متوقف نمی‌شود!)
---
## Error Handling
### خطاهای رایج:
| خطا | دلیل | راه حل |
|-----|------|--------|
| **Login failed** | Username/Password اشتباه | appsettings.json را بررسی کنید |
| **Table not found** | Table در target وجود ندارد | Migration بزنید یا از mapping صحیح استفاده کنید |
| **Timeout** | Network کند یا batch زیاد | BatchSize را کاهش دهید |
| **Deadlock** | همزمانی بالا | MaxConcurrentTables را کم کنید |
| **Permission denied** | User دسترسی ندارد | سطح دسترسی SQL را بررسی کنید |
---
## مثال خروجی
```
[12:30:15 INF] Starting migration...
[12:30:16 INF] Source: 30 tables found
[12:30:16 INF] Mapping: Categorys → Categories
[12:30:16 INF] Mapping: Productss → Products
[12:30:17 INF] Queue: 28 tables added (2 empty skipped)
[12:30:18 INF] Migrating: Categories (6 records)
[12:30:18 INF] Success: Categories (6/6) - 100%
[12:30:19 INF] Migrating: Products (150 records)
[12:30:21 INF] Success: Products (150/150) - 100%
...
[12:35:42 INF] === Migration Complete ===
[12:35:42 INF] Success: 28 tables, 45,320 records
[12:35:42 INF] Failed: 0 tables
[12:35:42 INF] Duration: 5 minutes 27 seconds
```
---
## فایل‌های باقی مانده برای پیاده‌سازی
### Models/MigrationSettings.cs
```csharp
public class MigrationSettings
{
public int BatchSize { get; set; } = 1000;
public int MaxRetryAttempts { get; set; } = 5;
public int RetryDelaySeconds { get; set; } = 5;
public int MaxConcurrentTables { get; set; } = 3;
public bool EnableDetailedLogging { get; set; } = true;
public bool SkipEmptyTables { get; set; } = true;
}
```
### Models/TableMapping.cs
```csharp
public class TableMapping
{
public string SourceTable { get; set; } = string.Empty;
public string TargetTable { get; set; } = string.Empty;
public long TotalRecords { get; set; }
public long MigratedRecords { get; set; }
public MigrationStatus Status { get; set; }
}
public enum MigrationStatus
{
Pending,
InProgress,
Completed,
Failed,
Retrying
}
```
### Models/MigrationQueueItem.cs
```csharp
public class MigrationQueueItem
{
public string SourceTable { get; set; } = string.Empty;
public string TargetTable { get; set; } = string.Empty;
public long TotalRecords { get; set; }
public int RetryCount { get; set; }
public DateTime? LastAttempt { get; set; }
public string? LastError { get; set; }
}
```
### Services/IMigrationService.cs
```csharp
public interface IMigrationService
{
Task RunAsync(CancellationToken cancellationToken);
}
```
### Services/MigrationService.cs
```csharp
public class MigrationService : IMigrationService
{
// کلاس اصلی که:
// 1. لیست table ها را از source می‌خواند
// 2. QueueManager را راه‌اندازی می‌کند
// 3. TableMigrator ها را همزمان اجرا می‌کند
// 4. Progress و statistics را نمایش می‌دهد
}
```
### Program.cs
```csharp
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
var host = Host.CreateDefaultBuilder(args)
.UseSerilog((context, config) => config.ReadFrom.Configuration(context.Configuration))
.ConfigureServices((context, services) =>
{
services.Configure<MigrationSettings>(context.Configuration.GetSection("MigrationSettings"));
services.AddSingleton<IMigrationService, MigrationService>();
// Register other services...
})
.Build();
await host.Services.GetRequiredService<IMigrationService>().RunAsync(CancellationToken.None);
```
---
## توقف و ادامه (Pause/Resume)
**نحوه توقف:**
```bash
Ctrl+C # Graceful shutdown
```
**نحوه ادامه:**
- هیچ state ذخیره نمی‌شود (stateless)
- دوباره `dotnet run` کنید
- چون `INSERT` استفاده می‌شود، رکوردهای duplicate ایجاد می‌شود
- **پیشنهاد**: قبل از اجرای مجدد، Target را TRUNCATE کنید
**برای Production:**
- از `MERGE` یا `INSERT IF NOT EXISTS` استفاده کنید
- یک جدول `MigrationState` برای ذخیره پیشرفت ایجاد کنید
---
## نکات امنیتی
1. **Credentials**:
- ❌ هرگز appsettings.json را commit نکنید
- ✅ از Environment Variables یا User Secrets استفاده کنید
2. **Network**:
- ✅ از VPN برای اتصال به Production استفاده کنید
- ✅ IP شما در Firewall مجاز باشد
3. **Permissions**:
- Source: فقط `SELECT` کافی است
- Target: نیاز به `INSERT` دارد
---
## بهینه‌سازی عملکرد
### برای دیتابیس کوچک (<100K records):
```json
{
"BatchSize": 5000,
"MaxConcurrentTables": 5
}
```
### برای دیتابیس متوسط (100K-1M):
```json
{
"BatchSize": 2000,
"MaxConcurrentTables": 3
}
```
### برای دیتابیس بزرگ (>1M):
```json
{
"BatchSize": 500,
"MaxConcurrentTables": 2
}
```
---
## حذف یا خاموش کردن
### خاموش کردن موقت:
```bash
# فقط اجرا نکنید!
```
### حذف کامل:
```bash
cd /home/masoud/Apps/project/FourSat
rm -rf DataMigration/
```
---
## لایسنس
این ابزار موقت برای استفاده داخلی FourSat است. بعد از sync کامل، حذف شود.
---
## سوالات متداول (FAQ)
**Q: چرا بعضی table ها migrate نمی‌شوند؟**
A: چک کنید:
1. Table در Target وجود دارد؟
2. Schema match می‌کند؟
3. Mapping صحیح است؟
**Q: چگونه فقط یک table خاص را migrate کنم؟**
A: در کد `MigrationService.cs`، فیلتر اضافه کنید:
```csharp
var tablesToMigrate = allTables.Where(t => t == "Users").ToList();
```
**Q: چگونه از duplicate جلوگیری کنم؟**
A: قبل از اجرا، Target را خالی کنید:
```sql
TRUNCATE TABLE [CMS].[Categories];
TRUNCATE TABLE [CMS].[Products];
-- ...
```
**Q: آیا می‌توانم بدون توقف سرور اجرا کنم؟**
A: بله، فقط `SELECT` روی Source اجرا می‌شود (ReadOnly).
---
**آخرین بروزرسانی**: December 6, 2025
**نسخه**: 1.0
**وضعیت**: آماده برای پیاده‌سازی نهایی