শার্ডেড টাইমস্ট্যাম্প

যদি কোনো কালেকশনে ক্রমিক ইনডেক্সযুক্ত ভ্যালু সহ ডকুমেন্ট থাকে, তাহলে Cloud Firestore প্রতি সেকেন্ডে রাইট রেট ৫০০ বার রাইট করার মধ্যে সীমাবদ্ধ করে দেয়। এই পৃষ্ঠায় বর্ণনা করা হয়েছে কীভাবে এই সীমাবদ্ধতা অতিক্রম করার জন্য একটি ডকুমেন্ট ফিল্ডকে শার্ড করতে হয়। প্রথমে, "ক্রমিক ইনডেক্সযুক্ত ফিল্ড" বলতে আমরা কী বুঝি তা সংজ্ঞায়িত করা যাক এবং এই সীমাবদ্ধতা কখন প্রযোজ্য হয় তা স্পষ্ট করা যাক।

ক্রমিক সূচীকৃত ক্ষেত্র

"সিকোয়েনশিয়াল ইনডেক্সড ফিল্ড" বলতে এমন যেকোনো ডকুমেন্টের সংগ্রহকে বোঝায়, যাতে একটি ক্রমান্বয়ে ক্রমবর্ধমান বা ক্রমান্বয়ে হ্রাসমান ইনডেক্সড ফিল্ড থাকে। অনেক ক্ষেত্রে, এটি একটি timestamp ফিল্ডকে বোঝায়, কিন্তু যেকোনো ক্রমান্বয়ে ক্রমবর্ধমান বা হ্রাসমান ফিল্ড ভ্যালু প্রতি সেকেন্ডে ৫০০ বার লেখার সীমাটি সক্রিয় করতে পারে।

উদাহরণস্বরূপ, userid নামক ইনডেক্সড ফিল্ডযুক্ত user ডকুমেন্টের একটি সংগ্রহের ক্ষেত্রে এই সীমাবদ্ধতাটি প্রযোজ্য হবে, যদি অ্যাপটি userid মানগুলো নিম্নোক্তভাবে নির্ধারণ করে:

  • 1281, 1282, 1283, 1284, 1285, ...

অন্যদিকে, সব timestamp ফিল্ড এই সীমাটি কার্যকর করে না। যদি কোনো timestamp ফিল্ড এলোমেলোভাবে বণ্টিত মান ট্র্যাক করে, তবে লেখার সীমাটি প্রযোজ্য হয় না। ফিল্ডটির প্রকৃত মানও কোনো বিষয় নয়, কেবল এটি একমুখীভাবে ক্রমবর্ধমান বা হ্রাসমান কিনা সেটাই বিবেচ্য। উদাহরণস্বরূপ, একমুখীভাবে ক্রমবর্ধমান ফিল্ড মানের নিম্নলিখিত উভয় সেটই লেখার সীমাটি কার্যকর করে:

  • 100000, 100001, 100002, 100003, ...
  • 0, 1, 2, 3, ...

একটি টাইমস্ট্যাম্প ফিল্ড শার্ডিং করা

ধরে নিন আপনার অ্যাপ একটি ক্রমান্বয়ে ক্রমবর্ধমান timestamp ফিল্ড ব্যবহার করে। যদি আপনার অ্যাপ কোনো কোয়েরিতে timestamp ফিল্ড ব্যবহার না করে, তাহলে আপনি টাইমস্ট্যাম্প ফিল্ডটিকে ইনডেক্স না করার মাধ্যমে প্রতি সেকেন্ডে ৫০০টি রাইটের সীমাটি দূর করতে পারেন। যদি আপনার কোয়েরিগুলোর জন্য timestamp ফিল্ডের প্রয়োজন হয়, তাহলে আপনি শার্ডেড টাইমস্ট্যাম্প ব্যবহার করে এই সীমাটি এড়িয়ে যেতে পারেন।

  1. timestamp ফিল্ডের পাশাপাশি একটি shard ফিল্ড যোগ করুন। shard ফিল্ডের জন্য 1..n স্বতন্ত্র মান ব্যবহার করুন। এর ফলে কালেকশনটির রাইট লিমিট বেড়ে 500*n হবে, কিন্তু আপনাকে অবশ্যই n সংখ্যক কোয়েরি অ্যাগ্রিগেট করতে হবে।
  2. প্রতিটি ডকুমেন্টে এলোমেলোভাবে একটি shard ভ্যালু নির্ধারণ করার জন্য আপনার রাইট লজিক আপডেট করুন।
  3. শার্ড করা ফলাফল সেটগুলোকে একত্রিত করতে আপনার কোয়েরিগুলো আপডেট করুন।
  4. shard ফিল্ড এবং timestamp ফিল্ড উভয়ের জন্য একক-ফিল্ড ইনডেক্স নিষ্ক্রিয় করুন। timestamp ফিল্ড ধারণকারী বিদ্যমান কম্পোজিট ইনডেক্সগুলো মুছে ফেলুন।
  5. আপনার আপডেট করা কোয়েরিগুলোকে সমর্থন করার জন্য নতুন কম্পোজিট ইনডেক্স তৈরি করুন। একটি ইনডেক্সে ফিল্ডগুলোর ক্রম গুরুত্বপূর্ণ, এবং shard ফিল্ডটি অবশ্যই timestamp ফিল্ডের আগে আসতে হবে। যে কোনো ইনডেক্সে timestamp ফিল্ড অন্তর্ভুক্ত থাকলে, তাতে অবশ্যই shard ফিল্ডও অন্তর্ভুক্ত থাকতে হবে।

আপনার কেবলমাত্র সেইসব ক্ষেত্রে শার্ডেড টাইমস্ট্যাম্প প্রয়োগ করা উচিত যেখানে প্রতি সেকেন্ডে ৫০০-এর বেশি রাইট রেট থাকে। অন্যথায়, এটি একটি অপরিপক্ক অপ্টিমাইজেশন। একটি timestamp ফিল্ডকে শার্ডিং করলে প্রতি সেকেন্ডে ৫০০ রাইটের সীমাবদ্ধতা দূর হয়, কিন্তু এর বিনিময়ে ক্লায়েন্ট-সাইড কোয়েরি অ্যাগ্রিগেশনের প্রয়োজন হয়।

নিম্নলিখিত উদাহরণগুলিতে দেখানো হয়েছে কিভাবে একটি timestamp ফিল্ডকে শার্ড করতে হয় এবং কিভাবে একটি শার্ড করা ফলাফল সেট কোয়েরি করতে হয়।

উদাহরণ ডেটা মডেল এবং কোয়েরি

উদাহরণস্বরূপ, মুদ্রা, সাধারণ স্টক এবং ইটিএফ-এর মতো আর্থিক উপকরণগুলির প্রায় রিয়েল-টাইম বিশ্লেষণের জন্য একটি অ্যাপের কথা ভাবুন। এই অ্যাপটি একটি instruments কালেকশনে এইভাবে ডকুমেন্ট লেখে:

নোড.জেএস
async function insertData() {
  const instruments = [
    {
      symbol: 'AAA',
      price: {
        currency: 'USD',
        micros: 34790000
      },
      exchange: 'EXCHG1',
      instrumentType: 'commonstock',
      timestamp: Timestamp.fromMillis(
          Date.parse('2019-01-01T13:45:23.010Z'))
    },
    {
      symbol: 'BBB',
      price: {
        currency: 'JPY',
        micros: 64272000000
      },
      exchange: 'EXCHG2',
      instrumentType: 'commonstock',
      timestamp: Timestamp.fromMillis(
          Date.parse('2019-01-01T13:45:23.101Z'))
    },
    {
      symbol: 'Index1 ETF',
      price: {
        currency: 'USD',
        micros: 473000000
      },
      exchange: 'EXCHG1',
      instrumentType: 'etf',
      timestamp: Timestamp.fromMillis(
          Date.parse('2019-01-01T13:45:23.001Z'))
    }
  ];

  const batch = fs.batch();
  for (const inst of instruments) {
    const ref = fs.collection('instruments').doc();
    batch.set(ref, inst);
  }

  await batch.commit();
}

এই অ্যাপটি নিম্নলিখিত কোয়েরিগুলো চালায় এবং timestamp ফিল্ড অনুসারে সাজায়:

নোড.জেএস
function createQuery(fieldName, fieldOperator, fieldValue, limit = 5) {
  return fs.collection('instruments')
      .where(fieldName, fieldOperator, fieldValue)
      .orderBy('timestamp', 'desc')
      .limit(limit)
      .get();
}

function queryCommonStock() {
  return createQuery('instrumentType', '==', 'commonstock');
}

function queryExchange1Instruments() {
  return createQuery('exchange', '==', 'EXCHG1');
}

function queryUSDInstruments() {
  return createQuery('price.currency', '==', 'USD');
}
insertData()
    .then(() => {
      const commonStock = queryCommonStock()
          .then(
              (docs) => {
                console.log('--- queryCommonStock: ');
                docs.forEach((doc) => {
                  console.log(`doc = ${util.inspect(doc.data(), {depth: 4})}`);
                });
              }
          );
      const exchange1Instruments = queryExchange1Instruments()
          .then(
              (docs) => {
                console.log('--- queryExchange1Instruments: ');
                docs.forEach((doc) => {
                  console.log(`doc = ${util.inspect(doc.data(), {depth: 4})}`);
                });
              }
          );
      const usdInstruments = queryUSDInstruments()
          .then(
              (docs) => {
                console.log('--- queryUSDInstruments: ');
                docs.forEach((doc) => {
                  console.log(`doc = ${util.inspect(doc.data(), {depth: 4})}`);
                });
              }
          );
      return Promise.all([commonStock, exchange1Instruments, usdInstruments]);
    });

কিছু গবেষণার পর, আপনি নির্ধারণ করেছেন যে অ্যাপটি প্রতি সেকেন্ডে ১,০০০ থেকে ১,৫০০টি ইন্সট্রুমেন্ট আপডেট পাবে। এটি ইনডেক্সড টাইমস্ট্যাম্প ফিল্ডযুক্ত ডকুমেন্ট থাকা কালেকশনগুলোর জন্য অনুমোদিত প্রতি সেকেন্ডে ৫০০টি রাইটের সীমা অতিক্রম করে। রাইট থ্রুপুট বাড়ানোর জন্য, আপনার ৩টি শার্ড ভ্যালু প্রয়োজন, MAX_INSTRUMENT_UPDATES/500 = 3 এই উদাহরণে x , y , এবং z শার্ড ভ্যালুগুলো ব্যবহার করা হয়েছে। আপনি আপনার শার্ড ভ্যালু হিসেবে সংখ্যা বা অন্যান্য অক্ষরও ব্যবহার করতে পারেন।

একটি শার্ড ফিল্ড যোগ করা

আপনার ডকুমেন্টগুলিতে একটি shard ফিল্ড যোগ করুন। shard ফিল্ডটির মান x , y , বা z এ সেট করুন, যা কালেকশনটির রাইট লিমিট প্রতি সেকেন্ডে ১,৫০০ রাইট পর্যন্ত বাড়িয়ে দেয়।

নোড.জেএস
// Define our 'K' shard values
const shards = ['x', 'y', 'z'];
// Define a function to help 'chunk' our shards for use in queries.
// When using the 'in' query filter there is a max number of values that can be
// included in the value. If our number of shards is higher than that limit
// break down the shards into the fewest possible number of chunks.
function shardChunks() {
  const chunks = [];
  let start = 0;
  while (start < shards.length) {
    const elements = Math.min(MAX_IN_VALUES, shards.length - start);
    const end = start + elements;
    chunks.push(shards.slice(start, end));
    start = end;
  }
  return chunks;
}

