d7c32dab2a
- 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
426 lines
12 KiB
Markdown
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
|
|
**وضعیت**: آماده برای پیادهسازی نهایی
|