توضیحات Sean Yeoh درباره نحوه کار AssetNote در پادکست باگ بانتی

توضیحات Sean Yeoh درباره نحوه کار AssetNote در پادکست باگ بانتی

راهنمای نشانگر افراد شرکت کننده در مصاحبه: 

   *: Sean (مهمان برنامه)   

   +: مجری 1     

– مجری 2

+ خب بیاید از اول شروع کنیم. ما مقدمۀ این گفتگو رو ضبط کردیم و بعد متوجه شدیم که همۀ این مدت  شلوار Sean مشکل داشت. پس اجازه بدید دوباره سناریو رو براتون معرفی کنم.

– خب ایشون Sean Yeoh هستند. Sean در مجموعۀ AssetNote مشغول به کاره. AssetNote یک ابزار مدیریت سطح حمله (ASM) است. Sean لطفا یک بک گراند در مورد این که چطور این ابزار شکل گرفت و به اینجا رسید بهمون بگو.

* ASM یا مدیریت سطح حمله از دو قسمت تشکیل شده که یک قسمت مربوط به ریکان و درک این مسئله هست که اصلاً خود سرویس ارائه شده چیه. از دید ما این مورد یعنی سرویس موردنظر به صورت عمومی به چی نگاشت میشه، از اینترنت ما می تونیم چه چیزهایی ازش ببینیم، چه دیتای DNSای یا چه محدودۀ IP ازش داریم و… به طور کلی هر چیزی که از اینترنت می تونیم در مورد سرویس به دست بیاریم که این می تونه شامل آدرس IPهای تصادفیِ حاوی مجموعه های TLSی باشه یا شامل اون دسته از دارایی های ابری باشه که معمولاً شما باهاش مواجه نمیشید ولی ما هیوریستیک هایی داریم که می تونیم این ها رو شناسایی کنیم. در کل این قسمت به شناسایی و درک چیزهای موجود مربوطه.

و قسمت دوم که به نظرم ما درش عملکرد خیلی خوبی داشتیم، قسمت امنیت قضیه هست. این بخش به فراتر از سطح حملۀ شما ارزش میده؛ یعنی آسیب پذیری هایی که دارید رو پیدا می کنه، بررسی می کنه که آیا هنوز این آسیب پذیری ها وجود دارند یا خیر و این کار رو به طور پیوسته ادامه میده. ما برای تمام آسیب پذیری ها به یک میزان بررسی انجام نمیدیم و ترافیک صرف نمی کنیم، ولی برای آسیب پذیری های با ارزش و مهم، ارزیابی های زیادی انجام میدیم. به نظرم ارزشمندترین موردی که افراد در مورد پلتفرم ما می فهمند این هست که وقتی ما به شما ایمیلی راجع به کشف یک آسیب پذیری جدید ارسال می کنیم، یعنی این که اولاً این آسیب پذیری قابل بهره برداری و اکسپلویت هست، ثانیاً این که شما باید هر چه سریع تر برطرفش کنید. بنابراین، ما چیزهای به دردنخور مثل یافته های TLS یا Cypher Suiteها رو نادیده می گیریم و به جاش بهتون خبر میدیم که مثلا یک آسیب پذیری SSRF در سطح حمله دارید که قابل اکسپلویت هست و باید برطرفش کنید، یا مثلاً در حال اجرای یک نسخۀ آسیب پذیر Tablet هستید که ما تونستیم ازش یه آسیب پذیری RCE بگیریم و پیلودی که بهش فرستادیم و پاسخی که ازش گرفتیم به این شکل بوده و… بخش اعظم بازخورد مثبتی که می گیریم به خاطر کشف همین آسیب پذیری های مهم و قابلیت اطمینان از میزان پوششی هست که انجام میدیم.

+ یه نکته ای هم که باید اینجا اضافه کنم اینه که ما گفتیم Sean برای AssetNote کار می کنه، ولی در حقیقت Sean شخصی هست که AssetNote رو ایجاد کرده. Sean و Shubs برنامه نویسان اولیۀ این ابزار بودند.

* آره، علت ساخت ابزار مذکور این بود که ما می خواستیم همۀ کارهای فوق العاده ای که Shubs از قبل داشت انجام می داد رو یک جا گردآوری کنیم. Shubs پایپلاینی برای خودش داشت که داخلش به طور پیوسته دارایی (asset) های جدید روی تارگت ها رو کشف می کرد، وقتی دارایی های جدید ایجاد میشدن، خبردار میشد (نوتیف دریافت می کرد) و در ادامه، مجموعه ابزار خودش رو به طور اتوماتیک روی این تارگت ها اجرا می کرد، ولی بعدش این پروسه بزرگ و بزرگ تر شد. در ادامه هر چقدر بیشتر  و بیشتر یاد می گیرید، متوجه میشید که تفاوت زیادی هست بین اون ابزاری که لازمه یک بار در روز، یک بار در هفته یا یک بار در کل عمر اجرا بشه و اون ابزاری که لازمه هر یک ساعت یک بار به همراه SLA اجرا بشه و مشتری با استفاده از اون قادر باشه هر زمانی که خواست و به هر میزان داده ای که خواست، کوئری های موردنظرش رو روی دیتاست هایی با بزرگی صدها گیگابایت اجرا کنه. و خب حرکت کردن از کدهای bash و اسکریپت های پایتون به سمت کد تولید قابل اعتماد کار آسونی نیست.

+ وقتی تصمیم به ایجاد AssetNote گرفتید، می دونستید دارید خودتون رو وارد همچین ماجرای پیچیده ای می کنید؟

* من با این دید وارد این ماجرا شدم که قراره برنامه های ساده و کوچک بنویسم. یادمه در یک جمعی بودم که یهو پیامی از Charles دریافت کردم که ازم در مورد برنامه نویسی یک چیزی کمک می خواست. بهش گفتم باشه حتماً! من برات یک اسکنر امضا می نویسم، فقط این اسکنر باید چه چیزهایی رو پشتیبانی کنه؟ بهم گفت فقط لازمه یه چند تا درخواست بفرسته که منم گفتم خب باشه، این که خیلی راحته، من برات اسکنر امضایی می نویسم که حدود 80000 درخواست در ثانیه بفرسته رفیق. ولی خب بعدش یه مشکلی پیش اومد، اونم این که چطور این اسکر رو اجرا کنیم؟ ما که نمی تونیم کاری کنیم این ابزار به طور پیوسته ملت رو ddos کنه. خب می دونید، ما لازم بود کلی یادگیری ساختاری انجام بدیم، یعنی مثلا از فاز «اجرای همه چیز روی یک واسط» به فاز «تقسیم کردن سرویس هایمان» رفتیم، یعنی همه چیز به شکل یکسانی مقیاس بندی نمیشدن.

+ اجازه بدید یه لحظه اینجا بحث رو نگه داریم. چون این بحث یه جورایی محور اصلی این اپیزود ما هست. با توجه به پنج سال گذشته و اتفاقاتی که زندگیشون کردید، مسیری که از باگ بانتی شروع کردید و به سمت نرم افزار تمام عیار سازمانی حرکت کردید، چه نوع تصمیمات ساختاریافته ای را به باگ بانتی هانترها پیشنهاد می کنید؟ از کجا شروع می کردید و چه طرحی رو برای تصمیم گیری در پیش می گرفتید؟ می دونم این یه سوال مبهمه! ولی می تونیم بحثمون رو از کارگذاران پیام (message Brokers) شروع کنیم، اون ها چه نقشی در این ساختار ایفا می کنند؟

* خب به نظرم سوال اول اینه که اصلاً message broker چیه و اصلاً مردم چرا ازش استفاده می کنند. ببینید در حالت کلی وقتی سعی دارید کارها رو اتومیت (خودکارسازی) کرده و یک سیستم ایجاد کنید، دو نوع روش وجود داره. اولین مورد اینه که می تونید کارها را به صورت همزمان انجام بدید. یعنی این که من از شما می خوام تا همۀ زیردامنه ها رو بهم بدید و اسکنشون کنید، در این حالت شما یک اسکریپتی اجرا می کنید که همۀ زیردامنه ها رو بگیره و بعدش یک اسکریپتی اجرا می کنید که همۀ این زیردامنه ها رو اسکن کنه، که خب این روش اوکیه و برای حالتی که شما مجموعه ای از کارها برای انجام دارید جواب میده.

ولی برخی اوقات شما قراره این کارها رو هر ساعت یک بار و روی صدها یا هزاران یا میلیون ها هاست انجام بدید، یه اسکریپت خیلی خیلی طولانی دارید که احتمالاً می دونید قراره روی صد یا هزار هاست اجرا بشه و جواب بده. اما وقتی وارد فازی میشیم که داخلش میلیون ها، صدها میلیون یا هزاران میلیون هاست وجود داره چی؟

+ این حالت واقعاً در باگ بانتی اتفاق می افته. در برخی برنامه های باگ بانتی، اسکریپت هایی بوده که من روی سه میلیون هاست اجراش کردم.

* احتمالاً شما در مورد چیزهایی مثل Axiom شنیدید که بهتون در موازی سازی این موارد کمک می کنند، اما در نهایت بازم شما چنین ابزارهایی رو اجرا می کنید، نتایج رو جمع آوری می کنید، بعد وارد حالتی میشید که دلتون میخواد پایپلاین داشته باشید. اینجاست که سراغ چیزهایی مثل RabbitMQ یا Amazon SQS یا سرویس های صف بندی مشابه میرید که این به نوعی تعریف message broker محسوب میشه. Message broker روشی هست که در اون، مجموعه ای از کارها از طریق چیزی که ما بهش تولیدکننده ها میگیم، پشت سر هم صف بندی میشن و در ادامه شما مجموعه ای از سرویس ها یا برنامه ها رو دارید که تک تک موارد داخل این صف رو برداشته و ازشون استفاده می کنند (مصرف کننده هستند). این کاری هست که message broker انجام میده.

