Thundering Herd Problem یا مشکل گله ی رعدآسا

در علوم کامپیوتر مساله ای مطرح است به نام “گله ی رعد آسا” یا Thundering Herd Problem.

وقتی که تعداد زیادی process منتظر یک رخداد یا event هستند، ولی وقتی که این event رخ داد تنها یکی از این process ها باید انجام گیرد. در این حالت بعد از رخداد یک process انجام میگردد و باقی آنها معطل میشوند و یا مکررا دوباره تلاش میکنند و در نتیجه سیستم هنگ میکند. این در حالیست که تنها یکی از آنها باید اجرا میشدند و باقی باید کنسل میشدند.

در UNIX این مشکل به این شکل حل شده است: همه پاسخها در یک file descriptor نوشته می شوند بنابراین پس از پاسخ تنها یکی از process ها یا thread ها اجرا میشوند (بجای اینکه همگی با هم اجرا و سیستم هنگ کند)

مثال دیگر:

فرض کنید یک API داریم و 500 پروسه این API را صدا میزنند. ولی این سرویس تنها میتواند 2 تای آنها را پاسخ دهد. و 488 مورد دیگر Fail می شوند. ولی دوباره تلاش میکنند و این بار این سرویس با 996 درخواست روبروست و مجددا تنها میتواند 2 تای آنها را پاسخ دهد و این سیکل تا زمانی ادامه پیدا میکند که سرویس مختل شود. این هم مثال دیگری از گله ی رعد آساست.

برای حل این مشکل راه حل این است که از فراخوانی اگر Fail شد پس از 2 به توان N ثانیه دوباره تلاش کند. بنابراین دفعه دوم بعد از 4 واحد زمانی سپس 8 ، 16 و… واحد زمانی دوباره تلاش میکند این فاصله زمانی که تابع نمایی exponential هست موجب می شود که سرویس فرصت بازسازی خود را داشته باشد. در ادامه این راه حل میتوان یک عدد تصادفی به عنوان jitter به این بازه زمانی اضافه کرد تا همه فراخوانی ها دوباره همزمان نشوند. یعنی فرمول فراخوانی مجدد میشود (T + 2**N + JITTER)

بهترین روش برای حل مشکل Race condition

تمامی مشکلات مربوط به race condition و همزمانی تغییر یک متغیر توسط thread ها و پروسه های موازی و شرایط deadlock از این واقعیت سرچشمه میگیره که چند فرایند به اشتباه یک متغیر رو با هم تغییر بدن. و اگر یک متغیر داشته باشیم که دیگه تغییر نکنه تمامی این مشکلات از ریشه حل میشه. یعنی اگر متغیرهای ما همگی immutable یا غیر قابل تغییر باشن دیگه این مشکلات رو نمیبینیم.

تنها در زبانهای functional مثل clojure این امکان وجود داره که تمامی متغیرها immutable باشن.

در زبانهای دیگه که functional نیستن باید این مشکل رو جوری دیگه حل کرد. یکی از روش هایی که این مشکل رو حل میکنن اینه که یک بخشی از برنامه که قراره همزمانی توش داشته باشیم رو با متغیرهای immutable مینویسیم و بقیه بخش های برنامه رو mutable.

و از بخش mutable میتونیم از transactional memory استفاده کنیم که از این متغیرها نگهداری بشه.

پس بهترین معماری اینه که برنامه به دو بخش تقسیم بشه. یک بخش که متغیرها رو توشون تغییر میدیم و یک بخش که متغیرها تغییر نمیکنن و immutable هستن. حالا از سیاست هایی از اون بخشی که متغیرها رو عوض میکنه محافظت میکنیم.

پیاده سازی کوتاه و مختصرRabbitMQ و اجرای Celery

Search: Rabbitmq docker in google

Starting RabbitMQ


Starting Celery

Note: If you are using windows save yourself using gevent execution pool:

(https://stackoverflow.com/questions/62524908/task-receive-but-doesnt-excute)

and for Django:

Running Celery worker and Beat


Django Celery Beat extention


Flow monitoring

Flow monitoring

ساختار ارثبری در جنگو

تصویر زیر نحوه ارثبری از unittest پایتون توسط جنگو را نشان می دهد، در واقع از تمام این ماژول ها و کلاس ها می توان استفاده کرد.

باید در نظر داشت که ترتیب اجرای آنها در جنگو به صورت زیر است

اول تمامی کلاسهایی که از django.test.TestCase ارث برده اند اجرا میشوند

سپس تمامی کلاس هایی که از SimpleTestCase و TransactionTestCase و LiveTestCase به ارث برده اند اجرا می شوند.

در نهایت تمامی تستهایی که از unittest.TestCase استفاده کرده اند.

Fixtures

می توان با استفاده از django dumpdata از جداول مدلها دیتای سریالایز شده تولید کرد و توی فایلهای مختلف ذخیره کرد به این فایلها میگن fixture که توی تست نرم افزار هم قابلیت داره. مثلا میخایم قبل از شروع یک TestCase فایلها در fixture کلاس testCase در دیتابیس ذخیره بشه بعد هم پاک شه. که برای شروع هر تست یک دیتابیس جدید setup بشه.

سپس

جنگو توی فولدر fixtures یا مسیری که توی settings گفتیم FIXTURE_DIR.

unittest و اصول اولیه

در Unit Test مفاهیم زیر اهمیت دارند:

Fixture: قبل از اجرای هر تست یک محیط باید فراهم شه که به این محیط fixture میگن. مثلا قبلا اجرای متدهای unittest شاید لازم باشه یک instance از کلاس مربوطه ساخته بشه و بعد از انجام شدن متد تست اون instance از بین بره. در ضمن ممکنه که قبل از از متد تست این instance نیاز به ساخته شدن داشته باشه. بنابراین این اینستنس رو توی یک تابع به اسم unittest.TestCase.setUp ساخته میشه. این متد قبل از هر متد تست اجرا میشه. به این محیط ایجاد شده قبل از اجرای هر تست Fixture میگن. بعد از اجرای هر تست هم unittest.TestCase.tearDown اجرا میشه که توی اون هم میشه اون اسنتنس رو پاک کرد. مثال دیگه میتونه این باشه که کانکشن به دیتابیس ساخته بشه که توی اون متدهای تست استفاده بشه. در واقع از یه بخشی از کد که توی همه متدها اجرا میشه فاکتور میگیریم.

Test Case: تست کیس اصل کار تست هستش. یک کلاس هست که توی اون متدهایی تعریف میشه که اون متدها هر کدوم یه قابلیت رو تست میکنه. مثلا

اگر یک TestCase.setUp به ارور بخوره هیچ تستی انجام نمیشه. اگر تمامی تست ها انجام بشه بعدش TestCase.tearDown اجرا میشه.

قبل از اجرای هر متد تست setUp و tearDown و __init__ اجرا میشه.

Test Suit: بهتره تست های بهم مرتبط رو با هم دسته بندی کنیم و باهم اجرا کنیم. این کارو با Test Suit انجام میدیم.

ترتیب اجرا fixture ها به صورت زیره: