Skip to content

Commit 8cea247

Browse files
Gurov Ilyabradmiro
authored andcommitted
Add Firestore distributed counter snippets (GoogleCloudPlatform#2412)
* Add samples for distributed counters * Refactor list building * Excess instance * Empty line delete * Empty lines delete * Make comments more detailed. * Fix line too long * Move distributed counter samples and tests into separate files. * Blank lines fix. * Add firestore.Client * Test prefix. * Add fixture. * Line too long fix. * Line too long fix. * Update firestore/cloud-client/distributed_counters.py Co-Authored-By: Brad Miro <[email protected]> * Update firestore/cloud-client/distributed_counters_test.py Co-Authored-By: Brad Miro <[email protected]> * Update firestore/cloud-client/distributed_counters_test.py Co-Authored-By: Brad Miro <[email protected]> * Update firestore/cloud-client/distributed_counters_test.py Co-Authored-By: Brad Miro <[email protected]>
1 parent 27c5c18 commit 8cea247

File tree

2 files changed

+126
-0
lines changed

2 files changed

+126
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Copyright 2019 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# [START fs_counter_classes]
16+
import random
17+
18+
from google.cloud import firestore
19+
20+
21+
class Shard(object):
22+
"""
23+
A shard is a distributed counter. Each shard can support being incremented
24+
once per second. Multiple shards are needed within a Counter to allow
25+
more frequent incrementing.
26+
"""
27+
28+
def __init__(self):
29+
self._count = 0
30+
31+
def to_dict(self):
32+
return {"count": self._count}
33+
34+
35+
class Counter(object):
36+
"""
37+
A counter stores a collection of shards which are
38+
summed to return a total count. This allows for more
39+
frequent incrementing than a single document.
40+
"""
41+
42+
def __init__(self, num_shards):
43+
self._num_shards = num_shards
44+
# [END fs_counter_classes]
45+
46+
# [START fs_create_counter]
47+
def init_counter(self, doc_ref):
48+
"""
49+
Create a given number of shards as
50+
subcollection of specified document.
51+
"""
52+
col_ref = doc_ref.collection("shards")
53+
54+
# Initialize each shard with count=0
55+
for num in range(self._num_shards):
56+
shard = Shard()
57+
col_ref.document(str(num)).set(shard.to_dict())
58+
# [END fs_create_counter]
59+
60+
# [START fs_increment_counter]
61+
def increment_counter(self, doc_ref):
62+
"""Increment a randomly picked shard."""
63+
doc_id = random.randint(0, self._num_shards - 1)
64+
65+
shard_ref = doc_ref.collection("shards").document(str(doc_id))
66+
return shard_ref.update({"count": firestore.Increment(1)})
67+
# [END fs_increment_counter]
68+
69+
# [START fs_get_count]
70+
def get_count(self, doc_ref):
71+
"""Return a total count across all shards."""
72+
total = 0
73+
shards = doc_ref.collection("shards").list_documents()
74+
for shard in shards:
75+
total += shard.get().to_dict().get("count", 0)
76+
return total
77+
# [END fs_get_count]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Copyright 2019 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from google.cloud import firestore
16+
import pytest
17+
18+
import distributed_counters
19+
20+
21+
shards_list = []
22+
doc_ref = None
23+
24+
25+
@pytest.fixture
26+
def fs_client():
27+
yield firestore.Client()
28+
29+
# clean up
30+
for shard in shards_list:
31+
shard.delete()
32+
33+
if doc_ref:
34+
doc_ref.delete()
35+
36+
37+
def test_distributed_counters(fs_client):
38+
col = fs_client.collection("dc_samples")
39+
doc_ref = col.document("distributed_counter")
40+
counter = distributed_counters.Counter(2)
41+
counter.init_counter(doc_ref)
42+
43+
shards = doc_ref.collection("shards").list_documents()
44+
shards_list = [shard for shard in shards]
45+
assert len(shards_list) == 2
46+
47+
counter.increment_counter(doc_ref)
48+
counter.increment_counter(doc_ref)
49+
assert counter.get_count(doc_ref) == 2

0 commit comments

Comments
 (0)