// Add a convenience function to select a random shard
function randomShard() {
  return shards[Math.floor(Math.random() * Math.floor(shards.length))];
}
async function insertData() {
  const instruments = [
    {
      shard: randomShard(),  // add the new shard field to the document
      symbol: 'AAA',
      price: {
        currency: 'USD',
        micros: 34790000
      },
      exchange: 'EXCHG1',
      instrumentType: 'commonstock',
      timestamp: Timestamp.fromMillis(
          Date.parse('2019-01-01T13:45:23.010Z'))
    },
    {
      shard: randomShard(),  // add the new shard field to the document
      symbol: 'BBB',
      price: {
        currency: 'JPY',
        micros: 64272000000
      },
      exchange: 'EXCHG2',
      instrumentType: 'commonstock',
      timestamp: Timestamp.fromMillis(
          Date.parse('2019-01-01T13:45:23.101Z'))
    },
    {
      shard: randomShard(),  // add the new shard field to the document
      symbol: 'Index1 ETF',
      price: {
        currency: 'USD',
        micros: 473000000
      },
      exchange: 'EXCHG1',
      instrumentType: 'etf',
      timestamp: Timestamp.fromMillis(
          Date.parse('2019-01-01T13:45:23.001Z'))
    }
  ];

  const batch = fs.batch();
  for (const inst of instruments) {
    const ref = fs.collection('instruments').doc();
    batch.set(ref, inst);
  }

  await batch.commit();
}

শার্ডেড টাইমস্ট্যাম্প কোয়েরি করা হচ্ছে

shard ফিল্ড যোগ করার জন্য আপনাকে আপনার কোয়েরিগুলো আপডেট করতে হবে, যাতে শার্ড করা ফলাফলগুলো অ্যাগ্রিগেট করা যায়:

নোড.জেএস
function createQuery(fieldName, fieldOperator, fieldValue, limit = 5) {
  // For each shard value, map it to a new query which adds an additional
  // where clause specifying the shard value.
  return Promise.all(shardChunks().map(shardChunk => {
        return fs.collection('instruments')
            .where('shard', 'in', shardChunk)  // new shard condition
            .where(fieldName, fieldOperator, fieldValue)
            .orderBy('timestamp', 'desc')
            .limit(limit)
            .get();
      }))
      // Now that we have a promise of multiple possible query results, we need
      // to merge the results from all of the queries into a single result set.
      .then((snapshots) => {
        // Create a new container for 'all' results
        const docs = [];
        snapshots.forEach((querySnapshot) => {
          querySnapshot.forEach((doc) => {
            // append each document to the new all container
            docs.push(doc);
          });
        });
        if (snapshots.length === 1) {
          // if only a single query was returned skip manual sorting as it is
          // taken care of by the backend.
          return docs;
        } else {
          // When multiple query results are returned we need to sort the
          // results after they have been concatenated.
          // 
          // since we're wanting the `limit` newest values, sort the array
          // descending and take the first `limit` values. By returning negated
          // values we can easily get a descending value.
          docs.sort((a, b) => {
            const aT = a.data().timestamp;
            const bT = b.data().timestamp;
            const secondsDiff = aT.seconds - bT.seconds;
            if (secondsDiff === 0) {
              return -(aT.nanoseconds - bT.nanoseconds);
            } else {
              return -secondsDiff;
            }
          });
          return docs.slice(0, limit);
        }
      });
}

function queryCommonStock() {
  return createQuery('instrumentType', '==', 'commonstock');
}

function queryExchange1Instruments() {
  return createQuery('exchange', '==', 'EXCHG1');
}

function queryUSDInstruments() {
  return createQuery('price.currency', '==', 'USD');
}
insertData()
    .then(() => {
      const commonStock = queryCommonStock()
          .then(
              (docs) => {
                console.log('--- queryCommonStock: ');
                docs.forEach((doc) => {
                  console.log(`doc = ${util.inspect(doc.data(), {depth: 4})}`);
                });
              }
          );
      const exchange1Instruments = queryExchange1Instruments()
          .then(
              (docs) => {
                console.log('--- queryExchange1Instruments: ');
                docs.forEach((doc) => {
                  console.log(`doc = ${util.inspect(doc.data(), {depth: 4})}`);
                });
              }
          );
      const usdInstruments = queryUSDInstruments()
          .then(
              (docs) => {
                console.log('--- queryUSDInstruments: ');
                docs.forEach((doc) => {
                  console.log(`doc = ${util.inspect(doc.data(), {depth: 4})}`);
                });
              }
          );
      return Promise.all([commonStock, exchange1Instruments, usdInstruments]);
    });

সূচক সংজ্ঞা আপডেট করুন