+ خب پس تعریف message broker اینطور میشه که: شما بخش های مختلفی دارید که می خواید به صورت همزمان روشون کار بشه. مثلاً یه ابزار کشف زیردامنه دارید که هر بار یک زیردامنه پیدا می کنه، میاد این زیردامنه رو در قالب یک پیام به message broker میفرسته و در ادامه، اون پیام به یک میکروسرویس دیگه ارسال میشه که قراره کار اسکن رو انجام بده، به یک میکروسرویس دیگه میره که قراره TLS رو بیرون بکشه، به یک میکروسرویس دیگه فرستاده میشه که قراره HTML مربوطه رو بیرون بکشه و… پس ایدۀ اصلی همینه، درسته؟

* درسته، یه جورایی روشی برای پخش پیام ها محسوب میشه.

– یه جورایی شبیه اینه که شما یک نامه ای رو به پست چی میدید و پستچی اون رو به افراد مختلف تحویل میده.

+ پس ما این message broker ها رو داریم که پیام ها رو به بخش های مختلف ارسال می کنند. کمی قبل ما در مورد معماری رویدادمحور صحبت می کردیم که عموماً متفاوت از این ماجرا نیست، ولی حدس می زنم در سطح مفهومی کمی متفاوت تر باشه.

* بله، ایدۀ اصلی در معماری های رویدادمحور معمولاً دو حالت داره. دسته بندی اول این طوریه که شما میگید من مجموعه ای از کارها برای انجام دارم مثلاً میخوام TLS ها رو بیرون بکشم، اسکن پورت انجام بدم، عنوان های HTTP رو به دست بیارم و… پس شما ایدۀ پروسه ها یا برنامه هایی که می خواید اجرا کنید رو دارید، بعدش میاید بر اساس این کارها، به نحوی نشانه ها یا هر چیزی رو که می خواید message broker تقسیم بندیشون کنه ایجاد می کنید. در ادامه، میاید میگید یک صف برای TLS، یک صف برای پورت ها و یک صف برای عناوین خواهم داشت. در نتیجۀ این امر، در نهایت هر سرویسی که در نظر داشتید مجزا خواهد بود.

اما فکر می کنم روش بعدی، معماری های رویدادمحور هستند که در اون لازمه خیلی زیاد فکر کنید که مثلا از چه داکری استفاده کنید، چه زیردامنه ها و چه آی پی هایی خواهید داشت و… به همین ترتیب، شروع به شکل دهی مدل می کنید و به این فکر می کنید که وقتی یک آدرس IP پیدا کردم، باید شروع به اطلاع رسانی کنم، یعنی فرضاً در ادامه من میخوام اسکن TLS انجام بدم، میخوام عناوین رو به دست بیارم و… اینجاست که دیگه شما به این فکر نمی کنید که سعی دارم چه کاری انجام بدم، بلکه بیشتر روی این تمرکز می کنید که چه چیزهایی دارم، در حال حاضر چه چیزهایی داره اتفاق میفته و در ادامه به این فکر می کنید که من بر اساس این رویدادهایی که داره اتفاق می افته، چه کاری می خوام انجام بدم.

+ اجازه بدید یه مثال برای این حالت بزنم. فرض کنید می خوایم هر پنج دقیقه یک بار، تصویری به روز رسانی شده از DNS روی همۀ دارایی هایمان داشته باشیم. وقتی رویداد نشانگر پنج دقیقه یا حتی چهار دقیقه و 45 ثانیه فرا میرسه، می تونیم رویدادی رو فعال کنیم که نمایش فعلی DNS ما رو منقضی کنه و چندین فعالیت مختلف دیگه رو آغاز بکنه. درسته؟

* بله، یک روش اینه که بگی وقتی کارها منقضی میشن، اتفاقات رخ بدن. پس مثلاً شما میگید هر پنج دقیقه یک DNS resolution روی همۀ دارایی ها انجام بدیم، اینجا رویداد می تونه این باشه که مثلاً فلان زیردامنه رو پیدا کردیم که به فلان رکورد DNS نگاشت میشه که دیتای شما در این رویداد زیردامنه و آی پی مربوطه هست.

– شما چطور جلوی گلوگاه (bottleneck) شدن سرویس های آبونه شده رو می گیرید؟ چرا که شما ممکنه چندین تسک داشته باشید که یکیشون برای انجام شدن به کلی کار نیاز داشته باشه؛ یعنی مثلاً یک تسک دارید که وظیفه اش ریزالو DNS هست، یه تسک دیگه دارید که عناوین HTML رو بیرون می کشه و… حالا بین این تسک ها، ریزالو DNS معمولاً مدت زمان خیلی بیشتری طول می کشه. با توجه به مدت زمان بالای انجام این تسک ممکنه به گلوگاه تبدیل بشه. اینجور مواقع چه کاری انجام میدید؟

* روش های مختلفی برای حل این مشکل وجود داره. ما چندین سال و بهتره بگم مدت زمان زیادی از کارمون رو صرف تفکر عمیق برای بهینه سازی این موارد کردیم. شما می دونید که مدت زمان لازم برای شناسایی تکنولوژی های به کار رفته روی یک وب سایت حدود 30 ثانیه تا یک دقیقه طول می کشه، پس با خودتون فکر می کنید آیا می تونم این 60 ثانیه رو به 30، به 20، به 10 ثانیه کاهش بدم؟ درسته ممکنه شما یه جاهایی به خاطر محدودیت های فنی، درگیر گلوگاه بشید، ولی وقتی به اون نقطه میرسید با خودتون میگید خب من الان 6 برابر سریع تر از قبل دارم عمل می کنم که خب بهتر به نظر میرسه.

با این حال جنبۀ دیگه ای که باهاش مواجه میشید اینه که مقیاستون داره بزرگتر میشه. اینجاست که می تونید تسکتون رو به بخش های کوچک تری تقسیم کنید و به جای این که همۀ 300 میلیون رکورد DNS رو یک جا ریزالو کنید، چندین ورکر ایجاد می کنید که هر کدوم یک رویداد رو شروع کرده و بخشی از این رکوردها رو ریزالو می کنند و به طور موازی با هم کار می کنند. حالا شما 300 ورکر دارید که هر کدوم به طور همزمان یک میلیون رکورد رو ریزالو می کنند و اینطوری می تونید تا حدی بر مشکلات توان عملیاتی غلبه کنید.

از طرف دیگه شما می تونید تصمیمات ساختاریافته با ابزارها انجام بدید، پس به جای این که یک حجم بزرگی از ورودی ها به حجم بزرگی از داده ها منجر بشن، می تونید شروع به پخش (استریم کردن) خروجی و نتایج بکنید و یه جورایی انگار خود ابزار داره به صورت غیر همزمان کار می کنه. پس به این طریق شما جریان پیوسته ای از کارها رو دارید، و البته بسته به مقیاس کاریتون واقعاً نیازی نیست همۀ این بهینه سازی ها رو انجام بدید.

+ یک نکته ای من با توجه به صحبت های امروز با Sean و آموخته های قبلیم می خواستم اضافه کنم اینه که خیلی از ابزارهای لازم برای باگ بانتی توسط روش های خارق العاده ای مثل اسکریپت های Nuclei گردآوری شده اند و شما واقعاً مجبور نیستید خودتون چیزی بنویسید، مگر این که واقعاً به این کار علاقه داشته باشید یا احساس نیاز کنید. Sean حالت چهره ات طوری به نظر میرسه که انگار با این حرف من مخالفی، آره؟

* من مهندسی انجام میدم چون این شغل هر روز منه. این کار منه که بیام برای شرکت ابزار تولید کنم. اگه من در زمینۀ ریکان DNS از تو بهتر عمل می کنم، به این خاطره که شغلم این رو ایجاب می کنه. ولی فکر می کنم اگه تو تک نفری داری این کار رو انجام میدی، من تیمی از افراد رو دارم که ازم پشتیبانی می کنند. وقتی میگم من سرویسی ساختم که به طور غیرهمزمان در یک message broker کار می کنه، در واقع دارم میگم من تیمی دارم که از دو مهندس زیرساخت فوق العاده تشکیل شده که کار نگهداری کلاسترهای Kubernetes رو انجام میدن. من یک تیم 5 نفره از مهندسینی دارم که به من کمک می کنند نرم افزار و همچین چیزایی رو بسازم. با این حال وقتی دارم از مشکلاتی که داریم صحبت می کنم، این مشکلات خیلی شبیه به اون چیزی هست که در محیط باگ بانتی هم می تونید ببینید. به نظرم وقتی مردم میگن نمی تونی مقیاس رو از یه حدی بیشتر کنی یا اینکه این ابزار نمی تونه فلان کار رو انجام بده، در واقع مشکل از محدودیت زمان و تلاش هست، نه محدودیت خود ابزار. من فکر می کنم مشکلات فنی نظیر «نمیشه اسکن سریع انجام داد» یا «فلان داده رو نداریم» قابل حل شدن هست.

+ آره، فقط کافیه بتونیم زمان و انرژی بیشتری براش صرف کنیم. یکی از چیزهایی که من واقعاً دوست دارم در موردش باهات حرف بزنم اینه که بیشتر وقت ها سوالی که برام پیش میاد اینه که خب فلان ابزار به اندازه کافی سریع عمل نمی کنه، پس من باید ولش کنم. ولی بعدش یه دید جدید به ذهنت میاد که اگه اسکن انبوه رو طوری تنظیم کنم که شش یا هفت بار سریع تر انجام بشه چی؟ و اینجاست که به طور معجزه آسایی همۀ مشکلات حل میشن و متوجه میشی خیلی جذاب تره که بتونی در این مشکلات عمیق تر بشی، چون اگه بخوای ابزار رو تنظیم کنی، باید بتونی اون رو در سطح پایین تری هم درک کنی که این مسئله می تونه برای رشدت به عنوان یک محقق امنیتی، کمک زیادی بکنه.

