Skip to content

Commit d3e4732

Browse files
committed
Add async collection group instrumentation
1 parent 5902515 commit d3e4732

File tree

2 files changed

+87
-0
lines changed

2 files changed

+87
-0
lines changed

newrelic/hooks/datastore_firestore.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,12 @@ def instrument_google_cloud_firestore_v1_async_query(module):
183183
if hasattr(class_, method):
184184
wrap_async_generator_method(module, "AsyncQuery", method, target=_get_parent_id)
185185

186+
if hasattr(module, "AsyncCollectionGroup"):
187+
class_ = module.AsyncCollectionGroup
188+
for method in ("get_partitions",):
189+
if hasattr(class_, method):
190+
wrap_async_generator_method(module, "AsyncCollectionGroup", method, target=_get_parent_id)
191+
186192

187193
def instrument_google_cloud_firestore_v1_aggregation(module):
188194
if hasattr(module, "AggregationQuery"):

tests/datastore_firestore/test_async_query.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ def sample_data(collection):
2626
for x in range(1, 6):
2727
collection.add({"x": x})
2828

29+
subcollection_doc = collection.document("subcollection")
30+
subcollection_doc.set({})
31+
subcollection_doc.collection("subcollection1").add({})
32+
33+
2934
# ===== AsyncQuery =====
3035

3136
async def _exercise_async_query(async_collection):
@@ -103,3 +108,79 @@ def _test():
103108
def test_firestore_async_aggregation_query_generators(async_collection, assert_trace_for_async_generator):
104109
async_aggregation_query = async_collection.select("x").where(field_path="x", op_string="<=", value=3).count()
105110
assert_trace_for_async_generator(async_aggregation_query.stream)
111+
112+
113+
# ===== CollectionGroup =====
114+
115+
116+
@pytest.fixture()
117+
def patch_partition_queries(monkeypatch, async_client, collection, sample_data):
118+
"""
119+
Partitioning is not implemented in the Firestore emulator.
120+
121+
Ordinarily this method would return a coroutine that returns an async_generator of Cursor objects.
122+
Each Cursor must point at a valid document path. To test this, we can patch the RPC to return 1 Cursor
123+
which is pointed at any document available. The get_partitions will take that and make 2 QueryPartition
124+
objects out of it, which should be enough to ensure we can exercise the generator's tracing.
125+
"""
126+
from google.cloud.firestore_v1.types.document import Value
127+
from google.cloud.firestore_v1.types.query import Cursor
128+
129+
subcollection = collection.document("subcollection").collection("subcollection1")
130+
documents = [d for d in subcollection.list_documents()]
131+
132+
async def mock_partition_query(*args, **kwargs):
133+
async def _mock_partition_query():
134+
yield Cursor(before=False, values=[Value(reference_value=documents[0].path)])
135+
return _mock_partition_query()
136+
137+
monkeypatch.setattr(async_client._firestore_api, "partition_query", mock_partition_query)
138+
yield
139+
140+
141+
async def _exercise_async_collection_group(async_client, async_collection):
142+
async_collection_group = async_client.collection_group(async_collection.id)
143+
assert len(await async_collection_group.get())
144+
assert len([d async for d in async_collection_group.stream()])
145+
146+
partitions = [p async for p in async_collection_group.get_partitions(1)]
147+
assert len(partitions) == 2
148+
documents = []
149+
while partitions:
150+
documents.extend(await partitions.pop().query().get())
151+
assert len(documents) == 6
152+
153+
154+
def test_firestore_async_collection_group(loop, async_client, async_collection, patch_partition_queries):
155+
_test_scoped_metrics = [
156+
("Datastore/statement/Firestore/%s/get" % async_collection.id, 3),
157+
("Datastore/statement/Firestore/%s/stream" % async_collection.id, 1),
158+
("Datastore/statement/Firestore/%s/get_partitions" % async_collection.id, 1),
159+
]
160+
161+
_test_rollup_metrics = [
162+
("Datastore/operation/Firestore/get", 3),
163+
("Datastore/operation/Firestore/stream", 1),
164+
("Datastore/operation/Firestore/get_partitions", 1),
165+
("Datastore/all", 5),
166+
("Datastore/allOther", 5),
167+
]
168+
169+
@validate_database_duration()
170+
@validate_transaction_metrics(
171+
"test_firestore_async_collection_group",
172+
scoped_metrics=_test_scoped_metrics,
173+
rollup_metrics=_test_rollup_metrics,
174+
background_task=True,
175+
)
176+
@background_task(name="test_firestore_async_collection_group")
177+
def _test():
178+
loop.run_until_complete(_exercise_async_collection_group(async_client, async_collection))
179+
180+
_test()
181+
182+
183+
@background_task()
184+
def test_firestore_async_collection_group_generators(async_client, async_collection, assert_trace_for_async_generator, patch_partition_queries):
185+
async_collection_group = async_client.collection_group(async_collection.id)
186+
assert_trace_for_async_generator(async_collection_group.get_partitions, 1)

0 commit comments

Comments
 (0)