প্রতি সেকেন্ডে ৫০০টি রাইটের সীমাবদ্ধতা অপসারণ করতে, timestamp ফিল্ড ব্যবহারকারী বিদ্যমান সিঙ্গেল-ফিল্ড এবং কম্পোজিট ইনডেক্সগুলো ডিলিট করে দিন।

যৌগিক সূচক সংজ্ঞাগুলি মুছুন

ফায়ারবেস কনসোল

  1. Firebase কনসোলে Cloud Firestore Composite Indexes পৃষ্ঠাটি খুলুন।

    যৌগিক সূচকে যান

  2. যেসব ইনডেক্সে timestamp ফিল্ড রয়েছে, সেগুলোর প্রতিটির জন্য বোতামে ক্লিক করুন এবং Delete-এ ক্লিক করুন।

জিসিপি কনসোল

  1. গুগল ক্লাউড কনসোলে, ডেটাবেস পৃষ্ঠায় যান।

    ডাটাবেসে যান

  2. ডাটাবেসের তালিকা থেকে প্রয়োজনীয় ডাটাবেসটি নির্বাচন করুন।

  3. ন্যাভিগেশন মেনুতে, Indexes-এ ক্লিক করুন এবং তারপরে Composite ট্যাবে ক্লিক করুন।

  4. যেসব ইনডেক্স ডেফিনিশনে timestamp ফিল্ড রয়েছে, সেগুলো খুঁজতে ফিল্টার ফিল্ডটি ব্যবহার করুন।

  5. এই প্রতিটি ইনডেক্সের জন্য, বোতামে ক্লিক করুন এবং Delete-এ ক্লিক করুন।

ফায়ারবেস সিএলআই

  1. যদি আপনি Firebase CLI সেট আপ না করে থাকেন, তাহলে CLI ইনস্টল করতে এবং firebase init কমান্ডটি চালাতে এই নির্দেশাবলী অনুসরণ করুনinit কমান্ড চলাকালীন, Firestore: Deploy rules and create indexes for Firestore নির্বাচন করতে ভুলবেন না।
  2. সেটআপের সময়, Firebase CLI আপনার বিদ্যমান ইনডেক্স ডেফিনিশনগুলোকে ডিফল্টরূপে firestore.indexes.json নামের একটি ফাইলে ডাউনলোড করে।
  3. যে কোনো ইনডেক্স ডেফিনিশন মুছে ফেলুন যাতে timestamp ফিল্ড রয়েছে, উদাহরণস্বরূপ:

    {
    "indexes": [
      // Delete composite index definition that contain the timestamp field
      {
        "collectionGroup": "instruments",
        "queryScope": "COLLECTION",
        "fields": [
          {
            "fieldPath": "exchange",
            "order": "ASCENDING"
          },
          {
            "fieldPath": "timestamp",
            "order": "DESCENDING"
          }
        ]
      },
      {
        "collectionGroup": "instruments",
        "queryScope": "COLLECTION",
        "fields": [
          {
            "fieldPath": "instrumentType",
            "order": "ASCENDING"
          },
          {
            "fieldPath": "timestamp",
            "order": "DESCENDING"
          }
        ]
      },
      {
        "collectionGroup": "instruments",
        "queryScope": "COLLECTION",
        "fields": [
          {
            "fieldPath": "price.currency",
            "order": "ASCENDING"
          },
          {
            "fieldPath": "timestamp",
            "order": "DESCENDING"
          }
        ]
      },
     ]
    }
    
  4. আপনার হালনাগাদ করা ইনডেক্স সংজ্ঞাগুলো স্থাপন করুন:

    firebase deploy --only firestore:indexes
    

একক-ক্ষেত্র সূচক সংজ্ঞা আপডেট করুন

ফায়ারবেস কনসোল

  1. Firebase কনসোলে Cloud Firestore Single Field Indexes পৃষ্ঠাটি খুলুন।

    একক ফিল্ড সূচীতে যান

  2. ছাড় যোগ করুন- এ ক্লিক করুন।

  3. কালেকশন আইডি-র জন্য, instruments লিখুন। ফিল্ড পাথ-এর জন্য, timestamp লিখুন।

  4. কোয়েরি স্কোপের অধীনে, কালেকশন এবং কালেকশন গ্রুপ উভয়ই নির্বাচন করুন।

  5. পরবর্তী ক্লিক করুন

  6. ইনডেক্সের সমস্ত সেটিংস নিষ্ক্রিয় করুন। সংরক্ষণ করুন- এ ক্লিক করুন।

  7. shard ফিল্ডের জন্যও একই ধাপগুলো পুনরাবৃত্তি করুন।