– دیدگاه خیلی خوبیه. اگه یک سیستم واقعاً ناکارآمد داشته باشی، به جای این که بیای قوی و تکثیرش کنی تا کارآمدتر بشه، اول بیا بهینه اش کن، بعد ببین باز هم نیازی هست تکثیرش کنی یا نه. چون این طوری هم در هزینه و هم در مقیاس بندیت می تونی صرفه جویی کنی و این صرفه جویی قراره در خیلی حوزه های دیگه نجاتت بده. یعنی به جای این که یک سیستم ناکارآمد رو چندین مرتبه استفاده کنی تا فقدان بهینه سازی رو جبران کنی، می تونی یه کم زمان بگذاری و این مشکل رو با بهینه سازی حل کنی.

+ آره واقعا حس خوبی داره! می دونی، من یه بار این کار رو انجام دادم. من همۀ چیزهایی که داشتم رو با پایتون بازنویسی کردم. مسئله این بود که من ابزار Aquatone رو داشتم که از وب سایت ها عکس می گرفت و بعدش گواهینامۀ TLSش رو دانلود می کرد. از اونجایی که این کار رو با دو اتصال TCP انجام می داد، من یه جورایی داشتم عذاب می کشیدم و می خواستم کاری کنم که این کار فقط با یک اتصال TCP انجام بشه. من همۀ این مراحل رو با استفاده از پروتکل chrome devtools نوشتم؛ به طوری که گواهینامۀ TLS رو از پروتکل chrome devtools می گرفت و همزمان از وب سایت اسکرین شات هم می گرفت. خب راستش زیاد سریع تر نشد، من انتظار داشتم حداقل سرعتش دو برابر بشه، ولی حتی اینطور هم نشد. با این حال من احساس خیلی خوبی داشتم.

* اینجاست که آدم متوجه میشه هر چیزی از جمله کروم هم یک جعبه سیاه از زمان و تلاش هست. می دونی، برای تشخیص صحیح تکنولوژی به کار رفته، لازمه که جاوا اسکریپت رو ارزیابی کنی و باهاش ور بری. من این کار رو انجام میدم. ما ساعت های بی شماری رو صرف این کردیم که راه حل های موجود رو به حیطۀ تجاری سازمانی تبدیل کنیم، ولی شده به یه جایی برسیم که بگیم ولش کن، بیا همه چی رو از اول بنویسیم.

– شده بعضی اوقات به اون خط قرمزی برسی که با خودت بگی، خب این دیگه ارزش زمان و تلاش رو نداره؟ یا پاداش نهایی همیشه براتون ارزش کار سخت رو داشته؟

* ما همیشه با ساده ترین راه حل شروع می کنیم و تقریباً هر چیزی که تا حالا اجرایی کردیم با این سوال شروع شده: چیکار می تونیم بکنیم که باعث بشه 80 درصد مسیر از قبل وجود داشته باشه و ما فقط لازم باشه 20 درصد از تلاشمون رو براش صرف کنیم؟ AssetNote با استفاده از اسکریپت های پایتونی پیرامون ابزارهای موجود آغاز شد.

– آره، هر کسی که اتومیشن (خودکارسازی) انجام میده، این کار رو می کنه.

* من فکر می کنم اگه خودتون دارید این کار رو انجام میدید، صد در صد نباید از معماری رویدادمحور یا یک سیستم Kubernetes مقیاس خودکار شروع کنید؛ بلکه باید از اسکریپت های پایتون ساده ای استفاده کنید که عملیات موردنظر شما رو به شکلی اتومیت می کنند که سرعتش بیشتر بشه.

– آیا اون XKCD برای اتومیشن رو دیدی؟

* آره، خیلی ازش خوشم میاد.

– من عکسش رو اینجا میارم. همونطور که می بینید ما این گراف XKCD رو داریم که نشون میده مردم چه فکری در مورد کارهایی که اتومیشن می تونه انجام بده دارن، اون ها فکر می کنند قراره یه کم اتومیشن انجام بدی، بعد یه کم سرعتت کم بشه و در ادامه به صورت نمایی و تصاعدی از موانع عبور کنی؛ اما چیزی که در واقعیت اتفاق می افته اینه که درسته اتومیشن یه کم سرعت کارت رو بالا می بره ولی بعدش بهره وری تو رو از بین می بره، چرا که باید همۀ تلاشت رو صرف نگهداری و بهتر کردن کدت کنی، یعنی انگار تو به جای این که سعی کنی از مزایای اتومیشن استفاده کنی، داری بیشتر زمانت رو صرف رفع ایرادات و کار کردن روی ابزارت می کنی. و من فکر می کنم این نمونه ای عالی برای اون چیزی بود که تو گفتی؛ چرا که خیلی راحته تو تله ای بیفتی که در اون مدام با خودت بگی «من باید این کار رو بدون نقص انجام بدم»، «من باید بهترین و سریع ترین ابزار ممکن رو برای انجام یک تسک ساده بسازم»؛ در حالی که باید همیشه این مخفف رو به یاد داشته باشی: K-I-S-S (ساده نگهش دار احمق!). کدت رو ساده نگهدار. 80 درصد از راه حل های موجود استفاده کن و تنها 20 درصد روش تلاش اضافه بکن.

* به نظرم بهترین نتایجی که من از همۀ ابزارهای اتومیشن گرفتم در واقع مربوط به وقتی نبوده که کلی زمان صرف طراحی یک معماری کامل کردم، بلکه مربوط به وقتی بوده که من ابزار موجود رو هر هفته اجرا کردم و یک روز در هفته رو مقابلش نشستم و از خودم پرسیدم آیا این ابزار به اندازۀ کافی این هفته برام مفید عمل کرده؟ یادته یه دوره ای سر این که کدوممون تونسته takeover بیشتری کسب کنه با هم رقابت داشتیم؟ روندی که در اون دوره طی می کردم این بود که هر هفته از خودم می پرسیدم آیا بازده این هفته کافی بود یا من باید زمان بگذارم و بهترش کنم؟ و اون هفته هایی که من بیشترین پیشرفت رو در این نبرد داشتم، مربوط به زمانی بوده که کمترین بازده رو داشتم، چرا که هیچی تحریک آمیزتر از این نیست که هیچ کس دیگه ای به اندازۀ تو پول درنیاره.

– وقتی در سطح تجاری سازمانی در حال بهینه سازی هستی، چطوری می تونی تله های اپلیکیشنت رو تشخیص بدی؟ در واقع چطور تحلیلش می کنی؟

+ خب بذار بگم این اصلی ترین دلیلی هست که ما داریم اینجا با این دو نفر صحبت می کنیم. اون دسته از شماهایی که به کدنویسی و بهینه سازی علاقه دارید و می خواید از طریق سریع ترین و بهینه ترین بودن، بانتی های باگ بانتی رو از آن خودتون کنید، با ما همراه باشید.

* در یک سطح بالا، ما سرویس هامون رو بین صد ها رپلیکا، صدها واسط EC2 پخش می کنیم. نمایش و مشاهده کردن دیتا در اونجا خیلی متفاوت تر از حالت اجرا بر روی یک ماشین هست. در حالتی که من مثلاً ابزار خودم رو می نویسم، یک رابط EC2 دارم که این برنامه رو اجرا می کنه. در چنین حالتی همه چیز خیلی سر راست هست. من می تونم یک profiler نصب کنم، مثلاً از golang profiler درون برنامه ای استفاده کنم و نگاه کنم ببینم زمان داره کجا سپری میشه، چطور دارم ازش استفاده می کنم و… و به این طریق یک سری بهینه سازی های برنامه ای ساده انجام بدم.

+ لطفاً یک لحظه وایسا! من تا حالا همچین کاری نکرده ام. پس profiler یک چیزیه که بهت میگه چه عملیاتی در حین اجرای کد بیشترین زمان رو به خودشون اختصاص میدن؟

* آره، کاری که profiler انجام میده اینه که با سربار خیلی کمی، هر چیزی که در برنامه ات اتفاق می افته رو ضبط می کنه (فکر کنم مقدار این سربار در golang در بدترین حالت پنج تا ده درصده). پس کار اصلی که profiler انجام میده اینه که هر یک میلی ثانیه یا هر ثانیه، کلی snapshot می گیره و میپرسه در حال حاضر چه چیزی در حال اجراست. در ادامه، کاری که شما با این snapshotها انجام میدید اینه که همشون رو با هم تجمیع می کنید و اون بهتون میگه که در این نقطۀ زمانی، فلان تابع در حال اجرا بوده، هفت ثانیه طول کشیده تا این تابع اجرا بشه و… و به این ترتیب شما شروع به دیدن یک نقشۀ حرارتی میکنید که حاوی همۀ این ابزارها بوده و بهتون نشون میده که در یک دورۀ زمانی مشخص، دقیقاٌ چه مدت زمانی در هر تابع حقیقی سپری شده. ارزش این کار اینه که شما به وضوح می تونید ببینید که کجای برنامه تون بیشتر از همه زمانبر هست. مثلاً این که آیا برنامۀ من مدت زمان زیادی رو منتظر یک درخواست شبکه ای می مونه؟ شاید در همون زمان انتظار، من تعداد زیادی درخواست شبکه ای دیگه رو از دست بدم. شاید به یک گلوگاه در سه خط از برنامه تون برخوردید که بدون هیچ دلیلی روی زیردامنه ها، لوپ میزد. شاید از این طریق بتونیم کاری کنیم که این لوپ رو دور بزنیم یا ساختارش رو طوری تغییر بدیم که لازم نشه این همه لوپ داشته باشیم. به این ترتیب شما می تونید بفهمید که چرا برنامه تون داره کند عمل می کنه.

+ ایول، من عاشق این رویکرد رویدادمحور شدم. حالا اگه من بودم، کاری که ازم برمی اومد این بود که فقط به کد زل بزنم و بگم: خب، این کندی کجای برنامه رخ میده؟ یعنی فقط از طریق حدس زدن می تونستم بفهمم.

