دانش آموخته مهندسی کامپیوتر دانشگاه شریف و مدیر فنی شرکت مشاوران نرم افزاری اعوان. علاقه مند به معماری راه حلهای نرم افزاری
کمال Event Sourcing با اکتور مدل
بسم الله الرحمن الرحیم
ده سال پیش حاج مهران حیدرزاده گزارشی از معماری بستر مالی LMAX به قلم برادر مارتین فاولر را به گروه ایمیلی شرکت اعوان ارسال کردند. با خواندن گزارش پرام ریخت. ببخشید منظورم اینه که خیلی شگفت زده شدم. پردازش 6 میلیون سفارش در ثانیه آن هم فقط با یک thread. این گزارش به مفهوم Event Sourcing پرداخته بود.
Event Sourcing means that the current state of the Business Logic Processor is entirely derivable by processing the input events. As long as the input event stream is kept in a durable store (which is one of the jobs of the input disruptor) you can always recreate the current state of the business logic engine by replaying the events.
راز رکورد شکنی LMAX این بود که اولاً آخرین وضعیت داده را به طور کامل در حافظه نگهداری میکرد. بنابراین دیگر نیازی به IO و مدیریت تراکنش برای خواندن و نوشتن داده نداشت. ثانیاً پردازش همه رویدادها را در یک thread انجام میداد. بنابراین دیگر نیازی به lock کردن thread ها جهت اجتناب از مشکلات همروندی نبود. فرض کنید یک سامانه بانکی موجودی حساب همه مشتریان را در حافظه موقت نگه دارد و هر برداشت یا واریزی را بلافاصله در حافظه موقت اعمال نماید.
بسیار سریع و عالی. فقط یک مشکل کوچک! اگر سیستم خاموش شد، داده ها از حافظه موقت پاک می شوند. برای حل این مشکل lmax رویدادهای ورودی به سامانه را در یک پایگاه داده فقط افزودنی (append-only) و غیر قابل تغییر(immutable) ثبت می کرد. مثل اینکه در سیستم بانکی همه دستورات برداشت و واریز را در یک فایل لاگ کنیم. اگر سیستم پایین و بالا شد، از ابتدای فایل لاگ، دوباره رویدادهای ورودی را اجرا (replay) میکنیم تا در حافظه موقت به وضعیت درست برسیم. خوب به سلامتی این مشکل حل شد.
فقط یک مشکل دیگر! با گذشت زمان تعداد رویدادها زیاد میشود. در نتیجه پایین و بالا شدن سیستم و بازسازی آخرین وضعیت در حافظه موقت، ساعتها یا روزها طول خواهد کشید. فرض کنید در مثال بانک قرار باشد، برای کشف موجودی هر حساب، همه برداشتها و واریزهای طول تاریخش را مجددا اجرا کنیم. برادرانمان در LMAX برای حل این مشکل هر از چندی از حافظه موقت Snapshot تهیه و در یک پایگاه داده مناسب ذخیره سازی می نمایند. در اینصورت بعد از پایین و بالا شدن سیستم، ابتدا آخرین Snapshot در حافظه موقت بارگزاری شده و سپس رویدادهای بعد از آن Snapshot اعمال میشوند.
بسیار خوب. به نظر میرسد همه چیز مرتب است. ولی همیشه می شود اوضاع را بهتر کرد.
- اگر میتوانستیم در عین حالی که از درگیر شدن با مشکلات همروندی اجتناب میکنیم، محدود به یک thread نباشیم. بلکه از تمام ظرفیت پردازنده یا حتی چند سرور موازی استفاده نماییم، بهتر بود.
- همچنین بهتر است محدود به حافظه یک سرور نباشیم. مثلاً بتوانیم موجودی برخی حسابهای بانکی را روی حافظه موقت سرور یک داشته باشیم و برخی سرور دو.
- برای مصرف کمتر حافظه بهتر بود به جای بارگزاری موجودی همه حسابهای بانکی، هر حسابی را که رویدادی روی آن حادث شد، مدتی وارد حافظه کرده و آخرین وضعیت او را بازسازی کنیم. سپس رویداد جدید را اعمال نموده و اگر مدتی رویدادی روی آن نیامد، آخرین snapshot آن رکورد را ذخیره و وی را از حافظه موقت خارج کنیم.
بیایید مثال سیستم بانکی را با کمک «اکتور مدل» کمی بهتر کنیم. اگر با اکتور مدل آشنا نیستید، پیشنهاد میکنم این مقاله را در مورد ضرورت آشنایی با «اکتور مدل» ملاحظه بفرمایید.
- به ازای هر حساب بانکی یک اکتور در نظر میگیریم. اکتور حساب، موجودی خود را در فیلدی در حافظه اش نگه میدارد.
- رویدادهای برداشت و واریز هر حساب را در mailbox اکتور وی قرار میدهیم تا یکی یکی اعمال شوند. چون میدانیم هیچگاه همزمان دو thread سراغ یک اکتور نمی آیند، مشکل همروندی و تداخل thread نداریم. ولی در کل محدود به یک thread نشدیم. چرا که دو اکتور مختلف می توانند همزمان با کمک دو thread به پیش بروند. بنابراین راحت می توانیم فیلد موجودی را کم و زیاد کنیم.
- اکتورها می توانند روی نودهای مختلف کلاستر توزیع شوند. بنابراین محدود به حافظه یک ماشین نیستیم.
- هر اکتور به سبک Event Sourcing رویدادهای وارده را در یک پایگاه داده لاگ میکند. هر از چندی هم از آخرین وضعیت خود اسنپ شات گرفته و در جای مناسبی ذخیره می کند.
- هر اکتوری که رویدادی به وی گزارش شود، به طور خودکار زنده شده و ابتدا وضعیت موجودی حساب را از آخرین Snapshot بازخوانی میکند. سپس رویدادهای موجود در mailbox خود را اعمال می نماید. اکتوری که مدتی رویدادی نداشت را میکشیم. به این ترتیب برای حسابهای راکد حافظه ای اشغال نمی شود.
خوشبختانه akka به عنوان یک بستر «اکتور مدل»، ساز و کار مناسبی برای پشتیبانی از Event Sourcing یا به اصطلاح قدیمی ترش Akka Persistance دارد.
اگر به مشارکت در یک پروژه چالشی مبتنی بر akka و اکتورمدل علاقه مند هستید، اینجا منتظر شما هستیم.
مطلبی دیگر از این انتشارات
روال تصمیمگیری در معماری نرمافزار
مطلبی دیگر از این انتشارات
کار عمیق با ذهن حواسجمع
مطلبی دیگر از این انتشارات
قالب پیشنهادنامه پروژههای نرمافزاری