مدیریت خطا همیشه یکی از تکراری و پربحث ترین موضوعات در گولنگ بوده و طبق این نظرسنجی توی سال 2019 مدیریت خطا جزو بزرگترین چالش برنامه نویسان گو هست.
توی این مقاله ما میخوایم راجب حالت های مختلف بهبود مدیریت خطا داخل کانکارنسی یا همون همروندی صحبت کنیم و از پکیج هایی که توسعه داده شده برای این موضوع استفاده خواهیم کرد.
توی این حالت ما یک گوروتین داریم که ممکنه چندبار خطا داخلش اتفاق بیفته, مثلا توی کد زیر ما نیاز داریم خطا هایی که اتفاق میفته رو لاگ بگیریم.
این کد یه متن CSV میخونه و چک میکنه, اگر متن CSV معتبر نباشه , یک خطا اتفاق میفته و این خطا رو لاگ میگیره, ولی ساختاری که نمایش میده این خطاهارو شلخته اس و مناسب نیست.
برای حل این موضوع دو تا پکیج خوب توسعه داده شده که میتونیم ازشون استفاده کنیم.
این پکیج خطا ها رو میگیره, گروه بندی میکنه و با یک فرمت استاندارد نمایش میده,اگر بخوایم کد قبلی رو مجدد با همین پکیج پیاده سازی کنیم به این شکل میشه:
خروجی کد بالا به این شکل میشه:
پیاده سازی این پکیج مشابه پکیج قبلی هست من فقط خروجی کد بالا رو با این پکیج این زیر میزارم:
من یک بنچ مارک هم آماده کردم با همون کد بالا که بتونیم تفاوت عملکرد این دو پکیج رو ببینیم:
name time/op alloc/op allocs/op HashiCorpMultiErrors-4 6.01µs ± 1% 6.78kB ± 0% 77.0 ± 0% UberMultiErrors-4 9.26µs ± 1% 10.3kB ± 0% 126 ± 0%
همانطور که میبینیم پکیجی که Uber توسعه داده کمی کند تر هست و حافظه بیشتری هم مصرف میکنه
اما طبق مستندات رسمی خود پکیج, این پکیج به گونه ای طراحی شده که خطا ها رو یکجا بگیره و گروه بندی کنه نه تیکه تیکه بگیره, من یک تغیر کوچیک روی کد بالا دادم که خطا ها رو جمع کنه یک جا بده به پکیج.
حالا مجدد تست میکنیم:
name time/op alloc/op allocs/op HashiCorpMultiErrors-4 6.01µs ± 1% 6.78kB ± 0% 77.0 ± 0% UberMultiErrors-4 6.02µs ± 1% 7.06kB ± 0% 77.0 ± 0%
بله دیدیم که عملکردش بهبود پیدا کرده, به هر حال زیاد فرقی بین این دو پکیج نیست و هردو دارند اینترفیسی از پکیج error خود گولنگ پیاده سازی میکنند.
وقتی که برای انجام یک کاری باید چندین گوروتین داشته باشیم و احتمال وقوع خطا داخل این گوروتین ها وجود داشته باشه, لازمه که این خطا هارو به صورت صحیح مدیریت کنیم تا برنامه مون عملکرد بهتری داشته باشه.
بیاین با هم یک برنامه ای بنویسیم که چندین گوروتین داشته باشه و پردازش هر گوروتین سه ثانیه طول بکشه که احتمال خطا داخل پردازش هم وجود داشته باشه:
برای اینکه نشون بدیم خطا چطور اتفاق میفته توی گوروتین سوم پردازش اول خطا برمیگردونیم که به این شکل میشه:
خب برنامه اجرا میکنیم و همانطور که انتظار داشتیم سه تا گوروتین هر سه مرحله طی کردند و برنامه تقریبا سه ثانیه طول کشید:
go run . 0.30s user 0.19s system 14% cpu 3.274 total
خب تا اینجا همه چیز اکی بود , مشکل جایی بوجود میاد که ما بخوایم هر مرحله پردازش این گوروتین ها به هم وابسته باشن و در صورت موفق نبودن یکی از پردازش ها باقی پردازش ها در گوروتین های دیگه هم لغو بشن.
برای حل این مشکل ما میتونیم از context ها در گولنگ استفاده کنیم که اگر یک گوروتین متوقف شد باقی گوروتین ها هم متوقف بشن.
پکیج errgroup که به خوبی این موضوع پیاده سازی کرده و ما میتونیم ازش استفاده کنیم, خب کد بالا مجدد با این پکیج بازنویسی میکنیم:
حالا برنامه اجرا میکنیم و میبینیم که با متوقف شدن یک گوروتین باقی گوروتین ها هم متوقف میشن که این باعث میشه برنامه مون سریع تر اجرا بشه:
go run . 0.30s user 0.19s system 38% cpu 1.269 total
امیدوارم توضیحاتم مفید بوده باشه. اگه خوشتون اومد، لایک یادتون نره! انتقادات و پیشنهادات هم میتونید توی نظرات بهم بگین.