– خب فکر می کنم این همون دلیلی هست که خیلی از شرکت های مهندسی وجود دارند. این خیلی متداوله که شما سیستمی داشته باشی که به علت تعداد بالای درخواست های کاربر، نیاز به مقیاس بزرگتری داشته باشه. برای این که بتونی این سیستم رو به کار بندازی، باید جایی رو که کندی در اون اتفاق می افته رو پیدا و بهینه اش کنی. در عین حال، در سطوح بالاتر با خودت میگی این سیستم علاوه بر این که باید کار بکنه، باید بتونه به بهترین شکل ممکن هم کار کنه. در چنین حالتی چیکار می کنید تا سیستم به بهترین شکل ممکن کار کنه؟

* اگه بخوای گام بعدی رو برداری، در یک نقطه ای دیگه فقط برنامۀ صنعتی پروفایل کافی نیست. چون در یه نقطه ای شما مجبور میشی کلی کارهای شبکه ای انجام بدی. مثلاً از خودت می پرسی: «خب من چطور می تونم با شبکه تعامل داشته باشم». جواب واضح این سوال اینه که «می تونی شبکه رو سریع تر کنی»، اما این هم برمی گرده به همون سوال پایه ای که «من وقتی google.com رو تایپ می کنم، چه اتفاقی می افته؟»، یعنی میخوای تا چه لایه ای و چه سطحی عمیق بشی.

+ به نکتۀ خیلی خوبی اشاره کردی رفیق! درک عمیق این که هر چیزی چطور کار می کنه، قطعاً نکتۀ اصلی و محوری هر موضوعی هست. شما باید تمامی جزئیات مربوط به یک چیز رو درک بکنی.

* به نظرم هر شخصی باید خودش میزان و جهت بهینه سازی موردنظرش رو تشخیص بده. این موضوع خیلی بستگی به این داره که برای هر شخصی چه چیزی سرگرم کننده و جالبه. مثلاً بعضی وقت ها برای من بهینه سازی و یه همچین کارهایی جالب میشه، یک وقت هایی هم ترجیح میدم که به جای این کار، مشغول هانت کردن باشم.

– تصور من اینه که وقتی در مقیاس AssetNote صحبت می کنیم، حتی تغییرات خیلی خیلی حاشیه ای هم ارزش بسیار زیادی خواهد داشت، دلیلش هم به خاطر مقیاس بالایی هست که دارید روش کار می کنید.

* بله، درسته. یکی از قابلیت هایی که ما داریم، قابلیت ادغام با سرویس های ابری شما، ادغام با AWS یا ادغام با GCP هست. ما همۀ دیتای مرتبط با دارایی ها رو می گیریم، اون رو غنی می کنیم و اطلاعات اضافه بهتون میدیم و بعدش یه جورایی کل پلتفرم رو باهاش تغذیه می کنیم. یکی از مهندسین من در حال حاضر داره به شکل خیلی نزدیکی با یکی از مشتری ها کار می کنه، چرا که در ابتدای کار وقتی ما این ویژگی رو ایجاد کردیم، در حال بررسی مقیاسی بودیم که امکان هندلش رو داشته باشیم. یک سوالی برامون پیش اومده بود و یه جورایی شما هم بهش اشاره کردین: تصمیم گیری دیتامحور! یعنی درک کردن این مسئله که هنگام ایجاد کردن این ویژگی، دنبال چه خصوصیات عملکردی هستیم، انتظارات ما چیه و چه چیزی رو می تونیم هندل کنیم؟ ما گفتیم خب، ما یک عدد معقول بزرگ رو در نظر می گیریم، مثلا 10 برابر بیشتر از چیزی که موجوده و مشتریانمون در نظر دارن. اینجا بود که یک مشتری پیدا شد و یک مقدار 10 برابر بیشتر از عدد ما رو در نظر گرفت. گفتم شوخیت گرفته؟ همچین چیزی امکان نداره. من باهاشون صحبت کردم و اون ها در جواب بهم گفتن: «معلومه AWS محدودیت درونی داره و ما باید از AWS بخوایم که این محدودیتش رو برای مورد ما برداره!».

خب، اینجا ایراد کار از ما نبود! ما انصافاً خوب تخمین زده بودیم و تخمین زنیمون بر اساس حداکثر محدودیت عمومی AWS انجام شده بود. این مشتری بود که با مقیاس دیگه ای سنجشش رو انجام داده بود. به همین خاطره که مهندس ما چندین هفتۀ گذشته رو مشغول بهینه سازی سرویسمون بوده تا امکان همگام سازی اون رو با مقدار زیاد داده فراهم کنه. میانگین انتظار ما اینه که بتونیم هر پنج دقیقه یک بار باهاتون sync (همگام) بشیم.

+ سطح انتظار خوبیه.

* بهینه سازی 90 درصدی این سرویس، تونسته مدت زمان sync شدن رو تا 10 دقیقه پایین بیاره. وقتی شما تونستی بهینه سازی 90 درصدی انجام بدی، این که بیای دوباره 50 درصد بهینه سازی مجدد روی اون انجام بدی، واقعاً یک مقدار بسیار باورنکردنی برای بهینه سازی محسوب میشه. و ما در واقع تونستیم به این حد برسیم. حالا نگاه می کنی ببینی دیگه چه بهینه سازی های کوچکی می تونی انجام بدی؟ مثلاً می تونیم برای چیزهایی که واقعاً بهشون نیاز نداریم، درخواست ایجاد نکنیم؟ می تونیم جلوی درخواست هایی که می دونیم قراره فوراً شکست بخورند رو بگیریم؟ می تونیم شروع به از قبل کش کردن درخواست ها بکنیم؟ این ها دومین مجموعه کلی از محدودیت هایی بودند که ما نمی دونستیم بین حساب های کاربری AWS وجود دارند.

+ می خوایم بحث رو به باگ بانتی برگردونیم و به بینشی ساختاریافته در این باره برسیم. خب، ما کمی در مورد message broker ها و نقشی که ایفا می کنند صحبت کردیم و فهمیدیم که با استفاده از message broker ها می تونیم ساختاری بسازیم که به میکروسرویس های مختلف اجازه میده تا تسک های مختلف نظیر اسکن کردن، ریزالو DNS و… رو مرتب سازی و به هم متصل کنند. شما با توجه به سابقۀ سال ها مهندسی خود، چه message broker ها و قطعات پشته ای رو پیشنهاد می کنید که می تونه به کارمون بیاد؟

* به نظرم اینجور چیزها به مقدار زمان و پولی که دارید بستگی داره. من فکر می کنم خیلی از چیزهایی که شما می بینید مثل SQS، RabbitMQ در صورتی راه حل های خوبی به شمار میرن که شما در لحظۀ حال چیزی رو لازم داشته باشید. من RabbitMQ رو پیشنهاد نمی کنم، علتش هم اینه که خیلی سخته بخوای لایو (زنده) نگهش داری و در ادامه وقتی به یک مقیاس مشخصی برسی، یک پست وبلاگ عالی هست که میگه message brokerت رو به عنوان یک پایگاه داده استفاده نکن. ایدۀ اصلیش اینه که اگه یک پیام، مدت زمان زیادی رو در message brokerتون منتظر بمونه، در چنین حالتی در واقع شما دارید از message brokerتون به عنوان یک پایگاه داده استفاده می کنید، که نباید این کار رو انجام بدید. ایدۀ message broker اینه که شما چیزها رو پشت سر هم صف کنید و بعدش اون چیزها مورد استفاده قرار بگیرند؛ بنابراین شما باید در نهایت message broker رو خالی کنید.

– خب پس وقتی که این پیام ها در صف هستند، شما با استفاده از چه چیزی، اون ها رو دسته بندی می کنید؟

* خب ما هم از صف استفاده می کنیم، ولی ایده اینه که شما باید انقدر پروسس داشته باشید که مطمئن بشید صف شما مورد استفاده قرار می گیره. اگه شما میگید تعداد موارد داخل صفتون داره بیشتر میشه، چیزی که من به عنوان message broker بهتون پیشنهاد می کنم NATS هست که به نظرم یک نرم افزار عالی برای این حالت محسوب میشه؛ چرا که هم نسبتاً سبکه و هم انواع مختلف SDKها رو براتون فراهم می کنه. NATS برای چنین حالاتی بسیار خوب عمل می کنه و کلی پروژۀ دیگۀ هم راستا با اون هم وجود دارند که بهش کمک می کنند، از جمله پروژۀ KEDA. بنابراین، اگه شما دارید از Kubernetes استفاده می کنید، این یک روش عالی برای افزایش مقیاس محسوب میشه. خب پس NATS یه راه حل خوب محسوب میشه، می تونید سراغ گزینه های دیگه ای مثل Redis هم برید. تنها مسئلۀ دشوار در مورد Redis اینه که خیلی سخته بخوای دید خوبی ازش به دست بیاری.

+ اوه واقعاً؟ این مسئله خیلی مهمیه!

* اگه شما سوالاتی مثل این داشته باشید که مثلاً «پیام من چه مدتی داخل صف بوده؟» یا این که «پیام های من کی قراره منقضی بشن؟»، در چنین حالتی ممکنه Redis بهترین راه حل نباشه؛ چرا که اون موقع به کلی ابزار دیگه نیاز خواهید داشت که بیاد این قضیه رو براتون مدیریت کنه. Redis در کاری که باید انجام بده یعنی ذخیره کردن کلید/مقدار خیلی خوب عمل می کنه و خیلی سریع نتایج خیلی دقیقی بهتون میده، ولی برای حالتی که بخواید یک چیزی رو به عنوان message broker استفاده کنید، ابزارهایی مثل NATS قابلیت های خیلی بیشتری رو می تونند براتون فراهم کنند؛ به عنوان مثال حالتی که بخواید پیام هایی که بیش از 24 ساعت در صف بودند رو منقضی کنید. حتی اگه شما سیستمی داشته باشید که هنگ کرده باشه، یه جورایی می تونید مطمئن بشید که اون روی بقیۀ نودتون حملۀ DOS انجام نمیده.

+ این خیلی عالیه. مشکلی که بهش اشاره کردی رو من موقع کار کردن با RabbitMQ داشتم.