জিসিপি কনসোল

  1. গুগল ক্লাউড কনসোলে, ডেটাবেস পৃষ্ঠায় যান।

    ডাটাবেসে যান

  2. ডাটাবেসের তালিকা থেকে প্রয়োজনীয় ডাটাবেসটি নির্বাচন করুন।

  3. ন্যাভিগেশন মেনুতে, Indexes-এ ক্লিক করুন এবং তারপরে Single Field ট্যাবে ক্লিক করুন।

  4. সিঙ্গেল ফিল্ড ট্যাবে ক্লিক করুন।

  5. ছাড় যোগ করুন- এ ক্লিক করুন।

  6. কালেকশন আইডি-র জন্য, instruments লিখুন। ফিল্ড পাথ-এর জন্য, timestamp লিখুন।

  7. কোয়েরি স্কোপের অধীনে, কালেকশন এবং কালেকশন গ্রুপ উভয়ই নির্বাচন করুন।

  8. পরবর্তী ক্লিক করুন

  9. ইনডেক্সের সমস্ত সেটিংস নিষ্ক্রিয় করুন। সংরক্ষণ করুন- এ ক্লিক করুন।

  10. shard ফিল্ডের জন্যও একই ধাপগুলো পুনরাবৃত্তি করুন।

ফায়ারবেস সিএলআই

  1. আপনার ইনডেক্স ডেফিনিশন ফাইলের fieldOverrides সেকশনে নিম্নলিখিতটি যোগ করুন:

    {
     "fieldOverrides": [
       // Disable single-field indexing for the timestamp field
       {
         "collectionGroup": "instruments",
         "fieldPath": "timestamp",
         "indexes": []
       },
     ]
    }
    
  2. আপনার হালনাগাদ করা ইনডেক্স সংজ্ঞাগুলো স্থাপন করুন:

    firebase deploy --only firestore:indexes
    

নতুন যৌগিক সূচক তৈরি করুন

timestamp ধারণকারী পূর্ববর্তী সমস্ত ইনডেক্স মুছে ফেলার পর, আপনার অ্যাপের প্রয়োজনীয় নতুন ইনডেক্সগুলো সংজ্ঞায়িত করুন। timestamp ফিল্ড ধারণকারী যেকোনো ইনডেক্সে অবশ্যই shard ফিল্ডও থাকতে হবে। উদাহরণস্বরূপ, উপরের কোয়েরিগুলো সমর্থন করার জন্য, নিম্নলিখিত ইনডেক্সগুলো যোগ করুন:

সংগ্রহ সূচীবদ্ধ ক্ষেত্র কোয়েরি স্কোপ
যন্ত্র shard, price.currency, timestamp সংগ্রহ
যন্ত্র shard, exchange, timestamp সংগ্রহ
যন্ত্র shard, instrumentType, timestamp সংগ্রহ

ত্রুটির বার্তা

আপডেট করা কোয়েরিগুলো চালিয়ে আপনি এই ইনডেক্সগুলো তৈরি করতে পারেন।

প্রতিটি কোয়েরি একটি এরর মেসেজ দেয়, সাথে ফায়ারবেস কনসোলে প্রয়োজনীয় ইনডেক্স তৈরি করার জন্য একটি লিঙ্ক থাকে।

