Flask, SQLAlchemy, Concurrency مشکلتان همین هست؟

در نوشته قبلی درباره SQLAlchemy نوشتم و همچنین درباره scoped_session که مشکل برنامه های مولتی ترد رو حل میکرد ولی چحوری باید این دو تا رو با Flask جور کنیم.

خود Flask به صورت پیش فرض قابلیت ارتباط با SQLAlchemy رو داره ولی به نظرم و اونجوری که کار کردم یه مقدار دست و پای آدم رو میبنده به همین خاطر خودم ترجیح میدم تو پروژه ها اونا رو از هم جدا کنم.

این کد رو ببینید

هر زمان که یک Session ایجاد کنید یک اتصال جداگانه به سرور دیتابیس ایجاد و به اون Session اختصاص داده میشه

حالا مشکلش چیه؟

فرض کنید دو درخواست به دو آدرس test1 و test2 در یک لحظه مشخص به صورت همزمان ارسال بشه در این صورت تابع db_session_handler که global هست باید یک session ایجاد کنه اما همونطور که اینجا اشاره شده تابع sessionmaker به صورت thread-safe نیست و در واقع non-current هست پس در اینجا به هر دو درخواست یک session میده حالا test1 و test2 دارن رو دیتابیس عملیات انجام میدن اما کار teest2 زودتر تموم میشه بعد Session بسته میشه اما کار test1 تموم نشده و چند لحظه بعد که تموم میشه و میخواد commit کنه خطا میده که session بسته شده

راه حلش چیه؟

SQLAlchemy تکنیکی به نام Scoped_Session و معرفی کرده که مخصوص برنامه های مولتی ترد و یا برنامه هایی که تعداد درخواست های زیادی در یک لحظه بهشون ارسال میشه هست (مثل یک سایت)

کد زیر رو ببینید

به این صورت که شما هر بار که تابع DB_Session رو صدا بزنید یک Session به شما میده و SQLAlchemy در سیستم خودش این رو مدیریت میکنه که یک Session جدید بده یا اینکه Session فعال رو بده یعنی متوجه میشه که درخواست جدید هست یا همون قدیمیه.

به یکی از دو صورت زیر استفاده میشه که به نظر من اولیه خوانا تر هست و خودم از همین استفاده میکنم

برای اینکه Scoped Session  رو ببندید  کافیه تابع DB_Session.remove رو اجرا کنید با اینکار  متغیر فعال Session حذف میشه و بعد تابع close اون session اجرا میشه یعنی  اگه Connection Poolling فعال باشه این اتصال به Pool برمیگرده در غیر این صورت اتصال بسته میشه و منابع سخت افزاری آزاد میشه

کد اول برنامه به این صورت در میاد

حالا کافیه تو کدتون DB_SESSION رو صدا بزنید تا یک Session جدید بگیرید مثل

خوب حالا جمع بندی میکنم به همراه بیان نکات مهم

  • در اینجا Connection Pooling رو فعال کردم چون برای برنامه های مولتی ترد و یا در کل برنامه هایی که در هر لحظه چند درخواست همزمان بهشون ارسال میشه برای اینکه کارایی برنامه بیشتر بشه و منابع سیستم هر بار برای ایجاد و بستن اتصال بیهوده مصرف نشه (تو لینکی که ابتدای پست اشاره کردم این مورد رو کامل توضیح دادم ) و همچنین برای اینکه با خطای Too Many Connections مواجه نشید
  • از Scoped Session استفاده کردم که مخصوص برنامه های مولتی ترد و یا در کل برنامه هایی که در یک لحظه چند درخواست بهشون ارسال میشه.
  • از teardown فلسک استفاده کردم تا بعد از تموم شدن درخواست اتصال رو به Pool برگردونه.