* آره، اون روی کل فضای ذخیره سازی شما اجرا میشه و بعد گند می زنه به دیسک های سیستم. اون وقته که مجبورم بگم خداحافظ پیام های من، خداحافظ همۀ چیزهایی که در حال اجرا شدن بودید. خب پس تا اینجا فهمیدیم که شما می خواید برای خودتون مقداری محدودیت تعریف کنید. برای مثال من می خوام بیشترین ظرفیت حافظه ام یک گیگابایت باشه، بیشترین فضای دیسکم 15 گیگابایت باشه، بیشترین طول عمر پیام هام 24 ساعت باشه و… و اگه کارها بیشتر از این مقدار تعیین شده بشه، یعنی یک جایی از کدم، اشکالی وجود داره و مشکل از message broker نیست.

+ و یکی از چیزهایی که من دربارۀ message broker یاد گرفتم این بود که وقتی داری با message broker کار می کنی، واقعاً می خوای مطمئن بشی که نرخ استفاده از موارد داخل صف، سریع تر از پخش (publish) موارد باشه. همونطور که شما هم قبلاً بهش اشاره کردید، این صف باید مدام خالی بشه و حتی اگه مقدار زیادی داده وارد پایپلاین شما شد، استفاده از صف اینطور باشه که همونطور که داری بهش مواردی رو اضافه می کنی، موارد داخلیش هم در حال تخلیه باشن.

* البته که شما یه جاهایی لبه (spike) خواهید داشت، چرا که طبیعتاً بعضی موارد لبه و تیزی بیشتری دارند، ولی در کل، هدف شما باید این باشه که در یه نقاطی این message broker کاملاً خالی باشه و اگه هیچ وقت این اتفاق نیفته، این سوال براتون پیش میاد که چرا هیچ وقت تعداد موارد داخل این message broker صفر نمیشه؟ آیا موردی وجود داره که مدت زمانش خیلی طول می کشه؟ و اینجاست که دوباره میری سراغ بهینه سازی.

+ پس ما دربارۀ NATS و KEDA فهمیدیم.

* KEDA برای مقیاس پذیری خیلی عالیه ولی عمدتاً به Kubernetes مرتبطه و اگه شما خودتون یه چیزی پیدا کردین، احتمالاً لازم نباشه از Kubernetes استفاده کنید. درسته من الان یک تیم دارم، ولی در یک بازۀ زمانی فقط خودم بودم که باید تعداد زیادی Kubernetes رو مدیریت می کردم.

+ من وقتی از این معماری استفاده می کردم، یک کلاستر Kubernetes برای میکروسرویس هام داشتم و خب، مشکلات خیلی زیادی در این زمینه برام پیش اومد. من فکر می کنم این که بخوای یک اسکن انبوه یا هر اسکن اجرایی دیگه ای رو از درون یک کانتینر انجام بدی، امکان پذیر نیست و هیچ وقت قرار نیست اتفاق بیفته، درسته؟

* باید خیلی آموزش ببینی تا بتونه به کار بندازیش. یکی از مشکلات رایجی که اغلب برات پیش میاد اینه که یه چیزی تحت عنوان «محدودیت قرارداد» برات اتفاق می افته. حالا تعریف این محدودیت قرارداد اینه که وقتی شما در سیستم عاملتون اتصالاتی رو برقرار می کنید، کرنل شما باید ببینه کدوم یک از این بسته هایی که به سیستمتون وارد/خارج میشه، واقعاً متعلق به شما هست. به لحاظ تئوری، کرنل شما این رو نمیخواد که وقتی شما یک درخواست وب به google.com می فرستی، ژورنال ها شروع به ارسال ترافیک به شما بکنند، جلوی درخواست google.com شما رو بگیرند و اون رو سمی (دستکاری) کنند. بنابراین کاری که انجام میدن اینه که تمام ترکیب آی پی و پورت های ورودی و خروجی رو ردیابی می کنند و چیزهای غیر مرتبط رو drop می کنند. این همون روندی هست که بهش قرارداد میگن و یک جدول بزرگ وجود داره که همۀ این ها رو ردیابی می کنه. حالا محدودیت قرارداد به این معنی هست ک این جدول ممکنه پر بشه که این امر در اثر اسکن پورت کاملاً محتمل هست. زیرا کاری که دارید انجام میدید اینه که دارید تعداد زیادی آدرس IP و پورت اسکن می کنید، یعنی دو تا چیزی که اساس جدول های قراردادی محسوب میشن. وقتی شما این کار رو برای میلیون ها هاست، برای ده ها هزار آدرس IP انجام میدید، جدول شما خیلی خیلی سریع شکل می گیره.

– این جدول قرارداد در سطح هاست هست یا در سطح کانتینر؟

* بستگی به این داره که شبکه تون چطوری راه اندازی شده. اگه استفادۀ شما در سطح Kubernetes یا شبکۀ کانتینر عادی باشه، شما دو تا فضای آدرس IP مختلف خواهید داشت، درسته؟ شما یک فضای آدرس IP مربوط به Pod خواهید داشت و یک فضای آدرس IP گره ها یا واسط هاتون رو. حالا شما به یک لایۀ ترجمه نیاز دارید که وقتی یک درخواست وب یا یک بسته از Pod به سمت واسط و از اون جا به سمت اینترنت میره، با استفاده از لایۀ مذکور، NAT رو به Pod ترجمه کنه. اگه دقیقاً نمی دونید که من دارم در مورد چی صحبت می کنم، باید بهتون بگم که ترجمۀ آدرس شبکه (NAT)، به شما این اجازه رو میده که پشت یک آدرس IP بزرگ، چندین آدرس IP داشته باشید.

– این همون روشی هست که روترتون با استفاده از اون به شما یک آدرس IP میده که به اینترنت عمومی وصل نیست.

* در Kubernetes هم به نوعی روند به همین شکل هست. Pod شما نیاز به آدرس IPای داره که به آدرس IP عمومی متصل نباشه، پس اونجا هم این ترجمه رو داریم. این امر در حقیقت به خاطر واسط اتفاق میفته؛ به این معنی که وقتی شما اینترنت رو از یک Pod در قالب بسته های خیلی زیاد سرازیر می کنید، همۀ اینا از طریق کرنل شما که بین فضای IP مربوط به Pod و فضای IP واسط ها قرار داره، ترجمه میشه. در یک سطح دیگه، اگر شما یک چیزی مثل AWS استفاده کنید، برای AWS هم بین فضای IP های خصوصی شما و آدرس عمومی اینترنت ترجمه انجام میشه. و هر کدوم از این ها گلوگاه های خودشون رو دارند، درسته؟ اگه قبلاً سعی کرده باشید به طور مجدد مسک (mask) انجام بدید، احتمالاً اغلب با این وضعیت روبرو شدید که به طور ناگهانی همۀ اتصالاتتون drop میشه و همه چیز ناپدید میشه. در چنین شرایطی چون مطمئن نیستید که اتفاقاتی که داره میفته، به خاطر این مسئله است یا خیر، پس کاری که انجام میدید اینه که میاید شبکۀ کانتینر رو از Pod به شبکۀ خاص (privileged) انتقال میدید. این کار از لحاظ امنیتی بده ولی به لحاظ اجرایی خوبه. به این ترتیب، شما یک لایه از دست اون خلاص میشید و سپس در بخش دوم، واسط خودتون رو از هر گونه NAT کردن (از محدوۀ IP خصوصی به محدودۀ IP عمومی) دور می کنید و اون رو مستقیماً به اینترنت عمومی می زنید. این حالت به این معنی هست که دیگه هیچ گونه گلوگاهی بین شما و اینترنت وجود نداره، مگر این که در سطح ISP یک CGNAT انجام بشه که در این صورت خود ISPتون، کلی NAT انجام میده. در چنین مواقعی می تونید از ISPتون سوال کنید که می تونیم CG Nat رو غیرفعال کنیم یا نه.

+ یکی از دلایلی که من گفتگو با شما رو بسیار ارزشمند می دونم اینه که می دونی چقدر طول می کشه به اون سطحی برسی که همۀ این مراحل رو از جدول قرارداد و بهینه سازی کرنل گرفته تا سطوح مختلف NAT کردن و امثالهم رو بتونی درک کنی؟ همۀ این ها به این خاطر هست که شما پنج سال گذشته در حال نبرد با این مشکلات بودین. واقعاً مایۀ افتخار هست که می تونیم اینجا بشینیم و همۀ این چیزها رو ازتون یاد بگیریم. به این ترتیب، شما مشکل رو با گذاشتن اون در خارج از کلاستر Kubernetes و مستقیماً روی یک باکس اینترنتی حل کردید تا گلوگاه بودن رو به کمترین میزان خودش رسونده و به این طریق بتونید اسکن موثرتری انجام بدید و بعد دوباره اون رو به message broker برگردونید.

یه سوال دیگه داشتم. مشکلی که من با ساختار پیام خودم داشتم این بود: من صفی داشتم که دیتا رو به پایگاه داده منعکس می کرد (می فرستاد) و همیشۀ خدا یک گلوگاه داشت. روند کار به این صورت بود که همۀ این دیتا وارد صف میشدند و این صف سعی می کرد در هر ثانیه 50 باجیلیون (یعنی یک مقدار خیلی زیاد) بار، پایگاه داده رو به روز رسانی کنه. اون پیام ها همیشه به نوعی اونجا گیر می کردند و به این ترتیب همه چی میرفت رو هوا.

 * پس شما به نوعی متوجه شدید که ذخیره سازی خارج از یک سیستم به مجموعۀ کاملاً متفاوتی از مسائل احتیاج داره. به همین خاطر هست که ادمین های پایگاه داده همۀ این دهه ها هنوز که هنوزه نقش خودشون رو حفظ کردند. چیزی که در چنین موقعیتی می یابید اینه که شما در موقعیت بهینه سازی پایگاه داده هستید، نه بهینه سازی برنامه!

+ راستش من هم به جای هر چیز دیگه ای از mongodb استفاده می کردم.

