اشتباه میلیونی

سوتی دادن در بحث IT و توسعه نرم افزار خیلی زیاد پیش می آید و دلیل آن هم این است که تاثیر تغییرات خیلی وسیع و سریع است. بیشتر برنامه نویس ها هم خاطراتی از این اشتباهات دارند، از پاک شدن اتفاقی فلان جدول دیتابیس عملیاتی مشتری تا مورد ارسال وسیع SMS از دل یک Application.
چند وقت پیش تصمیم گرفته شد که همه متودها و روتین های پروژه Async شود. یکی از موارد پایه ای که در لیست تغییرات قرار داشت مکانیزم Add/Update در یک Repository و مکانیزم SaveChanges در کلاس UnitOfWork بود. چون Entity Framework یک نسخه Async از SaveChanges اضافه کرده بود، بنا شد در پروژه هم از نسخه Async آن استفاده شود. حتی قرار شد پیاده سازی های داخلی غیر Async هم برداشته شوند تا کل پروژه ها خود به خود مجبور شوند Async شوند.
مشکل اصلی از آنجا شروع شد که یکی از پروژه ها به شکل غیر وب و به شکل به خصوصی بود، در نتیجه اصلا حالت غیر Async برای آن معنی نمی داد. مجبور شدیم در خیلی از بخش های پروژه از روش Task.Run و در ادامه آن از Result یا Wait() استفاده کنیم تا آن متودهای Async را بتوانیم از دل یک متود غیر Async اجرا کنیم.
در یکی از روتین ها که مربوط به ارسال SMS بود، جدولی از دیتابیس با یک فیلد Boolean چک می شد و وقتی که ارسال SMS چه موفق چه ناموفق به اتمام می رسید، فیلد مربوطه true می شد تا دفعه بعد توی لیست ارسال قرار نگیرد. بعد از Update رکورد مربوطه، متود SaveChangesAsync با کمک Task.Run فراخوانی می شد تا Transaction کامیت شده و تغییرات در دیتابیس ثبت شود. مشکل همینجا بود، در این فراخوانی باید Wait() پس از Task.Run قرار می گرفت تا متود را تا پایان عملیات SaveChanges منتظر نگه دارد. اما این قسمت آخر یعنی Wait() از قلم افتاده بود در نتیجه تغییر مورد نظر هرگز در دیتابیس انجام نمی نشست.
خلاصه این که روتین ارسال هر دو ثانیه جدول را چک می کرد، تعدادی SMS را انتخاب می کرد، آنها را ارسال می کرد، یک Commit ناموفق انجام می داد و دو ثانیه بعد هم چون Commit ناموفق بود دوباره همان SMS های قبلی را انتخاب می کرد و دوباره آنها را ارسال می کرد و همین طور الی آخر… به این ترتیب سیستم در طول کمتر از بیست و چهار ساعت تعداد 75 هزار SMS ارسال کرد که در ارزان ترین حالت یک میلیون تومان هزینه ایجاد کرده بود.
مورد می توانست از این حادتر هم باشد. قرار بود SMS های معمولی با سیستم ارزش افزوده جایگزین شود. به نحوی که هزینه ارسال SMS را گیرنده پرداخت کند، آن هم با هزینه چند برابر ارسال یک SMS معمولی. اگر این موضوع اتفاق افتاده بود، حجم آبرو ریزی خیلی بیشتر می شد و آن یک میلیون تومان هم به چهار پنج میلیون تومان افزایش پیدا می کرد.
هر چند که در این ماجرا، برنامه نویس بخش مذکور داوطلبانه مسئولیت اشتباه را بر عهده گرفت اما جا داشت که بعد از این پیشامد، مکانیزم کنترلی در نظر گرفته شود تا فعالیت های غیر عادی سیستم و استفاده های نامعمول از منابع را گزارش کرده و در صورت لزوم سیستم را موقتا از کار بیندازد. اگر در این مورد خاص، مکانیزمی برای کنترل حجم SMS های ارسالی وجود می داشت، ارسال 75 هزار SMS در 24 ساعت تشخیص داده شده و می شد که سیستم متوقف شود.
علاوه بر این، تست و کنترل کیفیت نباید دست کم گرفته شود. نداشتن تست و کنترل کیفیت باعث پایین آمدن کیفیت کار شده و بروز چنین دردسرهایی را تسهیل می کند. موضوعی که متاسفانه در ایران خیلی کم اهمیت است.

پاسخی بگذارید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *