تبدیل Git به یک راه حل پشتیبان گیری فایل های بزرگ

توجه داشته باشید: این کد در عین حال که کار می کند، بسیار گواه مفهوم است و به همین ترتیب دارای اشکالات زیادی است. من همچنین قصد ندارم اینها را تعمیر کنم زیرا این یک ایده بسیار احمقانه است. با این حال، فکر کردم به اندازه کافی جالب بود که به اشتراک بگذارم.
زمینه
این همه چیز از چند درک حاصل شد:
- GitHub حداکثر 100 مگابایت در هر فایل را قبل از اینکه آپلودها را مسدود کند اجازه می دهد
- بر اساس این پاسخ از Stack Overflow، “محدودیت سخت” برای اندازه یک مخزن در GitHub 100 گیگابایت است، اما واقعاً باید زیر 5 گیگابایت (و ترجیحاً 1 گیگابایت) نگه داشته شود – اگرچه یافتن اطلاعات در این مورد کمی دشوار است.
- GitHub به شما امکان می دهد تعداد نامحدودی مخزن داشته باشید
- یک نقطه پایانی وجود دارد که به شما امکان می دهد یک مخزن GitHub را به صورت برنامه ریزی شده راه اندازی کنید
- فایل های zip و tar هر دو این قابلیت را دارند که به راحتی از هم جدا شوند
بنابراین، تقریباً از همه اینها، آیا میتوانم چیزی بسازم که بتوانم در یک فایل بزرگ بگیرم، آن را به تکههای قابل مدیریت تقسیم کنم، آن را از طریق Git آپلود کنم و زمانی که نیاز به بازیابی از پشتیبان دارم، همه چیز را دوباره سرهم کنم؟
شما در مورد GitHub صحبت می کنید، چرا از Git استفاده می کنید؟
این به این فرض ساده منتهی می شود که احتمالاً GitHub از اینکه من این کار را انجام دهم چندان راضی نخواهد بود، بنابراین بهتر است اصلاً این کار را انجام ندهم.
TL;DR – بله می توانم
مخزن اینجا:
GitBackup شامل دستورات زیر است:
-r, --restore Restore files.
-b, --backup Backup files.
--help Display this help screen.
--version Display version information.
علاوه بر آرگومانهای خط فرمان، یک appSettings.json وجود دارد که میتوان آن را در فهرست اجرایی تنظیم کرد تا نحوه عملکرد برنامه را بیشتر تغییر دهد. این همان چیزی است که این فایل با تمام پیش فرض ها به نظر می رسد:
{
"AppSettings": {
"FilesToBackupLocation": "<current folder>"
"BackupLocation": "<current folder>\\backup",
"RestoreLocation": "<current folder>\\backup",
"FilesToRestoreLocation": "<current folder>\\restoreTestFolder",
"RecursiveFileBackup": false,
"GitSettings": {
"RepositoryNamingConvention": "gitBackup-",
"RepositorySizeInKilobytes": 800000,
"GitRemoteLocation": "", // this value will need to be set at least
"OverwriteOnAdd": true,
"Username": null,
"Email": null,
…
ویدیو در حال اجرا در اینجا:
چرا؟
راستش، به نظر یک چالش سرگرم کننده بود و تقریباً همین. در واقع، این یک ایده وحشتناک برای انتخاب به عنوان یک استراتژی پشتیبان است. سرعت آن فوقالعاده کند است (ویدئوی بالا بیش از 1 دقیقه منتظر انتقال فایلها است به صورت محلی) و اگر چیزی در مورد Git می دانید، فایل های باینری (مانند فایل های فشرده) هستند وحشتناک چیزی که از Git برای ذخیره استفاده می کند. این به این دلیل است که Git با تجزیه و تحلیل تفاوت ها در فایل ها و ذخیره آنها کار می کند. اگر از باینری ها استفاده می کنید، به این معنی است که git باید مقدار احمقانه ای از داده های اضافی را ذخیره کند تا کار کند. علاوه بر این، به دلیل نحوه نوشتن این نرم افزار، شما می توانید تا 4 کپی از همان داده ها روی دیسک به طور همزمان، که بسیار بد است. در نهایت، باید گفت که اگر واقعاً این کار را انجام میدهید، با تکیه بر شرکتی که فقط همه فایلهای شما را حذف نمیکند، حتی تلاش برای ذخیره هر چیزی به این روش ایده بدی است.
من می دانم که این بخش بیشتر دلیل آن است نه برای انجام این کار، به همین دلیل است که من واقعاً هیچ بهانه ای برای انجام این کار ندارم، جز اینکه بگویم می توانم.
انتخاب ابزار
من در درجه اول یک توسعه دهنده C# هستم، بنابراین از C# استفاده می کنم و با توجه به اینکه با Git CLI تعامل خواهم داشت، ساده ترین پروژه برای انتخاب یک برنامه کنسول است. علاوه بر این، من تصمیم گرفتم با استفاده از zip روی TAR از ویندوز استفاده کنم. من همچنین به یک پایگاه داده برای ذخیره جایی که فایل ها در GitHub زندگی می کنند نیاز دارم و با توجه به اینکه در حال حاضر همه چیز را در فایل ها ذخیره می کنم و باید آنها را جابجا کنم، یک پایگاه داده مبتنی بر فایل بهترین گزینه من بود، و از این SQLite قبلا، بنابراین این ابزار انتخابی من بود
فلوچارت ها
در این مرحله من نیاز داشتم که جریان برنامه را در ذهنم تجسم کنم. تصمیم گرفتم برای شروع به 2 دستور نیاز دارم، backup
و restore
. با توجه به پیچیدگی نسبتاً زیادی در مورد نحوه دقیق عملکرد هر دستور، در اینجا فلوچارت هایی وجود دارد که آن را توضیح می دهند:
پشتیبان گیری
بازگرداندن
ساختار پایگاه داده
همانطور که گفتم، من به یک پایگاه داده ساده نیاز داشتم تا جایی که همه چیز در مخازن زندگی می کند را پیگیری کنم و چون می خواهم از آن فایل نسخه پشتیبان تهیه کنم، بهترین انتخاب برای من استفاده از SQLite بود. تصمیم گرفتم با 3 میز بروم:
ورودی های مانیفست: جزئیات مربوط به هر فایل را در خود دارد.
ورودی های مخزن: جزئیات مربوط به هر مخزن ردیابی شده را در اختیار دارد
ورودی های فایل فشرده: جزئیات مربوط به فایل فشرده و همچنین ورودی مانیفست و ورودی مخزن که به فایل فشرده پیوست شده است را در خود نگه می دارد.
این با چیزی شبیه به این مطابقت دارد:
ساختار کد
من برای ایجاد این پایه کد به شدت به رابط ها وابسته بودم. احتمالاً سادهترین کار این است که فقط نمودار کلاس را برای این کد زیر نشان دهید. اگر علاقه مند به کاوش در خود کد هستید، پیوند آن در بالا آمده است.
بسته ها
در درون خود کد، از چند بسته استفاده کردم تا زندگی را برای خودم آسانتر کنم. موارد اصلی انتخاب شده در اینجا فهرست شده اند:
- CommandLineParser
- ساختارهای نحوی بهتری را برای آرگومان های خط فرمان ایجاد می کند، مانند -b برای پشتیبان گیری —
- DotNetZip
- کتابخانه محبوب برای کار با فایل های فشرده در Net
- این کتابخانه به دلیل پشتیبانی از تقسیم فایل ها انتخاب شده است
- LibGit2Sharp
- این یک کتابخانه برای کار با Git در سی شارپ است
- هسته چارچوب نهاد
- ORM معمولی C# برای پیوند دادن اشیاء در کد به پایگاه داده SQLite استفاده می شود
- یکی از چیزهای خوب در مورد این پیاده سازی، این است که می توانم EnsureCreated() را برای ایجاد یک پایگاه داده در صورتی که قبلا وجود نداشته باشد فراخوانی کنم.
- سریلوگ
- TestableIO.System.IO.Abstractions
- این کتابخانه نسخه انتزاعی System.IO را ایجاد می کند که آنها را بسیار قابل آزمایش تر می کند
چالش ها
در هنگام ساختن این کد با چند مشکل مواجه شدم – فکر کردم در صورتی که کسی با چیزی مشابه مواجه شود اینها را بیرون بیاندازم
کار با فایل سیستم ها
من باید کار می کردم به شدت با فایل سیستم این پروژه همه چیز از کتابخانه zip خروجی مستقیم به فایل ها گرفته تا جابجایی اشیا برای مخازن git. در حالی که کد به خودی خود کار خاصی انجام نمی دهد، System.IO در سی شارپ کاملا غیر قابل آزمایش است و من می خواستم چند تست برای این بنویسم. در نتیجه، من به روشی برای آزمایش سیستم فایل نیاز داشتم. من به استفاده از کتابخانه TestableIO اشاره کردم که به شما امکان می دهد به جای نوشتن wrapper خودم، از تزریق وابستگی در کلاس رابط استفاده کنید.
SQLite Write-Ahead Logging
اگر مطمئن نیستید که ثبت پیش از نوشتن چیست، توضیح کاملی در اینجا وجود دارد. در اصل، این راهی برای پیاده سازی بازگشت ATOMIC در SQLite است. با این حال، نحوه کار به این صورت است که commit های جدید به پایگاه داده در یک فایل جداگانه نگهداری می شوند، و سپس گهگاه در فرآیندی به نام “چک پوینت” به پایگاه داده اضافه می شوند. متأسفانه، از آنجایی که من مدت کوتاهی پس از نوشتن، فایل پایگاه داده را جابجا میکنم، مدت زمان بیشتری که منتظر ماندن برای بررسی نقطهای بود به این معنی بود که نسخهای از پایگاه داده را آپلود میکردم که حاوی آخرین تغییرات نیست. در حالی که من سعی کردم زمانی که چک پوینت رخ می دهد تغییر دهم، تصمیم گرفتم به طور کامل از مشکلات جلوگیری کنم و این ویژگی را خاموش کردم.
در حال اجرای کد
GitBackup شامل دستورات زیر است:
-r, --restore Restore files.
-b, --backup Backup files.
--help Display this help screen.
--version Display version information.
علاوه بر آرگومانهای خط فرمان، یک appSettings.json وجود دارد که میتوان آن را در فهرست اجرایی تنظیم کرد تا نحوه عملکرد برنامه را بیشتر تغییر دهد. این همان چیزی است که این فایل با تمام پیش فرض ها به نظر می رسد:
{
"AppSettings": {
"FilesToBackupLocation": "<current folder>",
"BackupLocation": "<current folder>\\backup",
"RestoreLocation": "<current folder>\\backup",
"FilesToRestoreLocation": "<current folder>\\restoreTestFolder",
"RecursiveFileBackup": false,
"GitSettings": {
"RepositoryNamingConvention": "gitBackup-",
"RepositorySizeInKilobytes": 800000,
"GitRemoteLocation": "", // this value will need to be set at least
"OverwriteOnAdd": true,
"Username": null,
"Email": null,
"Password": null
},
"FileCompressionSettings": {
"FileSizeInKilobytes": 80000
}
}
}
در آینده
بنابراین اکنون این را نوشتم و ثابت کردم که تقریباً بی فایده است، به احتمال زیاد به این پروژه باز نمی گردم. با این حال، اگر من بودم، چند پیشرفت وجود دارد که میتوان به سرعت انجام داد.
- ثبت اضافی
- من تعدادی اضافه کردم، اما بسیار ضعیف است
- مدیریت بهتر خطا
- این به سرعت ساخته شد و به همین دلیل رسیدگی به خطا بسیار ابتدایی است
- تست های بیشتر
- من چند مورد نوشته ام، اما برای نوشتن یک مجموعه آزمایشی کاملا قوی به چیزهای بیشتری نیاز دارم
- چند رشته ای
- مسلماً اکثر اوقات اجرای این برنامه در انتظار Git است، اما نوشتن در توانایی بستهبندی/باز کردن فایلهای فشرده و آپلود مخازن git به صورت چند رشتهای احتمالاً اجرای این کد را به سرعت افزایش میدهد.