تأخیر ۶ ثانیه‌ای در Read Replicaهای D1

یک Read Replica از D1 در توکیو، ۶.۱ ثانیه نسبت به یک عملیات نوشتن (write) در آمریکای شمالی عقب افتاد.

من این موضوع را از طریق یک ردیاب (tracker) که باعث محدود شدن (throttling) نمایش‌های اشتباه شده بود، متوجه شدم. مستندات به eventual consistency اشاره می‌کنند، اما زمان مشخصی برای برنامه‌ریزی به شما نمی‌دهند.

من یک پروب (probe) برای سنجش میزان کهنگی (staleness) ساختم تا اعداد واقعی را پیدا کنم. این پروب، ردیفی را با یک UUID و یک epoch می‌نویسد. سپس تا زمانی که آن ردیف در replica ظاهر شود، آن را پایش (poll) می‌کند و در نهایت میزان تأخیر را ثبت می‌کند.

نتایج حاصل از ۲۰۰ پروب در آسیا:

اگر دیتابیس اصلی (primary) شما در آمریکای شمالی و کاربران شما در آسیا باشند، این تأخیر بسیار زیاد خواهد بود.

من همچنین با یک خطای schema مواجه شدم. یک migration روی دیتابیس اصلی اجرا شد. یک Worker ری‌استارت شد. اولین درخواست‌ها قبل از اینکه جدول جدید به replica برسد، به آن برخورد کردند. خطا اعلام می‌کرد که جدول وجود ندارد؛ در حالی که جدول وجود داشت، اما replica عقب مانده بود.

من این مشکل را با مسیریابی برای دور زدن تأخیر (routing around the lag) حل کردم. من با آن نمی‌جنگم.

طراحی من به این صورت است:

  1. نویسنده (writer) یک written_at epoch به ردیف اضافه می‌کند.
  2. نویسنده یک هدر X-D1-Written-At به پاسخ اضافه می‌کند.
  3. خواننده (reader) آن هدر را با داده‌های دریافتی از replica مقایسه می‌کند.
  4. اگر داده‌های replica قدیمی‌تر از هدر باشند، خواننده به سراغ KV می‌رود (fallback).

KV در همان منطقه (region) زیر ۵۰۰ میلی‌ثانیه اجرا می‌شود. این سرویس تا ۱۰ میلیون خواندن در روز رایگان است. این روش، راهی ارزان برای دریافت داده‌های تازه برای پرچم‌های (flags) حیاتی فراهم می‌کند.

شما فقط در بازه زمانی کوتاهی که replica عقب مانده است، از KV استفاده می‌کنید. پس از اینکه replica همگام شد، اکثر خواندن‌ها به طور عادی به D1 متصل می‌شوند.

من اسکریپت کامل و الگوی migration را در پست مفصل خود به اشتراک گذاشته‌ام.

منبع: https://dev.to/riversea/d1-read-replicas-returned-stale-data-for-6-seconds-heres-what-i-measured-and-how-i-designed-mme