جمع آوری و تحلیل داده های قبل از سفر در تیم نقشه اسنپ

یکی از بزرگترین چالش‌های اسنپ، سردرآوردن از رفتار کاربران و برخورد آن‌ها با اپلیکیشن درخواست خودروی اسنپ(Snapp Cab یا اپلیکیشن مسافر) است. حجم زیاد داده‌ها و پیچیدگی ذاتی‌ آن‌ها در کنار مسائلی مثل حفظ امنیت داده‌ها و کاربران و ... همه دست به دست هم میدن تا این چالش رو سخت‌تر هم بکنن. در این مقاله نگاهی به تلاش ما برای ساخت سیستم و پایپ لاینی(pipeline) برای مدیریت و نگهداری این حجم از داده برای تیم نقشه‌ اسنپ میندازیم.


در اسنپ منظورمان از pre-ride چیست؟

اگر تا به حال از اسنپ استفاده کرده باشید، حتما قبل از زدن دکمه "درخواست سفر" به آیتم‌هایی مثل انتخاب مبدا یا مقصد روی نقشه، استفاده از گزینه‌های سفر یا انتخاب مکان‌های پیشنهادی یا مکان‌های منتخب و … برخورد کردید.

ما در تیم نقشه اسنپ به هر رخداد (event) ایی که از لحظه باز کردن اپلیکیشن مسافر ( یا Snapp Cab) تا لحظه شروع "جستجوی راننده" و قبل از دادن درخواست سفر انجام میشه pre-ride event میگیم.
pre-ride session
pre-ride session

دانستن اینکه کاربران اسنپ چطوری با این سرویس‌های "قبل از سفر" یا pre-ride تعامل میکنن و کیفیت این سرویس‌ها چقدر است برای ما اهمیت زیادی داره و به تیم ما کمک میکنه تصمیم‌هایی مبتنی بر داده‌های واقعی برای آینده محصولات خودمون بگیریم.

چالش‌های فنی مسیر

اگر نگیم همه، اکثر سیستم‌ها و سرویس‌های اسنپ یک Big Data System محسوب میشن.

کلان داده(Big Data) معمولاً به مجموعه داده‌هایی گفته می‌شود که بیش از حد بزرگ یا پیچیده هستند بطوریکه نمی‌توان با نرم‌افزارهای عادی و روش های متداول آن‌ها را پردازش یا تحلیل کرد.

چالش‌های کار کردن با این حجم از داده کم نیست، داریم در مورد صد‌ها یا هزاران write در ثانیه صحبت میکنیم، معمولا به منابع سخت افزاری زیاد به همراه نرم افزار‌های خاصی که برای کارکردن با کلان داده طراحی شدن نیاز است. این مقاله آشنایی خوبی در مورد مبحث کلان داده به شما میده اما اگر دوست دارید عمیق‌تر یادبگیرید کتاب Designing Data-Intensive Applications منبع فوق العاده‌ای برای آشنایی با طراحی و تحلیل همچین سیستم هایی هست.

به طور کلی، طراحی سیستم‌های کلان داده‌، جز کارهای سهل ممتنع به حساب میاد! در نگاه اول و روی کاغذ شاید ساده باشه ولی در اجرا بسیار دشوار میشه!

تعریف و شرح مسئله

قبل از اینکه به یک دنبال راه‌ حل فنی باشیم، بهتره مسئله رو شفاف کنیم. فرض کنید میخواییم سیستمی طراحی کنیم که در تحلیل داده‌های pre-ride به ما کمک کنه:

ابتدا باید به این سوالات پاسخ بدیم:

  • این داده‌ها چقدر اهمیت دارن؟
  • این داده‌ها برای چه مدتی باید ذخیره بشن؟
  • حجم داده‌ها چقدر است؟ با چه سرعتی تولید میشن؟
  • تحلیل داده‌ها به چه صورت خواهد بود؟ (آنلاین و real-time، آفلاین و …)
  • آیا data loss یا از دست رفتن داده مهم است؟ (availability)