* ببینید، mangodb عالیه ولی شما باید الگوهای دسترسی و الگوهای داده ای خودتون رو هم درک کنید. یعنی شما باید بتونید الگوهای خواندن و نوشتنتون رو درک کنید و تصور من اینه که در موردی که شما داشتید، احتمالاً اینطور ساختاربندی کردید که مثلا من یک آدرس IP دارم که به نوعی کلید محسوب میشه و بعدش کلی متادیتا دارم که در واقع آبجکت در نظر گرفته میشه. این حالت به لحاظ سادگی خوبه و برای کوئری زدن عالی محسوب میشه، ولی از لحاظ عملیات نوشتن احتمالاً بهترین نباشه، چرا که شما دارید روی همون کلیدهای یکسان، کلی به روز رسانی انجام میدید. و شما متوجه میشی که همۀ پایگاه داده ها این مسئله رو خیلی خوب هندل نمی کنند و علاوه بر این، بسته به این که پایگاه داده تون کجا میزبانی میشه و چطور مدیریت میشه، مشکلات و مسائل مربوط به خودش رو خواهد داشت. شما در مورد پایگاه داده ها متوجه میشی که هیچ وقت واقعاً محدود به شبکه نیستید، بلکه تا حد خیلی زیادی محدود به سرعت خواندن دیسک، پهنای باند حافظه، سایز خود پایگاه داده، بزرگی کَش هاتون و امثالهم هستید.

+ حدس من اینه که Mango رو در چنین سناریویی انتخاب کنم، ولی این رو هم می فهمم که بیشتر افراد Postgres رو انتخاب می کنند. نظر شما دربارۀ این تصمیم ساختاری چیه؟ نظرتون در مورد پایگاه داده های NoSQL در مقایسه با پایگاه داده های SQL چیه؟ من فکر می کنم خود مدل در حالت عمومی به نوعی دیتای ریکان محسوب میشه و در نتیجه پایگاه دادۀ رابطه ای تا حد زیادی براش بهتره؛ که در اون می تونید به راحتی بین همۀ این آبجکت های متفاوت، نقشه رسم کنید و به جای یک بلوک JSON تکی یا امثالهم، اون ها رو پخش کنید.

* فکر می کنم لزوماً یک جواب صحیح برای این مورد وجود نداره. من می دونم که بعضی افراد سراغ پایگاه داده های گرافی هم میرن. ما برای معماری و ساختارمون، Postgres رو انتخاب کردیم؛ دلیل اصلی این انتخابمون هم بیشتر مربوط به نحوۀ استفاده از اونه؛ چرا که یکی از چیزهایی که مشتری بهش ارزش میده اینه که میگه «ما یک کوئری برای دیتا با این ویژگی می خوایم، ما می خوایم که اون در فلان بخش از زیرساخت باشه، فلان تگ رو بخوره، که آنلاین باشه، که فلان پورت رو داشته باشه، که فلان نوع عنوان رو داشته باشه و…» همۀ این عناصر رابطه ای به نوعی شِمای ما رو می سازند.

– و این مسئله همون جایی هست که در اون، پایگاه دادۀ NoSQL واقعاً لنگ می زنه و واقعاً در این زمینه عملکرد بدی داره. یه جورایی انگار مدام داری دور هر کدوم از اسناد می چرخی و هی می پرسی: آیا این مطابقت داره؟ آیا این مطابقت داره؟ …

* Postgress برای پرسیدن چنین سوالاتی عالیه.

+ رفیق، من اندیس ها رو کشف کردم! اندیس، زندگی من رو نجات داد! من شروع به اندیس گذاری همه چی کردم و اینجوری بودم که هی می گفتم اجازه بدید من یک اندیس روی هر چیزی بگذارم :))

