در قسمت قبلی، با API های CRUD اسناد کار کردیم و دیدیم وقتی که هر یک از عملیات CRUD را در ایندکس انجام میدهیم، پاسخی مشابه ساختار زیر دریافت میکنیم:
{
"_index" : "tourism_places",
"_type" : "_doc",
"_id" : "6sovInwBQBEK3TDwiGJz",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 15,
"_primary_term" : 1
}
که کلید result بیانگر نوع عمل انجام شده است و کلید shards_ اطلاعاتی از تعداد shard های درگیر شده طی عملیات مورد نظر در اختیار ما میگذارد.
در این بخش در مورد کلیدهای seq_no ،_version_ و primary_term_ صحبت خواهیم کرد.
در قسمتهای قبلی گفتیم که معماری elasticsearch به شکل distrusted یا توزیعپذیر طراحی شده است، به این معنی که خوشهی elasticsearch میتواند شامل چندین گره باشد و یک ایندکس shard های خود را در چندین گرهی مختلف کپی کند تا نسخهی کپی از دادهها داشته باشد و در صورت ازدسترس خارج شدن یک گره، دادههای ایندکس از replica shard های آن قابل دسترسی باشد. نکتهی اصلی که میخواهیم به آن بپردازیم این است که دادهها باید در shard های primary و replica با یکدیگر همگامسازی (sync) شوند تا نتایج جستجو در یک ایندکس همواره یکسان باشد.
Elasticsearch برای حفظ این یکپارچگی، هنگامی که هر یک از عملیاتهای update ،index یا delete برای یک سند درخواست شود، ابتدا نتیجهی آن عملیات را در primary shard اعمال میکند، سپس در صورتی که ایندکس از replica shard استفاده کند نتیجهی اعمال شده را در replica shard ها به صورت موازی منعکس میکند. بنابراین مشخص نیست این درخواستهای موازی به چه ترتیب در سایر shard ها اعمال شود. در این شرایط Elasticsearch باید تضمین کند که هیچگاه نسخهی قدیمیتر جایگزین نسخهی جدیدتر یک سند نشود. اما چگونه؟ به تصویر زیر دقت کنید:
فرایند بالا را در اصطلاح مسالهی concurrency یا همروندی و خطای ناشی از آن، خطای version conflict نامیده میشود. Elasticsearch برای کنترل این مساله چند فیلد اطلاعاتی برای هر document در نظر میگیرد تا با مقایسه آنها از جایگزینی نسخهی قدیمیتر با نسخهای جدیدتر جلوگیری کند.
با هربار اجرای عملیات بر روی یک سند، مقداری برای فیلد seq_no_ توسط primary shard که تغییرات را اعمال کرده است، تعیین میشود. مقدار seq_no_ با اجرای هر عملیات افزایش مییابد و میتوان مطمئن بود که همواره تغییرات جدیدتر، مقدار seq_no_ بزرگتری دارند. همچنین مقدار primary_term_ برابر شناسهی shard اعمال کنندهی تغییرات است.
بنابراین Elasticsearch با مقایسهی مقدار seq_no_ یک سند، میتواند تشخیص دهد آیا سند مورد نظر طی دورهی همگامسازی دادهها، توسط API دیگری مورد تغییر قرار گرفته است یا خیر.
مقادیر seq_no_ و primary_term_ با همدیگر یک نسخهی یکتا را مشخص میکنند و در پاسخی که get API برمیگرداند همواره این فیلدها وجود دارد. پس این امکان برای ما نیز وجود دارد تا در زمان استفاده از API های update ،index یا delete با استفاده از پارامترهای if_seq_no و if_primary_term به صورت query string در URL مطمئن شویم که عملیات مورد نظر حتما در نسخهای از سند اعمال میشود که ما آن را دریافت کردیم. برای مثال فرض کنید سند با شناسهی 2 را get کرده باشیم و مقادیر seq_no_ و primary_term_ به ترتیب 134 و 1 باشند. حال به index API زیر دقت کنید:
PUT sample_index/_doc/2?if_seq_no=134&if_primary_term=1
{
"title": "sample",
"tags": [ "my_tag" ]
}
با این کار اگر از زمان دریافت این سند تا لحظهی فراخوانی API بالا، تغییری در این سند اعمال شده باشد، مقادیر seq_no_ و primary_term_ تغییر کرده و عملیات index با خطای version conflict مواجه میشود.
علاوه بر این قابلیت، در API های بروزرسانی گروهی (update_by_query) و حذف گروهی (delete_by_query) که در قسمت قبلی با آنها آشنا شدیم، پارامتری در query string وجود دارد به نام conflicts که تعیین میکند اگر در مدت زمان جستجوی دادهها تا لحظهی بروزرسانی یا حذف آنها مسالهی version conflict اتفاق افتاد، API چه رفتاری را دنبال کند. به عنوان مثال به API زیر دقت کنید:
POST sample_index/_update_by_query?conflicts=proceed
{
"doc": { … }
}
دو مقدار برای پارامتر conflicts قابل قبول است:
abort: مقدار پیشفرض است و مانع از اعمال تغییرات در اسنادی که خطای version conflict دارند خواهد شد. لیستی از مواردی که با این خطا مواجه شوند در پاسخ API نمایش داده خواهد شد و مابقی اسنادی که مشکل ندارند، بروزرسانی یا حذف خواهند شد.
proceed: در صورتی که این مقدار برای پارامتر تعیین شود، در صورت بروز خطای version conflict، عملیات ادامه مییابد و در هر صورت تغییراتِ این API را در سند مورد نظر اعمال میکند.