پاسخ ما، با توجه به نیازمندی‌های تیم از محصول، به صورت زیر است:

  • بیشتر تعامل‌های کاربر ( به خصوص آن‌هایی که با نقشه سر و کار دارند) در pre-ride به صورت کوتاه‌مدت (برای دسترسی سریعتر و راحتتر) و بلندمدت ذخیره بشه
  • متریک‌های مهم (مثل مدت زمان سرچ کاربر، زمان سرویس دهی و …) به صورت آنلاین و تا جای ممکن لحظه‌ای استخراج و مانیتور بشن
  • سرویس‌ها تا جای ممکن تلاش کنند پایدار(stable) و در دسترس باشن
  • از دست دادن داده برای بازه‌های بسیار کوتاه زمانی یا به صورت اتفاقی اهمیت زیادی نداره

از این جا به بعد کلماتی مثل لاگ(log) و داده(data) با معنی نسبتا یکسان و مشابه استفاده میشن. منظور از متریک (metric) هم در این متن یک پارامتر است که کیفیت یا کارایی یکی سیستم رو مشخص میکنه. مثلا فرض کنید نیاز داریم بدونیم آیا خیابون‌های نقشه با سرعت کافی لود میشوند یا خیر، برای این کار نیاز است متریک‌های مرتبطی تعریف کنیم، اون‌ها رو جمع آوری و مانیتور کنیم.

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

تلاش‌های قبلی

قبل از اینکه به راه حل و سیستم جدیدی که تیم ما طراحی کرده برسیم،‌ بیاید نگاهی به یک تلاش قبلی بکنیم. قبل از طراحی و پیاده‌سازی سیستم جدید (که جلوتر توضیح داده شده) سیستمی به اسم SSP یا Search Stream Processing یکی از اولین پروژه‌های تیم نقشه جهت ارزیابی سرویس جستجو(search) روی نقشه‌ی اسنپ بود. این سرویس با Java نوشته شده بود و از Apache Flink برای پروسس کردن دیتا استفاده میکرد. لاگ‌های ورودی از طریق Logstash جابجا میشدن و پس از عبور از یک Kafka به درون یک دیتابیس Elastic Search قرار میگرفتن. در اینجا بود که با استفاده از Kibana بالاخره میتونستید لاگ‌ها و متریک ها رو ببینید!

فکر میکنم واضح باشد که این سیستم سربار و پیچیدگی زیادی داره، از مصرف منابع گرفته تا نگهداری و توسعه دادن آن. با گذشت زمان مشخص شد که لازم است از اول یک مجموعه از سرویس ها رو طراحی و پیاده‌سازی کنیم که به نیازهای ما پاسخ بده، چه نیازهای فعلی و چه نیازهایی که ممکن است در آینده پدیدار بشن. دقت کنید که به طور خلاصه ما به دنبال یک تعادل مناسب بین سه فاکتور مهم: scalability ,maintainability ,reliability هستیم. در ادامه که به طراحی یک سیستم جدید میپردازیم سعی میکنیم به طور ضمنی این پارامتر‌ها را رعایت کنیم.

یک راه حل مدرن چه شکلی خواهد بود؟

شاید واژگانی مثل:

ETL (Extraction, Transformation, Loading)

ELT (Extraction, Loading, Transformation)

ETTL (Extraction, Transformation, Transportation, Loading)

به گوشتون خورده باشه. خیلی از راه حل‌های مدرن شرکت‌های بزرگ برای مقابله با این حجم از داده‌ها مبتنی بر همین کلمات است!

  1. داده ها رو جمع کن (Extract)
  2. روی داده‌هات transformation ایی که لازم داری رو انجام بده.Aggregation, Splitting, Mapping, Filtering, Joining, Duplication همه نمونه‌هایی از این transformation ها هستن که ممکنه روی داده ورودی اعمال بشن.
  3. داده‌های مرحله قبل رو در یک محل ذخیره سازی موقت ( و اولیه) که توانایی مدیریت این حجم از داده رو داشته باشد قرار بده (transportation)
  4. در صورت لزوم و برای استفاده های بلند مدت تر، داده‌های مرحله قبل رو به یک دیتابیس (database) ثانویه و قابل اتکا ( در بلند مدت) انتقال بده. به این نوع از دیتابیس ها data warehouse گفته میشود.

