5 minutes
الانظمة الموزعة
The Graph هو بروتوكول يتم تنفيذه كنظام موزع.
فشل الاتصالات،ووصول الطلبات خارج الترتيب،وأجهزة الكمبيوتر المختلفة ذات الساعات غير المتزامنة ، والخوادم التي تعيد التشغيل ،وحدوث عمليات إعادة التنظيم بين الطلبات. هذه المشاكل متأصلة في جميع الأنظمة الموزعة ولكنها تتفاقم في الأنظمة التي تعمل على نطاق عالمي.
ضع في اعتبارك هذا المثال لما قد يحدث إذا قام العميل بـ polls للمفهرس للحصول على أحدث البيانات أثناء إعادة التنظيم.
- المفهرس يأخذ الكتلة 8
- تقديم الطلب للعميل لـ block 8
- المفهرس يأخذ block 9
- المفهرس يأخذ block 10A
- تقديم الطلب للعميل لـ block 10A
- يكتشف المفهرس عملية التنظيم لـ 10B ويسترجع 10A
- تقديم الطلب للعميل لـ block 9
- المفهرس يأخذ block 10B
- المفهرس يأخذ block 11
- تقديم الطلب للعميل لـ block 11
من وجهة نظر المفهرس ، تسير الأمور إلى الأمام بشكل منطقي. حيث أن الوقت يمضي قدما ، على الرغم من أننا اضطررنا إلى التراجع عن كتلة الـ uncle وتشغيل الكتلة وفقا للاتفاق ، ويقدم المفهرس الطلبات باستخدام أحدث حالة يعرفها في ذلك الوقت.
لكن من وجهة نظر العميل ، تبدو الأمور مشوشة. يلاحظ العميل أن الردود كانت للكتل 8 و 10 و 9 و 11 بهذا الترتيب. نسمي هذا مشكلة “تذبذب الكتلة”. عندما يواجه العميل تذبذبا في الكتلة ، فقد تظهر البيانات متناقضة مع نفسها بمرور الوقت. يزداد الموقف سوءا عندما نعتبر أن المفهرسين لا يستوعبون جميع الكتل الأخيرة في وقت واحد ، وقد يتم توجيه طلباتك إلى عدة مفهرسين.
تقع على عاتق العميل والخادم مسؤولية العمل معا لتوفير بيانات متسقة للمستخدم. يجب استخدام طرق مختلفة اعتمادا على الاتساق المطلوب حيث لا يوجد برنامج واحد مناسب لكل مشكلة.
الاستنتاج من خلال الآثار المترتبة على الأنظمة الموزعة أمر صعب ، لكن الإصلاح قد لا يكون كذلك! لقد أنشأنا APIs وأنماطا لمساعدتك على تصفح بعض حالات الاستخدام الشائعة. توضح الأمثلة التالية هذه الأنماط ولكنها لا تزال تتجاهل التفاصيل التي يتطلبها كود الإنتاج (مثل معالجة الأخطاء والإلغاء) حتى لا يتم تشويش الأفكار الرئيسية.
Polling للبيانات المحدثة
The Graph provides the block: { number_gte: $minBlock }
API, which ensures that the response is for a single block equal or higher to $minBlock
. If the request is made to a graph-node
instance and the min block is not yet synced, graph-node
will return an error. If graph-node
has synced min block, it will run the response for the latest block. If the request is made to an Edge & Node Gateway, the Gateway will filter out any Indexers that have not yet synced min block and make the request for the latest block the Indexer has synced.
We can use number_gte
to ensure that time never travels backward when polling for data in a loop. Here is an example:
1/// Updates the protocol.paused variable to the latest2/// known value in a loop by fetching it using The Graph.3async function updateProtocolPaused() {4 // It's ok to start with minBlock at 0. The query will be served5 // using the latest block available. Setting minBlock to 0 is the6 // same as leaving out that argument.7 let minBlock = 089 for (;;) {10 // Schedule a promise that will be ready once11 // the next Ethereum block will likely be available.12 const nextBlock = new Promise((f) => {13 setTimeout(f, 14000)14 })1516 const query = `17 query GetProtocol($minBlock: Int!) {18 protocol(block: { number_gte: $minBlock } id: "0") {19 paused20 }21 _meta {22 block {23 number24 }25 }26 }`2728 const variables = { minBlock }29 const response = await graphql(query, variables)30 minBlock = response._meta.block.number3132 // TODO: Do something with the response data here instead of logging it.33 console.log(response.protocol.paused)3435 // Sleep to wait for the next block36 await nextBlock37 }38}
جلب مجموعة من العناصر المرتبطة
حالة أخرى هي جلب مجموعة كبيرة أو بشكل عام جلب العناصر المرتبطة عبر طلبات متعددة. على عكس حالة الـ polling (حيث كان التناسق المطلوب هو المضي قدما في الزمن) ، فإن التناسق المطلوب هو لنقطة واحدة في الزمن.
Here we will use the block: { hash: $blockHash }
argument to pin all of our results to the same block.
1/// Gets a list of domain names from a single block using pagination2async function getDomainNames() {3 // Set a cap on the maximum number of items to pull.4 let pages = 55 const perPage = 100067 // The first query will get the first page of results and also get the block8 // hash so that the remainder of the queries are consistent with the first.9 const listDomainsQuery = `10 query ListDomains($perPage: Int!) {11 domains(first: $perPage) {12 name13 id14 }15 _meta {16 block {17 hash18 }19 }20 }`2122 let data = await graphql(listDomainsQuery, { perPage })23 let result = data.domains.map((d) => d.name)24 let blockHash = data._meta.block.hash2526 let query27 // Continue fetching additional pages until either we run into the limit of28 // 5 pages total (specified above) or we know we have reached the last page29 // because the page has fewer entities than a full page.30 while (data.domains.length == perPage && --pages) {31 let lastID = data.domains[data.domains.length - 1].id32 query = `33 query ListDomains($perPage: Int!, $lastID: ID!, $blockHash: Bytes!) {34 domains(first: $perPage, where: { id_gt: $lastID }, block: { hash: $blockHash }) {35 name36 id37 }38 }`3940 data = await graphql(query, { perPage, lastID, blockHash })4142 // Accumulate domain names into the result43 for (domain of data.domains) {44 result.push(domain.name)45 }46 }47 return result48}
لاحظ أنه في حالة إعادة التنظيم ، سيحتاج العميل إلى إعادة المحاولة من الطلب الأول لتحديث hash الكتلة إلى كتلة non-uncle.