Skip to content

Commit 6d6b30a

Browse files
committed
all: update review comments + show type for BeginTransaction + remove prints
1 parent f437955 commit 6d6b30a

File tree

11 files changed

+194
-215
lines changed

11 files changed

+194
-215
lines changed

google/cloud/spanner_v1/_opentelemetry_tracing.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,6 @@ def trace_call(name, session=None, extra_attributes=None, observability_options=
6262
yield None
6363
return
6464

65-
if session:
66-
session._last_use_time = datetime.now()
67-
6865
tracer_provider = None
6966

7067
# By default enable_extended_tracing=True because in a bid to minimize
@@ -104,16 +101,6 @@ def trace_call(name, session=None, extra_attributes=None, observability_options=
104101
if not enable_extended_tracing:
105102
attributes.pop("db.statement", False)
106103
attributes.pop("sql", False)
107-
else:
108-
# Otherwise there are places where the annotated sql was inserted
109-
# directly from the arguments as "sql", and transform those into "db.statement".
110-
db_statement = attributes.get("db.statement", None)
111-
if not db_statement:
112-
sql = attributes.get("sql", None)
113-
if sql:
114-
attributes = attributes.copy()
115-
attributes.pop("sql", False)
116-
attributes["db.statement"] = sql
117104

118105
with tracer.start_as_current_span(
119106
name, kind=trace.SpanKind.CLIENT, attributes=attributes

google/cloud/spanner_v1/session.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,6 @@ def run_in_transaction(self, func, *args, **kw):
471471
) as span:
472472
while True:
473473
if self._transaction is None:
474-
add_span_event(span, "Creating Transaction")
475474
txn = self.transaction()
476475
txn.transaction_tag = transaction_tag
477476
txn.exclude_txn_from_change_streams = (
@@ -499,7 +498,7 @@ def run_in_transaction(self, func, *args, **kw):
499498
record_span_exception_and_status(span, exc)
500499
add_span_event(
501500
span,
502-
"Transaction was aborted, retrying afresh",
501+
"Transaction was aborted in user operation, retrying",
503502
attributes,
504503
)
505504

@@ -516,7 +515,7 @@ def run_in_transaction(self, func, *args, **kw):
516515
except Exception:
517516
add_span_event(
518517
span,
519-
"Invoking Transaction.rollback(), not retrying",
518+
"User operation failed. Invoking Transaction.rollback(), not retrying",
520519
span_attributes,
521520
)
522521
txn.rollback()
@@ -537,7 +536,7 @@ def run_in_transaction(self, func, *args, **kw):
537536
record_span_exception_and_status(span, exc)
538537
add_span_event(
539538
span,
540-
"Transaction was aborted, retrying afresh",
539+
"Transaction got aborted during commit, retrying afresh",
541540
attributes,
542541
)
543542

google/cloud/spanner_v1/snapshot.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -926,7 +926,7 @@ def begin(self):
926926
)
927927
txn_selector = self._make_txn_selector()
928928
with trace_call(
929-
"CloudSpanner.BeginTransaction",
929+
f"CloudSpanner.{type(self).__name__}.begin",
930930
self._session,
931931
observability_options=getattr(database, "observability_options", None),
932932
):

google/cloud/spanner_v1/transaction.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ def begin(self):
157157
)
158158
observability_options = getattr(database, "observability_options", None)
159159
with trace_call(
160-
"CloudSpanner.BeginTransaction",
160+
f"CloudSpanner.{type(self).__name__}.begin",
161161
self._session,
162162
observability_options=observability_options,
163163
) as span:

tests/_helpers.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ def tearDown(self):
7979
def assertNoSpans(self):
8080
if HAS_OPENTELEMETRY_INSTALLED:
8181
span_list = self.get_finished_spans()
82-
print("got_spans", [span.name for span in span_list])
8382
self.assertEqual(len(span_list), 0)
8483

8584
def assertSpanAttributes(

tests/system/_helpers.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,21 @@ def cleanup_old_instances(spanner_client):
137137

138138
def unique_id(prefix, separator="-"):
139139
return f"{prefix}{system.unique_resource_id(separator)}"
140+
141+
142+
class FauxCall:
143+
def __init__(self, code, details="FauxCall"):
144+
self._code = code
145+
self._details = details
146+
147+
def initial_metadata(self):
148+
return {}
149+
150+
def trailing_metadata(self):
151+
return {}
152+
153+
def code(self):
154+
return self._code
155+
156+
def details(self):
157+
return self._details

tests/system/test_observability_options.py

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,161 @@ def test_propagation(enable_extended_tracing):
131131
test_propagation(False)
132132

133133

134+
@pytest.mark.skipif(
135+
not _helpers.USE_EMULATOR,
136+
reason="Emulator needed to run this tests",
137+
)
138+
@pytest.mark.skipif(
139+
not HAS_OTEL_INSTALLED,
140+
reason="Tracing requires OpenTelemetry",
141+
)
142+
def test_transaction_abort_then_retry_spans():
143+
from google.auth.credentials import AnonymousCredentials
144+
from google.api_core.exceptions import Aborted
145+
from google.rpc import code_pb2
146+
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
147+
from opentelemetry.sdk.trace.export.in_memory_span_exporter import (
148+
InMemorySpanExporter,
149+
)
150+
from opentelemetry.trace.status import StatusCode
151+
from opentelemetry.sdk.trace import TracerProvider
152+
from opentelemetry.sdk.trace.sampling import ALWAYS_ON
153+
from opentelemetry import trace
154+
155+
PROJECT = _helpers.EMULATOR_PROJECT
156+
CONFIGURATION_NAME = "config-name"
157+
INSTANCE_ID = _helpers.INSTANCE_ID
158+
DISPLAY_NAME = "display-name"
159+
DATABASE_ID = _helpers.unique_id("temp_db")
160+
NODE_COUNT = 5
161+
LABELS = {"test": "true"}
162+
163+
counters = dict(aborted=0)
164+
already_aborted = False
165+
166+
def select_in_txn(txn):
167+
from google.rpc import error_details_pb2
168+
169+
results = txn.execute_sql("SELECT 1")
170+
for row in results:
171+
_ = row
172+
173+
if counters["aborted"] == 0:
174+
counters["aborted"] = 1
175+
raise Aborted(
176+
"Thrown from ClientInterceptor for testing",
177+
errors=[_helpers.FauxCall(code_pb2.ABORTED)],
178+
)
179+
180+
tracer_provider = TracerProvider(sampler=ALWAYS_ON)
181+
trace_exporter = InMemorySpanExporter()
182+
tracer_provider.add_span_processor(SimpleSpanProcessor(trace_exporter))
183+
observability_options = dict(
184+
tracer_provider=tracer_provider,
185+
enable_extended_tracing=True,
186+
)
187+
188+
client = Client(
189+
project=PROJECT,
190+
observability_options=observability_options,
191+
credentials=AnonymousCredentials(),
192+
)
193+
194+
instance = client.instance(
195+
INSTANCE_ID,
196+
CONFIGURATION_NAME,
197+
display_name=DISPLAY_NAME,
198+
node_count=NODE_COUNT,
199+
labels=LABELS,
200+
)
201+
202+
try:
203+
instance.create()
204+
except Exception:
205+
pass
206+
207+
db = instance.database(DATABASE_ID)
208+
try:
209+
db.create()
210+
except Exception:
211+
pass
212+
213+
db.run_in_transaction(select_in_txn)
214+
215+
span_list = trace_exporter.get_finished_spans()
216+
got_span_names = [span.name for span in span_list]
217+
want_span_names = [
218+
"CloudSpanner.CreateSession",
219+
"CloudSpanner.Transaction.execute_streaming_sql",
220+
"CloudSpanner.Transaction.execute_streaming_sql",
221+
"CloudSpanner.Transaction.commit",
222+
"CloudSpanner.Session.run_in_transaction",
223+
"CloudSpanner.Database.run_in_transaction",
224+
]
225+
226+
assert got_span_names == want_span_names
227+
228+
got_events = []
229+
got_statuses = []
230+
231+
# Some event attributes are noisy/highly ephemeral
232+
# and can't be directly compared against.
233+
imprecise_event_attributes = ["exception.stacktrace", "delay_seconds"]
234+
for span in span_list:
235+
got_statuses.append(
236+
(span.name, span.status.status_code, span.status.description)
237+
)
238+
for event in span.events:
239+
evt_attributes = event.attributes.copy()
240+
for attr_name in imprecise_event_attributes:
241+
if attr_name in evt_attributes:
242+
evt_attributes[attr_name] = "EPHEMERAL"
243+
244+
got_events.append((event.name, evt_attributes))
245+
246+
# Check for the series of events
247+
want_events = [
248+
("Starting Commit", {}),
249+
("Commit Done", {}),
250+
("Using Transaction", {"attempt": 1}),
251+
(
252+
"exception",
253+
{
254+
"exception.type": "google.api_core.exceptions.Aborted",
255+
"exception.message": "409 Thrown from ClientInterceptor for testing",
256+
"exception.stacktrace": "EPHEMERAL",
257+
"exception.escaped": "False",
258+
},
259+
),
260+
(
261+
"Transaction was aborted in user operation, retrying",
262+
{"delay_seconds": "EPHEMERAL", "attempt": 1},
263+
),
264+
("Using Transaction", {"attempt": 2}),
265+
("Acquiring session", {"kind": "BurstyPool"}),
266+
("Waiting for a session to become available", {"kind": "BurstyPool"}),
267+
("No sessions available in pool. Creating session", {"kind": "BurstyPool"}),
268+
("Creating Session", {}),
269+
]
270+
assert got_events == want_events
271+
272+
# Check for the statues.
273+
codes = StatusCode
274+
want_statuses = [
275+
("CloudSpanner.CreateSession", codes.OK, None),
276+
("CloudSpanner.Transaction.execute_streaming_sql", codes.OK, None),
277+
("CloudSpanner.Transaction.execute_streaming_sql", codes.OK, None),
278+
("CloudSpanner.Transaction.commit", codes.OK, None),
279+
(
280+
"CloudSpanner.Session.run_in_transaction",
281+
codes.ERROR,
282+
"409 Thrown from ClientInterceptor for testing",
283+
),
284+
("CloudSpanner.Database.run_in_transaction", codes.OK, None),
285+
]
286+
assert got_statuses == want_statuses
287+
288+
134289
def _make_credentials():
135290
from google.auth.credentials import AnonymousCredentials
136291

0 commit comments

Comments
 (0)