همانطور که میبینید، در گام‌های ۳ و ۴، دیتای خام، یا تبدیل شده، در یک دیتابیس ذخیره میشه و بنابراین هرکسی که نیاز به تحلیل و بررسی اون‌ها داشته باشه میتونه از این دیتابیس‌های اولیه و ثانویه استفاده کنه. اصولا دیتابیس اولیه(یا موقت) برای استفاده‌های لحظه‌ای تر مثل real-time analysis استفاده میشه.

البته دقت کنید که معماری سیستم ما قرار نیست کاملا وفادار به قرارداد‌های عمومی مثل ETL باشد. در بعضی از جاها ETL، بعضی جاها ELT و در بعضی از جاها ETTL میتونه باشه! بستگی داره از چه زاویه‌ای نگاه کنی!!!

معماری کلی

طبق صحبت‌های قبلی، به نظر میرسه که سیستم ما قراره چند بخش اساسی داشته باشه:‌

  1. پایپ لاینی برای انتقال داده‌ها
  2. سرویس یا سرویس‌هایی برای اعمال transformation ها
  3. محلی برای ذخیره‌سازی داده‌های اولیه و نهایی

پایپ لاینی برای انتقال داده

در کل انتقال لاگ/دیتا بین سرویس‌ها و ابزار‌های مختلف کار ساده‌ای نیست. یک ابزار جدید و قدرتمند برای اینکار Vector است. Vector کمی با سرویس‌های نام آشنایی مثل Kafka یا RabbitMQ یا NATS و حتی Logstash فرق داره. این ابزار به طور خاص طراحی شده که به برنامه نویس‌ها امکان ساخت پایپ لاین های دیتای سبک و بسیار سریع رو بده. Vector سریع و بهینه است، به راحتی روی سرور deploy و آماده استفاده در محیط production میشه. مکانیزم کلی به این صورت است که شما یک یا چند Source به عنوان ورودی و یک یا چند Sink به عنوان خروجی تعریف میکنید. در کانفیگ خود برنامه هم میشه یک سری transformation تعریف کرد. در واقع Vector به خودی خود یک پایپ لاین ETL کامل است! البته چون این ابزار انعطاف پذیری کافی رو به ما نمیداد، ازش صرفا برای انتقال داده استفاده کردیم و ترجیح دادیم که این پیچیدگی‌ها رو به نرم‌افزار‌های خودمون انتقال بدیم تا یک سرویس خارجی.

استفاده از Vector باعث شد به نسبت Kafka ایی که در پایپ لاین قبلی داشتیم حجم زیادی در منابع صرفه جویی کنیم (در حدود ۱۰ برابر!).

سرویس‌هایی برای اعمال transformation ها

خب، وقت اون رسیده که دست به کد بشیم! با زبان Go سرویس‌/میکروسرویس‌هایی رو توسعه میدیم که وظیفه تغییر دادن دیتا و استخراج متریک‌های مورد نیاز ما رو بر عهده میگیرن. تلاش ما این است که اکثر Business Logic مثل اینکه چه نوع از لاگ‌هایی ورودی معتبر هستن و … در این سرویس‌های Validate بشوند. این سرویس‌‌ها باید Stateless باشند تا امکان مقیاس پذیری افقی رو (Horizontal scaling) برای ما فراهم کنن.

همچنین انتخاب زبان Go هم بی دلیل نیست، اولا که این زبان برای محیط‌ کلاد (Cloud) و برای سرویس‌هایی که قراره حجم زیادی از ریکوئست‌ها رو مدیریت کنن انتخاب بی نظیری هست. از طرفی عمده سرویس‌های اسنپ نیز با Go نوشته شدن بنابراین منطقی ترین انتخاب برای ما همین Go میتونه باشه.

محلی برای ذخیره داده

در اینجا میخواهیم از Clickhouse استفاده کنیم. دیتابیس Clickhouse در وهله اول یک دیتابیس OLAP(Online Analytical Processing) است. این دسته از دیتابیس‌ها برای کار ما عالی هستن چون طراحی شدن که روی حجم زیادی از داده، کوئری‌های آنالتیکال اجرا کنن.