* من ساعت بی شماری رو فقط صرف این کردم که زل بزنم به توضیحات Postgree و سعی کنم بفهمم چرا کوئری ها کند هستند. شما چندین فاز برای بهینه سازی Postgres دارید: اولین فاز اینه که خب، یک اندیس روش بگذار. فاز بعدی اینه که خب، آیا به رپلیکاهای خواندن یه نگاهی بندازم یا نگاهم به سخت افزار بهتر باشه؟ بعدش با خودت میگی «شاید سخت افزار بهتر گزینۀ مناسب تری باشه» ولی بعدش این مشکل براتون پیش میاد: حالا با این مسئله چیکار کنم که حتی با استفاده از اندیس ها هم کوئری ها زمان زیادی طول می کشن؟ خب در چنین مواقعی میخوای چیکار کنی؟ خب در چنین مواقعی من شاید شروع به از پیش محاسبه کردن دیتا بکنم، شاید شروع به استفاده از چیزهایی مثل ویوهای سریالی بکنم. این کارها شاید مشکلات فعلی رو حل کنه، ولی مشکلات مخصوص به خودش رو هم ایجاد می کنه؛ چون برای این که بتونی داده های اخیر رو داشته باشی، ویوهای تحقق یافته (materialized) مدام نیاز به تازه شدن (refresh) دارند. اما این کار هم عملیات پایگاه داده ای خودش رو استفاده می کنه و در نهایت میشه گفت: مهندسی، مجموعه ای از بده بستان ها ((trade-off محسوب میشه؛ طوری که با خودت میگی: «من این مقدار زمان دارم و این مقدار می تونم تلاش  کنم و این راه حل رو هم دارم. حالا می خوام به این برسم که چقدر باید زمان و تلاش به خرج بدم تا به بهترین راه حل برسم.»

+ توضیح خیلی خوبی بود. هیچ وقت قرار نیست راه حلی کاملاً بهینه و بدون هیچ گونه بده بستان داشته باشیم.

* برگردیم به اون سوال شما! به لحاظ معماری و ساختار، Postgres عالیه. به نظر من لازمه که شما کمی در مورد این که شِماتون به چه شکلی هست، فکر کنید. یکی از تله هایی که من تا همین اواخر متوجهش نبودم این بود که وقتی شما به طور پیوسته و در توالی بسیار بسیار سریع، ردیف های یکسان رو به روز رسانی می کنید، Postgress عملکرد بسیار ضعیفی از خودش نشون میده. و من فکر می کنم یکی از چیزهایی که آرزو می کنم کاش در گذشته انجامش می دادم اینه که کاش زمان بیشتری رو صرف فکر کردن در مورد جدول های فقط خواندنی (read-only) و فقط نوشتنی (write-only) و تقسیم بندی اون ها می کردم؛ چرا که شما به نوعی می فهمی که اگه من فرضاً همۀ این آدرس IPها رو داشتم، یک ستون هم در نظر می گرفتم که نشون میداد که آیا این آدرس ها آنلاین هستند یا آفلاین، و بعدش شاید یک timestamp بهشون اضافه می کردم که نشون می داد چه زمانی این اتفاق میفته. با این حال بیا تصور کنیم شما هر ساعت یک بار، روی این آدرس های IP اسکن انبوه اجرا می کنی، این طوری به شکل موثری هر ساعت یک بار کل این جدول رو بازنویسی می کنی؛ حتی اگه وضعیت آنلاین/آفلاین بودن رو هر دفعه به روز رسانی نکنی، اگه timestamp رو به روزرسانی کنی و بگی من آخرین بار این مورد رو در فلان ساعت به روزرسانی کردم، روشی که Postgres در چنین حالتی قراره در پیش بگیره اینطوریه که:

به جز حالتی که شما چیزی رو دارید که اون ها ازش تحت عنوان Hot tuple (فضای اضافه برا انجام عملیات کوچک) یاد می کنند، Postgres سطر شما رو حذف می کنه و یک سطر جدید ایجاد و درج می کنه. و این همون روشی هست که postgres با استفاده ازش خصوصیات داراییش (asset properties) رو نگهداری می کنه؛ درست مثل حالتی که وقتی من چیزی رو می نویسم و هنوز تمومش نکردم، شما بخشی از آن چه که من نوشتم رو نمی خونید. این یک دید جالب اصلاحی از دنیاست. بنابراین، این که به طور مرتب چیز یکسانی رو به روزرسانی کنی، به این معنی هست که شما یک سطر یکسان رو حذف می کنی و دوباره چندین بار اضافه اش می کنی. راه حلی که اون ها از طریقش این مشکل رو حل می کنند، چیزی تحت عنوان جاروبرقی (vacuuming) هست که به معنی پاکسازی سطرهای حذف شده هست تا به این طریق بتونی دوباره سطرها رو اونجا بنویسی. کلی تنظیمات برای vacuum وجود داره که ازتون خواسته میشه اون ها رو انجام بدید. اگه بیشتر از توان vacumتون، عملیات نوشتن رو انجام بدین، فضای شما با سرعت خیلی زیادی رشد می کنه یا اندیس هاتون بسیار ناکافی و ناکارآمد میشن؛ چرا که جدول شما به اصطلاح نفخ می کنه و انگار فقط 100 مگابایت از هر یک گیگابایت فضایی که دارید، اشغال میشه.

پس میشه اینطور گفت که بسته به مقیاسی که دارید، یه جاهایی Postgres می تونه خوب عمل کنه. اگه شما مقیاس کوچکی دارید و فقط چند نفر رو پوشش میدید، در این صورت مشکلی پیش نمیاد؛ اما وقتی با صدها گیگابایت یا ترابایت داده کار می کنید، قضیه فرق می کنه و باید به سوال کاملاً متفاوتی پاسخ بدید.

– علاوه بر این، شما موقع کار کردن با جدول فقط نوشتنی (write only) یا جدولی که لزوماً سابقۀ اتفاقات در حال وقوع و تغییرات ایجاد شده در طول زمان و… رو نگه میداره، مزایایی رو هم تجربه می کنید.

* چنین جدولی بسیار بسیار سریع بزرگ و بزرگ تر میشه، پس شاید لازم باشه به راه حل هایی مثل Clickhouse یا پایگاه داده های مبتنی بر ستون یه نگاهی بندازید. این راه حل ها، داده ها رو به شکل نسبتاً متفاوتی فشرده سازی می کنند و برای مواردی مثل این بهتر عمل می کنند. من مقایسه رو به سمت IoT می برم. از جمله نمونه کاربردهای IoT میشه به این اشاره کرد که با استفاده از چیزهایی مثل Clickhouse دما رو در جای جای دنیا اندازه گیری می کنید. در هر ثانیه شما یک دمای جدید ثبت می کنید و اون رو به پایگاه داده اضافه می کنید که در ذخیره سازی این دماها می تونند بسیار کارآمد عمل کنند. به نظرم مورد ما هم خیلی به این قضیه شبیه هست. به جای این که دما رو ذخیره کنیم، در هر ساعت بین میلیون چیزهای دیگه ای که در دنیا وجود دارند، ما چیزهای آنلاین و کدهای وضعیتمون رو ذخیره می کنیم. بنابراین، بعضی وقت ها این می تونه نمونه کاربرد بهتری باشه، اما به نوعی یک بده بستان هم میاد وسط، اونم این که «من چطوری قراره این رو کوئری کنم؟»، «آیا من فقط به این علاقه مندم که بدونم نتیجۀ نهایی چیه یا این که به ترندها علاقه دارم یا این که به قطعه قطعه کردن داده هام بر اساس این که کدوم مشتری درخواست کرده، علاقه مندم؟»

+ به همین خاطر بود که این سوال رو پرسیدیم. چرا که در ابتدای کار شاید ما فقط بخوایم از خط فرمانمون کوئری انجام بدیم، ولی شاید یه روزی هم دلمون بخواد یه نگاهی به ترندها بندازیم مثلاً این که کِی به کِی این آدرس IPها چرخش پیدا می کنند یا …

– یا از دید مشتری، بخوایم بدونیم کی تغییری ایجاد شده یا چرا داریم فلان رفتار عجیب رو مشاهده می کنیم.

* ما خیلی خیلی وقت ها این سوال رو دریافت می کنیم. به نظرم برای افرادی که باگ بانتی کار می کنند، این یک مجموعه سوال، کاملاً متفاوت محسوب میشه و من فکر می کنم چیزی که شما باید براش بهینه سازی انجام بدین، این نیست که یک نمونه کاربرد یادگیری طولانی مدت براش وجود داره، بلکه برا اینه که «من همین الان به چه اطلاعاتی نیاز دارم و چطور می تونم به این اطلاعات دست پیدا کنم؟». به محض این که این کار رو انجام دادید، می تونید بگردید ببینید کدوم شِما برای نمونه کاربردتون مناسبه. مطمئناً من نمی تونم به شما بگم کدوم شِما خوبه، چرا که شما پنج تا مهندس پشتتون ندارید که کل این سیستم رو ساخته باشه یا …

+ یا اینکه شما یک اپلیکیشن سازمانی نیستید. بیاید دربارۀ یک نمونه کاربردی فکر کنیم، اینطوری راحت تره. تقسیم دامنه ها و آدرس های IP به شکل منطقی و عدم در نظر گرفتن زیردامنه ها به عنوان کلید اصلی، بزرگ ترین اشتباه ساختاری من بود که هنگام ساخت سیستم مروری خود مرتکبش شدم. همۀ توجه من مشغول زیردامنه ها بود و درسته که یک زیردامنه محصول نهایی (پایان همه چیز) محسوب میشه، اما در نهایت همه چیز به آدرس های IP برمیگرده. این رو گفتم تا بحث رو به اینجا برسونم: اشتباهات این چنینی مثل تمرکز بر روی زیر دامنه ها به جای تمرکز روی آدرس IPها به عنوان پایین ترین سطح موجودیت های منطقی، چی هستند و چرا اتفاق می افتند؟

* من فکر می کنم این مسئله هم به نوع ریکانی که در حال انجامش هستید برمی گرده. به نظرم زیردامنه ها از این جهت خوب هستند که ساده ترین راه شناخته شده برای بررسی چیزها هستند. دلیل این که شما میگید آدرس های IP مناسب تر هستند اینه که تعداد زیادی دارایی وجود دارند که به هیچ زیردامنه ای نپیوسته اند یا این که هنوز روی اینترنت نیستند و شما نمی تونید ببینیدشون.

– یک مجموعۀ مجزا از تله ها هم وجود دارند که وقتی به این سطح می رسید ممکنه براتون اتفاق بیفته. از جمله این که چیزهایی مثل زیردامنه ها دوباره تخصیص داده میشن؛ از این رو ممکنه یک آدرس IP رو ذخیره کرده باشی و وقتی داری اسکنش می کنی، متوجه سرویسی جدید روی اون بشی که قابل اکسپلویت و بهره برداری هست، ولی در ادامه متوجه میشی که اون به شخص دیکه ای تعلق داره. بعدش میای این مورد رو به شرکت مربوطه گزارش میدی و اون ها در جواب میگن این آدرس IP متعلق به ما نیست. به نظرم همۀ اینا به نحوی به فاکتورهای همبستگی بستگی داره. فکر می کنم قطعاً اون می تونه یک موجودیت منطقی جدید در یک سیستم ساختاری ریکان باشه؛ منظورم اینه که مثلاً شما فلان دامنه رو داری، داری فلان پایگاه داده رو استفاده می کنی، شما فلان زیردامنه یا FQDN رو داری و فلان آدرس IP رو داری و نوعی رابطه هم بین این دو تا داری. حالا این که چطور این دو تا با هم رابطه دارن، می تونه یک DNS entry اون ها رو به هم مرتبط کرده باشه؛ می تونه یک TLS باشه، یک SNI باشه، می تونه داده های مربوط به تاریخ باشه، به نظرتون دیگه چی می تونه باشه؟

* من چندین سال هست که دارم روی این ایده مدلسازی می کنم و سعی کرده ام در AssetNote اجراییش کنم، اما نمونۀ کاربردی برای این موضوع کمی متفاوت و ظریفه، مگه نه؟ وقتی شروع به دسترسی بیشتر و بیشتر به برنامه های بومی Cloud می کنید، روش هایی که از طریق اون یک دارایی رو شناسایی می کنید، شروع به تغییر بسیار زیادی می کنه. در انجام تنظیمات به روش قدیمی، احتمالاً روند به این شکل باشه که ما یک آدرس IP روی سرورها داریم و ممکنه یک زیردامنه هم بهش اختصاص بدیم، اما وقتی شما از منظری دیگه نگاه می کنی، Uber شروع به مسیردهی چیزها بر اساس مسیر فرعی شما می کنه و اگه بخواید دقیق تر بشید، Uber بر اساس مسیر فرعی شما در یک موقعیت جغرافیایی مشخص، شروع به مسیریابی چیزها می کنه. از این رو حتی اگه شما همون آدرس IP رو با همون زیردامنه و همون مسیر انتخاب کنید، شما دو تا دیتاسنتر مختلف رو انتخاب خواهید کرد که ممکنه در حال اجرای نسخه های متفاوتی از اپلیکیشن شما باشند. علاوه بر این، ممکنه که ویژگی ها بر اساس User ID شما، فلگ خورده باشن و به نسخۀ دیگه ای از اپلیکیشنتون مسیردهی بشن. همۀ این ها به این معنی هست که در نهایت، اپلیکیشنی که بهش نگاه می کنید ممکنه نسخۀ یکسانی نباشه. ممکنه شما یک نسخۀ چینی از اپلیکیشن Uber رو در مقابل نسخۀ آمریکایی این اپلیکیشن تحویل بگیرید یا حتی ممکنه نسخۀ بتای اپلیکیشن Uber رو در مقابل نسخۀ تولیدی گرفته باشید. و من فکر می کنم فاکتورگیری از همۀ این ها به چیزی که شما ازش تحت عنوان همبستگی (correlation) یاد می کنید، احتمالاً برای بیشتر افراد زیاده روی باشه.

خب نظرتون دربارۀ این چیه: شما برای هر شرکتی به صورت مجزا، یک همبستگی منحصر به فرد ایجاد می کنید، مدل ذهنیتون رو می سازید، مسئول مربوطه رو عقب می کشید و ازش در مورد نحوۀ استقرار اپلیکیشنشون سوال می پرسید. و بعد از اونجا شروع به کار کردن به سمت عقب می کنی و با خودت میگی که من فاکتورگیری می کنم و برای این کار این مسئله رو در نظر می گیرم که فرضاً برای اون ها موقعیت جغرافیایی مهم نیست، User ID مهم نیست، به جاش tenant IDشون مهمه، زیردامنه شون مهمه و… ولی برای Uber این داستان کاملاً متفاوته، ما در یک پارادایم کاملاً متفاوت هستیم و روش همبستگی مورد استفاده کاملاً متفاوته.

+ نکتۀ خوبی بود و خیلی خوب به نقص موجود اشاره کردید. این مسئله یک جورایی فراتر از مهندسیه و در نهایت شما به چنان سناریوهایی برمی خورید که می دونی همه چی عالی نیست ولی بعدش این تصمیم رو می گیرید که آیا این یک بده بستونه؟ آیا برطرف کردن این مشکل، به حد کافی برای من بانتی میاره که بتونم زمانی که براش صرف می کنم رو توجیه کنم؟ بیشتر وقت ها جواب نه هست، مگر اینکه اون رو به کار و دغدغۀ خودتون تبدیل کنید.

+ آره، بعضی وقت ها شما ممکنه سیستمی داشته باشید که 90 درصد موارد کار می کنه، بعدش تصمیم می گیرید که مثلاً سه هفته زیرساخت گوگل رو بررسی کنید که خب حالا این پایه و اساس خودش رو داره و کتاب هایی مخصوص به خودش نوشته شده؛ پس ما به چیزی نفوذ می کنیم که فقط مخصوص به این حوزه هست. شاید به این نکته برسی که خب حالا مثلاً من سه ماه روی این مورد زمان گذاشتم و به حد کافی درکش کردم، پس حالا می تونم اون رو به خط اصلی (mainline) ادغام کرده و برگردونم و اون رو بین بقیه موارد تغذیه کنم. شما نباید همه چیز و هر موضوعی رو به هیولای غول پیکرتون تبدیل کنید، همیشه می تونید قطعات کوچکی داشته باشید که برای چیز دیگه ای هستند و بعد این که به نحوی فهمیدید اون چیز چطور کار می کنه، طبیعتاً می تونید سراغ ادامۀ کاری که در حال انجامش بودید برگردید.

+ عجب، همون طور که شما گفتید من از دنیای ریکان خودم عقب نشینی کردم به دنیای رویداد تست نفوذ زندۀ خودم؛ و خب می دونی، من اخیراً انقدر دستام روی کیبورد هست و مدت زمان زیادی رو صرف تست نفوذ دستی می کنم که همۀ اون چالش های شگفت انگیزی که در دنیای ریکان داره اتفاق میفته رو فراموش کردم.

– اون یک مشکل مهندسی هست، یه فضای مسئلۀ مهندسی هست. تو باید این مسئله رو درک کنی که اگه سعی داری به تنهایی با یک مشکل مقابله کنی و داری با این موضوع سر و کله می زنی و مهندس هم نیستی، چنین وضعی قابل انتظار هست. تو باید بدونی که حل مشکل موردنظر سخته! مهندسان تمام وقت هم هر روز کل روزشون رو صرف حل این مشکل می کنند. چندین تیم مهندسی وجود داره که کل روزش رو صرف برطرف کردن این مشکل کرده که یا نمی تونه حلش کنه یا مجبوره هفته ها و حتی سال ها برای حل مشکل مربوطه وقت بگذاره. برای همین من بازم فکر می کنم هر چیزی رو که نیاز به بهینه سازی داره، بهینه کنید. دربارۀ مسائلتون فکر کنید، در مورد محتوا فکر کنید، فکر کنید ببینید چه داده هایی نیاز دارید و روی چه تارگت هایی تمرکز کردید، اگه باید بهینه سازی انجام بدید، حتماً بهینه سازی کنید و اگه نباید بهینه سازی انجام بدید، این کار رو نکنید. می دونی، منظورم اینه که هر جایی لازمه اون رو به کار بگیرید، به طور کامل ازش استفاده کنید و اگه براتون کافی نبود، بهبودش بدید.

* یا یک شرکت رو استخدام کنید تا براتون این کار رو براتون انجام بده.

– آره، برید سراغ AssetNote :))

