تو این پست یه توضیح کوتاهی میخوام راجع به
بگم که
سوال:
توی برنامههای concurrent چند execution flow داریم:
Thread
Process
Coroutine / Task
وقتی چند execution flow به منابع مشترک دسترسی داشته باشن:
فایل
Database
API
Shared state
Socket
Cache
نیاز داریم دسترسیشون رو مدیریت و محدود کنیم درغیراین صورت مشکلاتی به وجود میاد مثل race condition و فشار زیاد روی منابع
چند نمونه معروف
•
•
•
•
یه مدل ذهنی خوب براش اینه که بهش مثل پارکینگ نگاه کنید:
فرض کنید پارکینگ ۳ جا داره؛ تا وقتی جا خالی هست ماشین وارد میشه، ولی ماشین چهارم باید منتظر بمونه تا یکی خارج بشه.
استفاده سادهش هم به این شکل هست:
توی این کد وقتی اجرا میشه فقط سه تا task همزمان اجرا میشن و بقیه taskها صبر میکنن تا یکی از taskها تموم بشه و یه slot خالی بشه.
یعنی اول یک slot میگیره و وقتی کار تموم شد slot رو آزاد میکنه.
یه سینتکس دیگه هم داره که به صورت دستی میتونید انجام بدید، شبیه بقیه context manager ها ولی خب آدم سالم تا وقتی context manager هست دستی انجام نمیده 😄
Semaphore توی asyncio بدم.بگم که
Semaphore یک synchronization primitive هست.سوال:
synchronization primitive چیه؟توی برنامههای concurrent چند execution flow داریم:
Thread
Process
Coroutine / Task
وقتی چند execution flow به منابع مشترک دسترسی داشته باشن:
فایل
Database
API
Shared state
Socket
Cache
نیاز داریم دسترسیشون رو مدیریت و محدود کنیم درغیراین صورت مشکلاتی به وجود میاد مثل race condition و فشار زیاد روی منابع
چند نمونه معروف
synchronization primitive : •
Lock → فقط یک نفر همزمان وارد بشه•
Semaphore → اجازه بده N نفر همزمان وارد بشن•
Event → به بقیه خبر بده یه اتفاق افتاده•
Condition → ترکیب Lock و Eventیه مدل ذهنی خوب براش اینه که بهش مثل پارکینگ نگاه کنید:
فرض کنید پارکینگ ۳ جا داره؛ تا وقتی جا خالی هست ماشین وارد میشه، ولی ماشین چهارم باید منتظر بمونه تا یکی خارج بشه.
استفاده سادهش هم به این شکل هست:
import asyncio
sem = asyncio.Semaphore(3)
async def my_coroutine(sem):
async with sem:
print("Acquired")
await asyncio.sleep(1)
print("Released")
async def main():
tasks = [
asyncio.create_task(
my_coroutine(sem)
)
for _ in range(10)
]
await asyncio.gather(*tasks)
asyncio.run(main())
توی این کد وقتی اجرا میشه فقط سه تا task همزمان اجرا میشن و بقیه taskها صبر میکنن تا یکی از taskها تموم بشه و یه slot خالی بشه.
یعنی اول یک slot میگیره و وقتی کار تموم شد slot رو آزاد میکنه.
یه سینتکس دیگه هم داره که به صورت دستی میتونید انجام بدید، شبیه بقیه context manager ها ولی خب آدم سالم تا وقتی context manager هست دستی انجام نمیده 😄
await sem.acquire()
try:
# critical section
finally:
sem.release()
❤6
بنظرم خوبه که یک صحبتی راجب به CI, CD بکنیم
خیلی ها کلا سمت اینجور مسایل نمیرن چون با خودشون میگن ما برنامه نویسیم و اصلا نباید سمت چیزای DevOps بریم و اصلا وظیفه ما نیست
اما بنظرم داشتن دانش نسبی توی این زمینه خیلی میتونه مفید باشه به خصوص اگر پروژه شخصی دارید که روی سرور ران کردید یا کلا پروژه هاتون رو خودتون دیپلوی میکنید.
حالا اصلا CI و CD چی هستن؟
عبارت CI مخفف Continuous Integration هست. به زبان ساده یعنی هر بار که کد جدیدی به پروژه اضافه میشه، یک سری چک به صورت خودکار انجام بشه تا مطمئن بشیم چیزی خراب نشده. مثلاً به محض اینکه روی GitHub پوش میکنید، تستها اجرا بشن، لینتر اجرا بشه، بررسی امنیتی انجام بشه و اگر مشکلی وجود داشت همون لحظه مشخص بشه. اینطوری به جای اینکه دو هفته بعد وسط Production بفهمید یک نفر یه چیزی رو خراب کرده، همون موقع متوجه میشید و فیکسش میکنید.
و CD هم مخفف Continuous Delivery یا Continuous Deployment هست که مرحله بعد از CI محسوب میشه. یعنی وقتی تمام چکها با موفقیت انجام شدن، پروژه به صورت خودکار روی سرور دیپلوی بشه. مثلاً شما کد رو روی شاخه main پوش میکنید، GitHub Actions تستها رو اجرا میکنه و اگر همه چیز سبز بود خودش به سرور وصل میشه، نسخه جدید رو دریافت میکنه و سرویس رو آپدیت میکنه. نتیجه اینه که دیگه لازم نیست هر بار SSH بزنید، git pull بگیرید و سرویسها رو دستی ریستارت کنید و کل فرآیند انتشار نرمافزار reliable، سریعتر و کمخطاتر میشه.
اما یک نکته مهم وجود داره. خیلیها فکر میکنن CI/CD یعنی GitHub Actions یا GitLab CI یا Jenkins. در صورتی که اینها فقط ابزار هستن. CI/CD در اصل یک فرآیند و طرز فکره. هدفش اینه که هر تغییری که وارد پروژه میشه به صورت استاندارد بررسی بشه و انتشار نسخههای جدید reliable تر باشه.
یکی از اشتباهات رایج هم اینه که بعضیها فکر میکنن برای داشتن CI باید برن سراغ Kubernetes، AWS، Terraform و کلی ابزار عجیب و غریب. در حالی که برای اکثر پروژههای شخصی و حتی خیلی از پروژههای واقعی، اولین CI میتونه فقط همین باشه:
موضوع مهم بعدی تست هست. واقعیت اینه که CI بدون تست ارزش خیلی زیادی نداره. اگر Pipeline شما فقط اجرا بشه و هیچ تستی وجود نداشته باشه، عملاً فقط دارید یک سری دستورات رو اتوماتیک اجرا میکنید. که دقیقا همون کار رو با pre commit هم میتونید انجام بدید.
تمام هدف CICD اینه که اعتماد شما به کدتون بیشتر بشه و با خیال راحت تری بتونید نسخه های جدید پروژه رو دیپلوی کنید.
معمولاً داخل Pipelineهای حرفهای ممکنه کارهای زیر انجام بشه:
✅ Linting
✅ Formatting Check
✅ Unit Tests
✅ Integration Tests
✅ Type Checking
✅ Security Scanning
✅ Migration Validation
✅ Docker Build
✅ Deploy
✅ Health Check
✅ Notification
در نهایت به نظرم یک Backend Developer لازم نیست DevOps Engineer باشه، اما باید حداقل درکی از فرآیند Build، Test، Deploy و نگهداری سرویس داشته باشه. توانایی طراحی یک Pipeline ساده، اجرای تستها و دیپلوی خودکار پروژه، بیشتر از اینکه یک مهارت DevOps باشه، بخشی از مهارتهای پایهای یک Software Engineer محسوب میشه.
خیلی ها کلا سمت اینجور مسایل نمیرن چون با خودشون میگن ما برنامه نویسیم و اصلا نباید سمت چیزای DevOps بریم و اصلا وظیفه ما نیست
اما بنظرم داشتن دانش نسبی توی این زمینه خیلی میتونه مفید باشه به خصوص اگر پروژه شخصی دارید که روی سرور ران کردید یا کلا پروژه هاتون رو خودتون دیپلوی میکنید.
حالا اصلا CI و CD چی هستن؟
عبارت CI مخفف Continuous Integration هست. به زبان ساده یعنی هر بار که کد جدیدی به پروژه اضافه میشه، یک سری چک به صورت خودکار انجام بشه تا مطمئن بشیم چیزی خراب نشده. مثلاً به محض اینکه روی GitHub پوش میکنید، تستها اجرا بشن، لینتر اجرا بشه، بررسی امنیتی انجام بشه و اگر مشکلی وجود داشت همون لحظه مشخص بشه. اینطوری به جای اینکه دو هفته بعد وسط Production بفهمید یک نفر یه چیزی رو خراب کرده، همون موقع متوجه میشید و فیکسش میکنید.
و CD هم مخفف Continuous Delivery یا Continuous Deployment هست که مرحله بعد از CI محسوب میشه. یعنی وقتی تمام چکها با موفقیت انجام شدن، پروژه به صورت خودکار روی سرور دیپلوی بشه. مثلاً شما کد رو روی شاخه main پوش میکنید، GitHub Actions تستها رو اجرا میکنه و اگر همه چیز سبز بود خودش به سرور وصل میشه، نسخه جدید رو دریافت میکنه و سرویس رو آپدیت میکنه. نتیجه اینه که دیگه لازم نیست هر بار SSH بزنید، git pull بگیرید و سرویسها رو دستی ریستارت کنید و کل فرآیند انتشار نرمافزار reliable، سریعتر و کمخطاتر میشه.
اما یک نکته مهم وجود داره. خیلیها فکر میکنن CI/CD یعنی GitHub Actions یا GitLab CI یا Jenkins. در صورتی که اینها فقط ابزار هستن. CI/CD در اصل یک فرآیند و طرز فکره. هدفش اینه که هر تغییری که وارد پروژه میشه به صورت استاندارد بررسی بشه و انتشار نسخههای جدید reliable تر باشه.
یکی از اشتباهات رایج هم اینه که بعضیها فکر میکنن برای داشتن CI باید برن سراغ Kubernetes، AWS، Terraform و کلی ابزار عجیب و غریب. در حالی که برای اکثر پروژههای شخصی و حتی خیلی از پروژههای واقعی، اولین CI میتونه فقط همین باشه:
git push
↓
ruff
↓
pytest
↓
notification
موضوع مهم بعدی تست هست. واقعیت اینه که CI بدون تست ارزش خیلی زیادی نداره. اگر Pipeline شما فقط اجرا بشه و هیچ تستی وجود نداشته باشه، عملاً فقط دارید یک سری دستورات رو اتوماتیک اجرا میکنید. که دقیقا همون کار رو با pre commit هم میتونید انجام بدید.
تمام هدف CICD اینه که اعتماد شما به کدتون بیشتر بشه و با خیال راحت تری بتونید نسخه های جدید پروژه رو دیپلوی کنید.
معمولاً داخل Pipelineهای حرفهای ممکنه کارهای زیر انجام بشه:
✅ Linting
✅ Formatting Check
✅ Unit Tests
✅ Integration Tests
✅ Type Checking
✅ Security Scanning
✅ Migration Validation
✅ Docker Build
✅ Deploy
✅ Health Check
✅ Notification
در نهایت به نظرم یک Backend Developer لازم نیست DevOps Engineer باشه، اما باید حداقل درکی از فرآیند Build، Test، Deploy و نگهداری سرویس داشته باشه. توانایی طراحی یک Pipeline ساده، اجرای تستها و دیپلوی خودکار پروژه، بیشتر از اینکه یک مهارت DevOps باشه، بخشی از مهارتهای پایهای یک Software Engineer محسوب میشه.
❤6👍1