OLAP vs OLTP
OLAP vs OLTP

تجربه ما نشون داده که این دیتابیس تمام معیار‌های ما برای یک دیتابیس خوب رو تیک میزنه! به راحتی مقایس(scale) پیدا میکنه، به اندازه کافی بالغ شده و سرعت فوق العاده بالایی داره، از منابع سخت افزاری خود به خوبی استفاده و ویژگی‌های زیادی رو در اختیار توسعه دهنده قرار میده و در نهایت، SQL رو هم پشتیبانی میکنه. البته که برای استفاده از تمام پتانسیل‌های Clickhouse باید آستین ها رو بالا بزنی و یادبگیری چطوری ازش درست استفاده بکنی چون این دیتابیس Config های متفاوت و زیادی داره که معمولا در مواجه با حجم زیاد داده اهمیت پیدا میکنن.

سیستم نهایی

خلاصه صحبت‌های بالا رو میتوان در دیاگرم زیر، که همان پایپ لاین جمع‌اوری داده ما است، ببینیم:

این سیستم از بخش‌های زیر تشکیل شده:

  1. فرآیندهایی برای process کردن داده‌های ورودی و تبدیل آن‌ها به متریک و داده‌های قابل استفاده
  2. ذخیره موقت خروجی بخش ۱ برای استفاده‌های کوتاه مدت (مثلا بررسی وضعیت فعلی کیفیت سرویس‌هایی مثل سرچ نقشه، تایل‌ها، مکان‌های منتخب و …)
  3. ذخیره دائمی خروجی بخش ۱(نتیجه پراسس ها) برای تحلیل‌های عمیق تر توسط تحلیل‌گران داده یا مهندسان اسنپ یا استفاده از داده‌ها برای مدل‌های آماری و …

بهتره اشاره کنم که داده‌ها به صورت رمزنگاری شده جابه‌جا و ذخیره میشن که از امنیت آن‌ها مطمئن باشیم. سیستم ارائه شده به دلیل استفاده از ترکیب Go, Clickhouse, Vector به مراتب کمتر از قبل منابع استفاده میکنه، درحالی که سرعت بالاتری هم داره و آسونتر توسعه پیدا میکنه.

یک سوال: به نظرتون اگر قرار بود بر اساس مدل ETL یا ETTL یا مدل‌های مشابه، این سیستم را تقسیم میکردیم، کدوم بخش E بود؟ کدوم بخش T یا T بود؟! کجاها L میشد؟

پایان

در این نوشته چالش‌های مربوط به جمع‌ آوری pre-ride events رو بررسی کردیم. مهمترین این چالش‌ها، حجم زیاده داده و مشکلات مرتبط به آن و طراحی سیستمی بود که انعطاف پذیری‌های کافی رو داشته باشه. با بررسی تکنولوژی‌های موجود به این نتیجه رسیدیم که بهترین انتخاب برای ابزارها چی میتونه باشه و سپس سیستمی ارائه دادیم که نیازمندی‌های ما رو برطرف بکنه. این سیستم طی یک سال گذشته در تیم ما توسعه پیدا کرده(و همچنان در حال بهبود و پیشرفته!) و به ما کمک کرده شناخت بهتری از رفتار کاربران اسنپ داشته باشیم. اینطوری تصمیم‌های بهتری برای آینده و وضعیت فعلی محصولاتمون میگیریم.

شما هم میتونید با الگو گرفتن از این سیستم، پایپ لاین دیتای منحصر به تیم و شرکت خودتون رو طراحی کنید.

در ضمن، اگه حس می‌کنین از پروژه‌هایی شبیه به این خوشتون میاد و در نتیجه علاقه دارید به تیم ما ملحق بشید، خوشحال می‌شیم که رزومه‌هاتون رو از طریق آدرس engineering@snapp.cab یا از سایت فرصت های شغلی Careers - Jobs - Snapp برای ما ارسال کنید. موفق باشید

تشکر ویژه از اعضای تیم آدرس:‌ علی حیدرآبادی،‌ امیر سالاری، پارسا پردل،‌ سپهر رفیعی، روزبه ترابیان، محمد جعفری، راضیه مصیبی و علی رسولی :)