+ فکر می کنم یکی از چیزهایی که می خواستم مطرح کنم تا به نوعی به محتوای فنی عمیقمون برگردیم این بود که: می تونید ما رو یکم در مورد اینکه چطور با وایلدکارد های DNS سر و کله بزنیم و در این زمینه از چه نوع تله هایی می تونیم اجتناب کنیم، راهنمایی کنید؟ تا Sean یک ثانیه فکر می کنه، برای اون دسته از مخاطبینی که کاملاً با این مفهوم آشنا نیستند، باید بگم که بعضی دامنه ها مثل Facebook، Zoom، Spotify و… تعداد زیادی وایلدکارد دارند و این مسئله به این معنیه که شما می تونید هر رشتۀ تصادفی رو در مثلاً zoom.us بگذارید و اون به یک آدرس ریزالو بشه.

– اساساً اون چیزیه که به شما اجازه میده زیردامنه هایی مثل store.myshopify.com یا هر چیز مشابه دیگه ای رو تولید کنید. روشی که همۀ اون ها بدون ایجاد DNS entry های جدید مسیریابی میشن اینه که یک DNS entry همه جانبه دارند که همون وایلدکارد هست. به همین خاطر هر چیزی دات هر چیزی (anything.anything) به مجموعه ای یکسان از آدرس های IP یا CNameها ریزالو میشه و در نتیجه بعدش مسیردهی میشه.

+ حالا بزرگترین مشکل این موضوع، تشخیص اینه که کدوم یک از پاسخ هایی که برای زیردامنه ها دریافت می کنیم، پاسخ (ریپسانس) وایلدکارد هست (که همیشه قراره یکسان باشه و هیچ دادۀ جالبی نداره) و کدوم یکیشون منحصر به فرده؛ و خب این یکی از سخت ترین مسائل محسوب میشه.

* روشی که ما این مسئله رو در سرکار حل می کنیم کاملاً متفاوت از روشی هست که در بانتی ها حلش می کنیم. وقتی سر کار این مسئله رو حلش می کنیم، به اکانت AWS شما دسترسی داریم، در نتیجه بهش احتیاجی نداریم و می تونیم کلی از انرژیمون رو ذخیره کنیم. اما در فضای باگ بانتی، ما این کار رو زمان ریزالو انجام میدیم؛ ما ریزالور سفارشی خودمون رو می سازیم که همۀ کارهای مربوط به بررسی ریزالو شدن رو انجام میده و تا حد زیادی در هر لایه عمل تشخیص وایلدکارد رو هم انجام میده. بنابراین، در هر عمقی از این به روز رسانی، ما سعی می کنیم تشخیص بدیم که این مورد وایلدکارد هست یا نه، می فهمیم که چی انتظار میره وایلدکارد باشه، چی انتظار نمیره وایلدکارد باشه و سپس قضیه به نوعی بستگی به این داره که در اون نقطه برای چی دارین بهینه سازی انجام میدین. آیا تمایل داری میلیون ها تلاش برای بارگزاری رو ریزالو کنی تا یکی دو تا رکورد پیدا کنی که درست باشه یا این که می خوای کل مجموعۀ فعلی رو دور بندازی و بری سراغ بعدی؟

+ هر دوی این ها انتخاب های خوبی هستند. من قبلاً هر دوشون رو انجام دادم و هر دوشون به نتایج بسیار خوبی منجر شدن. اولین مورد به این خاطر که اگه واقعاً روی مسئله ای متمرکز بشید، نتیجه این میشه که هاست هایی پیدا می کنید که هیچ کس دیگه ای پیداشون نکرده، چرا که اون ها تمایلی به این ندارند که هفته ها یک مورد رو کند و کاش کنند؛ اگر هم برید سراغ گزینۀ دوم و همه چیز رو نادیده بگیرید، ساعت ها ساعت ها و ساعت ها زمان رو ذخیره می کنید تا یکی از سخت ترین مسائلی که بهش برخوردید رو حل کنید.

– در سطح ظاهری، ساده ترین راه اینه که نادیده بگیری یا این که هر تک مورد رو بررسی کنی. هیچ کدوم از این گزینه ها کاملاً عالی نیستند؛ یکیش کاملاً خسته کننده است و دیگری یه حفره و گپی رو از خودش به جا می گذاره.

* اون روش فقط وقتی خسته کننده است که کند باشه، ولی اگه فرضاً قادر بودی میلیون ها نتیجه رو در هر ثانیه ریزالو کنی، دیگه لازم نیست بده بستون انجام بدی، فقط کافیه میلیون ها نتیجه رو در هر ثانیه ریزالو کنی.

– البته همونطور که گفتید: فرضاً!

+ که این هم به بهینه سازی برمی گرده، درسته؟ و من میخوام بگم که مدت زمان واقعاً زیادی میشه که من کدم رو نگاه و بررسی نکردم، ولی میخوام بگم وقتی سعی می کنم این مشکل رو حل کنم و در واقع تلاش کنم همۀ وایلدکاردها رو حذف کنم، در واقع من یک زیردامنۀ مشخص رو ریزالو کردم، یه چیزی بهش اضافه کردم و چندین بار تونستم اون رو هم ریزالو کنم. نتیجه رو مقایسه کردم و برای هر entryای که این کار رو انجام می دادم، سه یا چهار بار عمل ریزالو رو انجام می دادم. در نتیجه در اون ظرفیت، به صورت خطی داشت رشد می کرد. یه بخش دیگه از این مسئله اینه که: شما می تونید با خودتون بگید که خب باشه، من به نوعی فهمیدم که آیا این بخش از زیردامنه، وایلدکارد هست یا نه؛ اما بعدش به سناریوهایی برمی خورید که در اون اوضاع به این شکله:

Subdomain.anything wildcard

و بعدش دوباره پایگاه دادۀ شما شروع به منفجر شدن می کنه.

* جواب همه چیز در علوم کامپیوتر، یک hash map هست. یعنی میگی هش مپ همۀ وایلدکاردها برای تک تک سطح ها؛ بعدش می تونی هر یک از سطوح مربوطه رو به حالت ضربدری با هم مطابقت بدی و ببینی تا چقدر جلو میری.

+ چه ساختار زیبایی! من عاشق هش مپ ها هستم. این توصیۀ خوبیه. به عنوان مثال uber.com رو در نظر بگیریم. مثلاً من تشخیص میدم که test.uber.com وایلدکارد نیست، ولی abc.uber.com وایلدکار هست و بعد همونطور که زیردامنه ها تغییر می کنند، می تونی به رشد این روند ادامه بدی و بعدش به یک نقطه ای برسی که ببینی یک موردی رو قبلاً ریزالو کردی (یعنی همۀ زیردامنه ها تعیین تکلیف شدن).

خب، ما همین الان داریم این علامت رو از مدیرمون می گیریم که دیگه باید این اپیزود رو جمع و جور کنیم. Sean، رفیق، مرسی که اومدی، مرسی که بهمون اجازه دادی اطلاعات رو از ذهنت بیرون بکشیم. ما حتماً دوباره از Sean دعوت می کنیم. Sean، آیا چیزی مونده که بخوای با مردم به اشتراک بگذاری؟

* آره، تا وقتی که مجبور نبودی، سعی نکن مسائل سخت رو حل کنی و این که وقتی می تونی آسیب پذیری های راحت پیدا کنی، سراغ آسیب پذیری های سخت نرو.

– و این که مردم کجا می تونند AssetNote رو پیدا کنند؟

* AssetNote رو می تونید از https://www.assetnote.io و https://www.assetnote.com و همچنین در توییتر با آی دی @ assetnote پیدا کنید.

دیدگاهتان را بنویسید

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