ফায়ারবেস সিএলআই

  1. আপনার ইনডেক্স ডেফিনিশন ফাইলে নিম্নলিখিত ইনডেক্সগুলো যোগ করুন:

     {
       "indexes": [
       // New indexes for sharded timestamps
         {
           "collectionGroup": "instruments",
           "queryScope": "COLLECTION",
           "fields": [
             {
               "fieldPath": "shard",
               "order": "DESCENDING"
             },
             {
               "fieldPath": "exchange",
               "order": "ASCENDING"
             },
             {
               "fieldPath": "timestamp",
               "order": "DESCENDING"
             }
           ]
         },
         {
           "collectionGroup": "instruments",
           "queryScope": "COLLECTION",
           "fields": [
             {
               "fieldPath": "shard",
               "order": "DESCENDING"
             },
             {
               "fieldPath": "instrumentType",
               "order": "ASCENDING"
             },
             {
               "fieldPath": "timestamp",
               "order": "DESCENDING"
             }
           ]
         },
         {
           "collectionGroup": "instruments",
           "queryScope": "COLLECTION",
           "fields": [
             {
               "fieldPath": "shard",
               "order": "DESCENDING"
             },
             {
               "fieldPath": "price.currency",
               "order": "ASCENDING"
             },
             {
               "fieldPath": "timestamp",
               "order": "DESCENDING"
             }
           ]
         },
       ]
     }
    
  2. আপনার হালনাগাদ করা ইনডেক্স সংজ্ঞাগুলো স্থাপন করুন:

    firebase deploy --only firestore:indexes
    

ক্রমিক সূচীকৃত ক্ষেত্রগুলির জন্য লেখার সীমা বোঝা

সিকোয়েনশিয়াল ইনডেক্সড ফিল্ডের রাইট রেটের সীমাবদ্ধতাটি আসে Cloud Firestore যেভাবে ইনডেক্স ভ্যালু সংরক্ষণ করে এবং ইনডেক্স রাইট স্কেল করে, তার থেকে। প্রতিটি ইনডেক্স রাইটের জন্য, Cloud Firestore একটি কী-ভ্যালু এন্ট্রি সংজ্ঞায়িত করে, যা ডকুমেন্টের নাম এবং প্রতিটি ইনডেক্সড ফিল্ডের ভ্যালুকে সংযুক্ত করে। Cloud Firestore এই ইনডেক্স এন্ট্রিগুলোকে ট্যাবলেট নামক ডেটার গ্রুপে সংগঠিত করে। প্রতিটি Cloud Firestore সার্ভারে এক বা একাধিক ট্যাবলেট থাকে। যখন কোনো নির্দিষ্ট ট্যাবলেটের রাইট লোড খুব বেশি হয়ে যায়, তখন Cloud Firestore ট্যাবলেটটিকে ছোট ছোট ট্যাবলেটে বিভক্ত করে এবং নতুন ট্যাবলেটগুলোকে বিভিন্ন Cloud Firestore সার্ভারে ছড়িয়ে দিয়ে হরাইজন্টালি স্কেল করে।

Cloud Firestore আভিধানিকভাবে কাছাকাছি ইনডেক্স এন্ট্রিগুলোকে একই ট্যাবলেটে রাখে। যদি একটি ট্যাবলেটের ইনডেক্স ভ্যালুগুলো খুব কাছাকাছি থাকে, যেমন টাইমস্ট্যাম্প ফিল্ডের ক্ষেত্রে, Cloud Firestore দক্ষতার সাথে ট্যাবলেটটিকে ছোট ছোট ট্যাবলেটে ভাগ করতে পারে না। এর ফলে একটি হট স্পট তৈরি হয়, যেখানে একটিমাত্র ট্যাবলেটে অতিরিক্ত ট্র্যাফিক জমা হয় এবং সেই হট স্পটে রিড ও রাইট অপারেশনগুলো ধীর হয়ে যায়।

একটি টাইমস্ট্যাম্প ফিল্ডকে শার্ডিং করার মাধ্যমে, Cloud Firestore একাধিক ট্যাবলেটের মধ্যে দক্ষতার সাথে ওয়ার্কলোড ভাগ করে নিতে পারে। যদিও টাইমস্ট্যাম্প ফিল্ডের মানগুলো কাছাকাছি থাকতে পারে, সংযুক্ত শার্ড এবং ইনডেক্স মান Cloud Firestore ইনডেক্স এন্ট্রিগুলোর মধ্যে যথেষ্ট জায়গা দেয়, যার ফলে এন্ট্রিগুলো একাধিক ট্যাবলেটের মধ্যে ভাগ করা যায়।

এরপর কী?