Skip to content

Commit cfe27c8

Browse files
committed
All add much deeper tests to check for span statuses plus retries+abort
Also while here, fixed #1246
1 parent 2e49067 commit cfe27c8

File tree

7 files changed

+251
-20
lines changed

7 files changed

+251
-20
lines changed

google/cloud/spanner_v1/_opentelemetry_tracing.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@ def trace_call_end_lazily(
142142
ctx_manager.__enter__()
143143

144144
def discard(exc_type=None, exc_value=None, exc_traceback=None):
145+
if not exc_type:
146+
span.set_status(Status(StatusCode.OK))
147+
145148
ctx_manager.__exit__(exc_type, exc_value, exc_traceback)
146149

147150
return discard
@@ -175,7 +178,12 @@ def trace_call(name, session=None, extra_attributes=None, observability_options=
175178
span.record_exception(error)
176179
raise
177180
else:
178-
span.set_status(Status(StatusCode.OK))
181+
if span._status.status_code == StatusCode.UNSET:
182+
# OpenTelemetry-Python only allows a status change
183+
# if the current code is UNSET or ERROR. At the end
184+
# of the generator's consumption, only set it to OK
185+
# it wasn't previously set otherwise
186+
span.set_status(Status(StatusCode.OK))
179187

180188

181189
def set_span_status_error(span, error):
@@ -204,5 +212,5 @@ def add_event_on_current_span(event_name, attributes=None, span=None):
204212

205213
def record_span_exception_and_status(span, exc):
206214
if span:
207-
span.set_status(Status(StatusCode.ERROR, "foo"))
215+
span.set_status(Status(StatusCode.ERROR, str(exc)))
208216
span.record_exception(exc)

google/cloud/spanner_v1/batch.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class _BatchBase(_SessionWrapper):
5050
def __init__(self, session):
5151
super(_BatchBase, self).__init__(session)
5252
self._mutations = []
53-
self.__discard_span = trace_call_end_lazily(
53+
self.__base_discard_span = trace_call_end_lazily(
5454
f"CloudSpanner.{type(self).__name__}",
5555
self._session,
5656
None,
@@ -157,6 +157,11 @@ def delete(self, table, keyset):
157157
dict(table=table),
158158
)
159159

160+
def _discard_on_end(self, exc_type=None, exc_val=None, exc_traceback=None):
161+
if self.__base_discard_span:
162+
self.__base_discard_span(exc_type, exc_val, exc_traceback)
163+
self.__base_discard_span = None
164+
160165

161166
class Batch(_BatchBase):
162167
"""Accumulate mutations for transmission during :meth:`commit`."""
@@ -253,6 +258,7 @@ def commit(
253258
)
254259
self.committed = response.commit_timestamp
255260
self.commit_stats = response.commit_stats
261+
self._discard_on_end()
256262
return self.committed
257263

258264
def __enter__(self):
@@ -276,6 +282,7 @@ def __exit__(self, exc_type, exc_val, exc_tb):
276282
if self.__discard_span:
277283
self.__discard_span(exc_type, exc_val, exc_tb)
278284
self.__discard_span = None
285+
self._discard_on_end()
279286

280287

281288
class MutationGroup(_BatchBase):

google/cloud/spanner_v1/session.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,6 @@ def exists(self):
180180
)
181181

182182
observability_options = getattr(self._database, "observability_options", None)
183-
print(f"obsopts {observability_options}")
184183
with trace_call(
185184
"CloudSpanner.GetSession", self, observability_options=observability_options
186185
) as span:

google/cloud/spanner_v1/snapshot.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ def _restart_on_unavailable(
9090
span_name, session, attributes, observability_options=observability_options
9191
):
9292
iterator = method(request=request)
93+
9394
while True:
9495
try:
9596
for item in iterator:

google/cloud/spanner_v1/transaction.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ def rollback(self):
206206
)
207207
self.rolled_back = True
208208
del self._session._transaction
209+
self._discard_on_end()
209210

210211
def commit(
211212
self, return_commit_stats=False, request_options=None, max_commit_delay=None
@@ -286,6 +287,7 @@ def commit(
286287
if return_commit_stats:
287288
self.commit_stats = response.commit_stats
288289
del self._session._transaction
290+
self._discard_on_end()
289291
return self.committed
290292

291293
@staticmethod

tests/system/test_observability_options.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,48 @@ def _make_credentials():
137137
from google.auth.credentials import AnonymousCredentials
138138

139139
return AnonymousCredentials()
140+
141+
142+
from tests import _helpers as ot_helpers
143+
144+
145+
@pytest.mark.skipif(
146+
not ot_helpers.HAS_OPENTELEMETRY_INSTALLED,
147+
reason="Tracing requires OpenTelemetry",
148+
)
149+
def test_trace_call_keeps_span_error_status():
150+
# Verifies that after our span's status was set to ERROR
151+
# that it doesn't unconditionally get changed to OK
152+
# per https://github.com/googleapis/python-spanner/issues/1246
153+
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
154+
from opentelemetry.sdk.trace.export.in_memory_span_exporter import (
155+
InMemorySpanExporter,
156+
)
157+
from google.cloud.spanner_v1._opentelemetry_tracing import trace_call
158+
from opentelemetry.trace.status import Status, StatusCode
159+
from opentelemetry.sdk.trace import TracerProvider
160+
from opentelemetry.sdk.trace.sampling import ALWAYS_ON
161+
from opentelemetry import trace
162+
163+
tracer_provider = TracerProvider(sampler=ALWAYS_ON)
164+
trace_exporter = InMemorySpanExporter()
165+
tracer_provider.add_span_processor(SimpleSpanProcessor(trace_exporter))
166+
observability_options = dict(tracer_provider=tracer_provider)
167+
168+
with trace_call(
169+
"VerifyBehavior", observability_options=observability_options
170+
) as span:
171+
span.set_status(Status(StatusCode.ERROR, "Our error exhibit"))
172+
173+
span_list = trace_exporter.get_finished_spans()
174+
got_statuses = []
175+
176+
for span in span_list:
177+
got_statuses.append(
178+
(span.name, span.status.status_code, span.status.description)
179+
)
180+
181+
want_statuses = [
182+
("VerifyBehavior", StatusCode.ERROR, "Our error exhibit"),
183+
]
184+
assert got_statuses == want_statuses

0 commit comments

Comments
 (0)