অনেক রিয়েলটাইম অ্যাপে এমন ডকুমেন্ট থাকে যা কাউন্টার হিসেবে কাজ করে। উদাহরণস্বরূপ, আপনি একটি পোস্টে 'লাইক' বা একটি নির্দিষ্ট আইটেমের 'প্রিয়' গণনা করতে পারেন।
Cloud Firestore , আপনি সীমাহীন হারে একটি একক ডকুমেন্ট আপডেট করতে পারবেন না। যদি আপনার একক ডকুমেন্টের উপর ভিত্তি করে একটি কাউন্টার থাকে এবং এতে ঘন ঘন বৃদ্ধি করা হয় তবে আপনি অবশেষে ডকুমেন্টের আপডেটগুলি নিয়ে বিতর্ক দেখতে পাবেন। একটি একক ডকুমেন্টের আপডেট দেখুন।
সমাধান: বিতরণকৃত কাউন্টার
আরও ঘন ঘন কাউন্টার আপডেট সমর্থন করার জন্য, একটি বিতরণকৃত কাউন্টার তৈরি করুন। প্রতিটি কাউন্টার হল একটি ডকুমেন্ট যার মধ্যে "শার্ড" এর একটি উপ-সংগ্রহ রয়েছে এবং কাউন্টারের মান হল শার্ডগুলির মানের যোগফল।
লেখার থ্রুপুট শার্ডের সংখ্যার সাথে রৈখিকভাবে বৃদ্ধি পায়, তাই 10 টি শার্ড সহ একটি বিতরণকৃত কাউন্টার একটি ঐতিহ্যবাহী কাউন্টারের তুলনায় 10 গুণ বেশি লেখা পরিচালনা করতে পারে।
ওয়েব
// counters/${ID}
{
"num_shards": NUM_SHARDS,
"shards": [subcollection]
}
// counters/${ID}/shards/${NUM}
{
"count": 123
}
সুইফট
// counters/${ID} struct Counter { let numShards: Int init(numShards: Int) { self.numShards = numShards } } // counters/${ID}/shards/${NUM} struct Shard { let count: Int init(count: Int) { self.count = count } }
অবজেক্টিভ-সি
// counters/${ID} @interface FIRCounter : NSObject @property (nonatomic, readonly) NSInteger shardCount; @end @implementation FIRCounter - (instancetype)initWithShardCount:(NSInteger)shardCount { self = [super init]; if (self != nil) { _shardCount = shardCount; } return self; } @end // counters/${ID}/shards/${NUM} @interface FIRShard : NSObject @property (nonatomic, readonly) NSInteger count; @end @implementation FIRShard - (instancetype)initWithCount:(NSInteger)count { self = [super init]; if (self != nil) { _count = count; } return self; } @end
Kotlin
// counters/${ID} data class Counter(var numShards: Int) // counters/${ID}/shards/${NUM} data class Shard(var count: Int)
Java
// counters/${ID} public class Counter { int numShards; public Counter(int numShards) { this.numShards = numShards; } } // counters/${ID}/shards/${NUM} public class Shard { int count; public Shard(int count) { this.count = count; } }
পাইথন
Python
নোড.জেএস
প্রযোজ্য নয়, নীচের কাউন্টার ইনক্রিমেন্ট স্নিপেটটি দেখুন।
যাও
পিএইচপি
প্রযোজ্য নয়, নীচের কাউন্টার ইনিশিয়ালাইজেশন স্নিপেটটি দেখুন।
সি#
নিম্নলিখিত কোডটি একটি বিতরণকৃত কাউন্টার শুরু করে:
ওয়েব
function createCounter(ref, num_shards) { var batch = db.batch(); // Initialize the counter document batch.set(ref, { num_shards: num_shards }); // Initialize each shard with count=0 for (let i = 0; i < num_shards; i++) { const shardRef = ref.collection('shards').doc(i.toString()); batch.set(shardRef, { count: 0 }); } // Commit the write batch return batch.commit(); }
সুইফট
func createCounter(ref: DocumentReference, numShards: Int) async { do { try await ref.setData(["numShards": numShards]) for i in 0...numShards { try await ref.collection("shards").document(String(i)).setData(["count": 0]) } } catch { // ... } }
অবজেক্টিভ-সি
- (void)createCounterAtReference:(FIRDocumentReference *)reference shardCount:(NSInteger)shardCount { [reference setData:@{ @"numShards": @(shardCount) } completion:^(NSError * _Nullable error) { for (NSInteger i = 0; i < shardCount; i++) { NSString *shardName = [NSString stringWithFormat:@"%ld", (long)shardCount]; [[[reference collectionWithPath:@"shards"] documentWithPath:shardName] setData:@{ @"count": @(0) }]; } }]; }
Kotlin
fun createCounter(ref: DocumentReference, numShards: Int): Task<Void> { // Initialize the counter document, then initialize each shard. return ref.set(Counter(numShards)) .continueWithTask { task -> if (!task.isSuccessful) { throw task.exception!! } val tasks = arrayListOf<Task<Void>>() // Initialize each shard with count=0 for (i in 0 until numShards) { val makeShard = ref.collection("shards") .document(i.toString()) .set(Shard(0)) tasks.add(makeShard) } Tasks.whenAll(tasks) } }
Java
public Task<Void> createCounter(final DocumentReference ref, final int numShards) { // Initialize the counter document, then initialize each shard. return ref.set(new Counter(numShards)) .continueWithTask(new Continuation<Void, Task<Void>>() { @Override public Task<Void> then(@NonNull Task<Void> task) throws Exception { if (!task.isSuccessful()) { throw task.getException(); } List<Task<Void>> tasks = new ArrayList<>(); // Initialize each shard with count=0 for (int i = 0; i < numShards; i++) { Task<Void> makeShard = ref.collection("shards") .document(String.valueOf(i)) .set(new Shard(0)); tasks.add(makeShard); } return Tasks.whenAll(tasks); } }); }
পাইথন
Python
নোড.জেএস
প্রযোজ্য নয়, নীচের কাউন্টার ইনক্রিমেন্ট স্নিপেটটি দেখুন।
যাও
পিএইচপি
সি#
রুবি
কাউন্টার বাড়ানোর জন্য, একটি এলোমেলো শার্ড বেছে নিন এবং গণনা বাড়ান:
ওয়েব
function incrementCounter(ref, num_shards) { // Select a shard of the counter at random const shard_id = Math.floor(Math.random() * num_shards).toString(); const shard_ref = ref.collection('shards').doc(shard_id); // Update count return shard_ref.update("count", firebase.firestore.FieldValue.increment(1)); }
সুইফট
func incrementCounter(ref: DocumentReference, numShards: Int) { // Select a shard of the counter at random let shardId = Int(arc4random_uniform(UInt32(numShards))) let shardRef = ref.collection("shards").document(String(shardId)) shardRef.updateData([ "count": FieldValue.increment(Int64(1)) ]) }
অবজেক্টিভ-সি
- (void)incrementCounterAtReference:(FIRDocumentReference *)reference shardCount:(NSInteger)shardCount { // Select a shard of the counter at random NSInteger shardID = (NSInteger)arc4random_uniform((uint32_t)shardCount); NSString *shardName = [NSString stringWithFormat:@"%ld", (long)shardID]; FIRDocumentReference *shardReference = [[reference collectionWithPath:@"shards"] documentWithPath:shardName]; [shardReference updateData:@{ @"count": [FIRFieldValue fieldValueForIntegerIncrement:1] }]; }
Kotlin
fun incrementCounter(ref: DocumentReference, numShards: Int): Task<Void> { val shardId = Math.floor(Math.random() * numShards).toInt() val shardRef = ref.collection("shards").document(shardId.toString()) return shardRef.update("count", FieldValue.increment(1)) }
Java
public Task<Void> incrementCounter(final DocumentReference ref, final int numShards) { int shardId = (int) Math.floor(Math.random() * numShards); DocumentReference shardRef = ref.collection("shards").document(String.valueOf(shardId)); return shardRef.update("count", FieldValue.increment(1)); }
পাইথন
Python
নোড.জেএস
যাও
পিএইচপি
সি#
রুবি
মোট গণনা পেতে, সমস্ত অংশের জন্য অনুসন্ধান করুন এবং তাদের count
ক্ষেত্রগুলি যোগ করুন:
ওয়েব
function getCount(ref) { // Sum the count of each shard in the subcollection return ref.collection('shards').get().then((snapshot) => { let total_count = 0; snapshot.forEach((doc) => { total_count += doc.data().count; }); return total_count; }); }
সুইফট
func getCount(ref: DocumentReference) async { do { let querySnapshot = try await ref.collection("shards").getDocuments() var totalCount = 0 for document in querySnapshot.documents { let count = document.data()["count"] as! Int totalCount += count } print("Total count is \(totalCount)") } catch { // handle error } }
অবজেক্টিভ-সি
- (void)getCountWithReference:(FIRDocumentReference *)reference { [[reference collectionWithPath:@"shards"] getDocumentsWithCompletion:^(FIRQuerySnapshot *snapshot, NSError *error) { NSInteger totalCount = 0; if (error != nil) { // Error getting shards // ... } else { for (FIRDocumentSnapshot *document in snapshot.documents) { NSInteger count = [document[@"count"] integerValue]; totalCount += count; } NSLog(@"Total count is %ld", (long)totalCount); } }]; }
Kotlin
fun getCount(ref: DocumentReference): Task<Int> { // Sum the count of each shard in the subcollection return ref.collection("shards").get() .continueWith { task -> var count = 0 for (snap in task.result!!) { val shard = snap.toObject<Shard>() count += shard.count } count } }
Java
public Task<Integer> getCount(final DocumentReference ref) { // Sum the count of each shard in the subcollection return ref.collection("shards").get() .continueWith(new Continuation<QuerySnapshot, Integer>() { @Override public Integer then(@NonNull Task<QuerySnapshot> task) throws Exception { int count = 0; for (DocumentSnapshot snap : task.getResult()) { Shard shard = snap.toObject(Shard.class); count += shard.count; } return count; } }); }
পাইথন
Python
নোড.জেএস
যাও
পিএইচপি
সি#
রুবি
সীমাবদ্ধতা
উপরে দেখানো সমাধানটি Cloud Firestore শেয়ার্ড কাউন্টার তৈরি করার একটি স্কেলেবল উপায়, তবে আপনার নিম্নলিখিত সীমাবদ্ধতাগুলি সম্পর্কে সচেতন থাকা উচিত:
- শার্ড কাউন্ট - শার্ডের সংখ্যা বিতরণকৃত কাউন্টারের কর্মক্ষমতা নিয়ন্ত্রণ করে। খুব কম শার্ড থাকলে, কিছু লেনদেন সফল হওয়ার আগে পুনরায় চেষ্টা করতে হতে পারে, যা লেখার গতি কমিয়ে দেবে। অনেক শার্ড থাকলে, পঠন ধীর এবং ব্যয়বহুল হয়ে ওঠে। আপনি কাউন্টারের মোট পরিমাণ একটি পৃথক রোল-আপ ডকুমেন্টে রেখে পঠন-ব্যয় অফসেট করতে পারেন যা ধীর গতিতে আপডেট করা হয় এবং ক্লায়েন্টদের সেই ডকুমেন্ট থেকে মোট পরিমাণ পড়ার জন্য পাঠাতে হয়। বিনিময়ের অর্থ হল ক্লায়েন্টদের রোল-আপ ডকুমেন্ট আপডেট হওয়ার জন্য অপেক্ষা করতে হবে, কোনও আপডেটের পরে অবিলম্বে সমস্ত শার্ড পড়ে মোট পরিমাণ গণনা করার পরিবর্তে।
- খরচ - একটি কাউন্টার মান পড়ার খরচ শার্ডের সংখ্যার সাথে রৈখিকভাবে বৃদ্ধি পায়, কারণ সম্পূর্ণ শার্ড সাবকালেকশন লোড করতে হবে।