kurrentdbclient 0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kurrentdbclient/__init__.py +49 -0
- kurrentdbclient/asyncio_client.py +1662 -0
- kurrentdbclient/client.py +1914 -0
- kurrentdbclient/common.py +535 -0
- kurrentdbclient/connection.py +107 -0
- kurrentdbclient/connection_spec.py +371 -0
- kurrentdbclient/events.py +141 -0
- kurrentdbclient/exceptions.py +239 -0
- kurrentdbclient/gossip.py +104 -0
- kurrentdbclient/instrumentation/__init__.py +0 -0
- kurrentdbclient/instrumentation/opentelemetry/__init__.py +185 -0
- kurrentdbclient/instrumentation/opentelemetry/attributes.py +20 -0
- kurrentdbclient/instrumentation/opentelemetry/grpc.py +165 -0
- kurrentdbclient/instrumentation/opentelemetry/package.py +2 -0
- kurrentdbclient/instrumentation/opentelemetry/spanners.py +1097 -0
- kurrentdbclient/instrumentation/opentelemetry/utils.py +199 -0
- kurrentdbclient/instrumentation/opentelemetry/version.py +2 -0
- kurrentdbclient/persistent.py +1982 -0
- kurrentdbclient/projections.py +735 -0
- kurrentdbclient/protos/Grpc/cluster_pb2.py +92 -0
- kurrentdbclient/protos/Grpc/cluster_pb2.pyi +765 -0
- kurrentdbclient/protos/Grpc/cluster_pb2_grpc.py +514 -0
- kurrentdbclient/protos/Grpc/code_pb2.py +37 -0
- kurrentdbclient/protos/Grpc/code_pb2.pyi +357 -0
- kurrentdbclient/protos/Grpc/code_pb2_grpc.py +24 -0
- kurrentdbclient/protos/Grpc/gossip_pb2.py +46 -0
- kurrentdbclient/protos/Grpc/gossip_pb2.pyi +126 -0
- kurrentdbclient/protos/Grpc/gossip_pb2_grpc.py +98 -0
- kurrentdbclient/protos/Grpc/persistent_pb2.py +140 -0
- kurrentdbclient/protos/Grpc/persistent_pb2.pyi +1135 -0
- kurrentdbclient/protos/Grpc/persistent_pb2_grpc.py +399 -0
- kurrentdbclient/protos/Grpc/projections_pb2.py +99 -0
- kurrentdbclient/protos/Grpc/projections_pb2.pyi +558 -0
- kurrentdbclient/protos/Grpc/projections_pb2_grpc.py +485 -0
- kurrentdbclient/protos/Grpc/shared_pb2.py +62 -0
- kurrentdbclient/protos/Grpc/shared_pb2.pyi +218 -0
- kurrentdbclient/protos/Grpc/shared_pb2_grpc.py +24 -0
- kurrentdbclient/protos/Grpc/status_pb2.py +39 -0
- kurrentdbclient/protos/Grpc/status_pb2.pyi +67 -0
- kurrentdbclient/protos/Grpc/status_pb2_grpc.py +24 -0
- kurrentdbclient/protos/Grpc/streams_pb2.py +132 -0
- kurrentdbclient/protos/Grpc/streams_pb2.pyi +1038 -0
- kurrentdbclient/protos/Grpc/streams_pb2_grpc.py +269 -0
- kurrentdbclient/py.typed +0 -0
- kurrentdbclient/streams.py +1400 -0
- kurrentdbclient-0.3.dist-info/LICENSE +29 -0
- kurrentdbclient-0.3.dist-info/METADATA +3769 -0
- kurrentdbclient-0.3.dist-info/RECORD +49 -0
- kurrentdbclient-0.3.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,1097 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import inspect
|
|
5
|
+
import json
|
|
6
|
+
import sys
|
|
7
|
+
from typing import (
|
|
8
|
+
Any,
|
|
9
|
+
Callable,
|
|
10
|
+
Dict,
|
|
11
|
+
Generic,
|
|
12
|
+
Iterable,
|
|
13
|
+
Literal,
|
|
14
|
+
Optional,
|
|
15
|
+
Protocol,
|
|
16
|
+
Sequence,
|
|
17
|
+
Tuple,
|
|
18
|
+
TypeVar,
|
|
19
|
+
Union,
|
|
20
|
+
cast,
|
|
21
|
+
overload,
|
|
22
|
+
)
|
|
23
|
+
from uuid import UUID
|
|
24
|
+
|
|
25
|
+
import grpc
|
|
26
|
+
from opentelemetry.context import Context
|
|
27
|
+
from opentelemetry.trace import (
|
|
28
|
+
NonRecordingSpan,
|
|
29
|
+
Span,
|
|
30
|
+
SpanContext,
|
|
31
|
+
SpanKind,
|
|
32
|
+
StatusCode,
|
|
33
|
+
TraceFlags,
|
|
34
|
+
Tracer,
|
|
35
|
+
set_span_in_context,
|
|
36
|
+
)
|
|
37
|
+
from opentelemetry.util.types import AttributeValue
|
|
38
|
+
from typing_extensions import Self
|
|
39
|
+
|
|
40
|
+
from kurrentdbclient import (
|
|
41
|
+
AsyncKurrentDBClient,
|
|
42
|
+
AsyncReadResponse,
|
|
43
|
+
KurrentDBClient,
|
|
44
|
+
NewEvent,
|
|
45
|
+
ReadResponse,
|
|
46
|
+
RecordedEvent,
|
|
47
|
+
StreamState,
|
|
48
|
+
)
|
|
49
|
+
from kurrentdbclient.client import BaseKurrentDBClient
|
|
50
|
+
from kurrentdbclient.common import (
|
|
51
|
+
AbstractAsyncCatchupSubscription,
|
|
52
|
+
AbstractAsyncPersistentSubscription,
|
|
53
|
+
AbstractAsyncReadResponse,
|
|
54
|
+
AbstractCatchupSubscription,
|
|
55
|
+
AbstractPersistentSubscription,
|
|
56
|
+
AbstractReadResponse,
|
|
57
|
+
AsyncRecordedEventIterator,
|
|
58
|
+
AsyncRecordedEventSubscription,
|
|
59
|
+
RecordedEventIterator,
|
|
60
|
+
RecordedEventSubscription,
|
|
61
|
+
)
|
|
62
|
+
from kurrentdbclient.connection_spec import URI_SCHEMES_DISCOVER
|
|
63
|
+
from kurrentdbclient.instrumentation.opentelemetry.attributes import Attributes
|
|
64
|
+
from kurrentdbclient.instrumentation.opentelemetry.utils import (
|
|
65
|
+
AsyncSpannerResponse,
|
|
66
|
+
OverloadedSpannerResponse,
|
|
67
|
+
SpannerResponse,
|
|
68
|
+
_set_span_error,
|
|
69
|
+
_set_span_ok,
|
|
70
|
+
_start_span,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
STREAMS_APPEND = "streams.append"
|
|
74
|
+
STREAMS_SUBSCRIBE = "streams.subscribe"
|
|
75
|
+
SPAN_NAMES_BY_CLIENT_METHOD = {
|
|
76
|
+
KurrentDBClient.append_to_stream.__qualname__: STREAMS_APPEND,
|
|
77
|
+
KurrentDBClient.subscribe_to_all.__qualname__: STREAMS_SUBSCRIBE,
|
|
78
|
+
KurrentDBClient.subscribe_to_stream.__qualname__: STREAMS_SUBSCRIBE,
|
|
79
|
+
KurrentDBClient.read_subscription_to_all.__qualname__: STREAMS_SUBSCRIBE,
|
|
80
|
+
KurrentDBClient.read_subscription_to_stream.__qualname__: STREAMS_SUBSCRIBE,
|
|
81
|
+
AsyncKurrentDBClient.append_to_stream.__qualname__: STREAMS_APPEND,
|
|
82
|
+
AsyncKurrentDBClient.subscribe_to_all.__qualname__: STREAMS_SUBSCRIBE,
|
|
83
|
+
AsyncKurrentDBClient.subscribe_to_stream.__qualname__: STREAMS_SUBSCRIBE,
|
|
84
|
+
AsyncKurrentDBClient.read_subscription_to_all.__qualname__: STREAMS_SUBSCRIBE,
|
|
85
|
+
AsyncKurrentDBClient.read_subscription_to_stream.__qualname__: STREAMS_SUBSCRIBE,
|
|
86
|
+
}
|
|
87
|
+
SPAN_KINDS_BY_CLIENT_METHOD = {
|
|
88
|
+
KurrentDBClient.append_to_stream.__qualname__: SpanKind.PRODUCER,
|
|
89
|
+
KurrentDBClient.subscribe_to_all.__qualname__: SpanKind.CONSUMER,
|
|
90
|
+
KurrentDBClient.subscribe_to_stream.__qualname__: SpanKind.CONSUMER,
|
|
91
|
+
KurrentDBClient.read_subscription_to_all.__qualname__: SpanKind.CONSUMER,
|
|
92
|
+
KurrentDBClient.read_subscription_to_stream.__qualname__: SpanKind.CONSUMER,
|
|
93
|
+
AsyncKurrentDBClient.append_to_stream.__qualname__: SpanKind.PRODUCER,
|
|
94
|
+
AsyncKurrentDBClient.subscribe_to_all.__qualname__: SpanKind.CONSUMER,
|
|
95
|
+
AsyncKurrentDBClient.subscribe_to_stream.__qualname__: SpanKind.CONSUMER,
|
|
96
|
+
AsyncKurrentDBClient.read_subscription_to_all.__qualname__: SpanKind.CONSUMER,
|
|
97
|
+
AsyncKurrentDBClient.read_subscription_to_stream.__qualname__: SpanKind.CONSUMER,
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _get_span_kind(func: Callable[..., Any]) -> SpanKind:
|
|
102
|
+
return SPAN_KINDS_BY_CLIENT_METHOD.get(func.__qualname__, SpanKind.CLIENT)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _get_span_name(func: Callable[..., Any]) -> str:
|
|
106
|
+
return SPAN_NAMES_BY_CLIENT_METHOD.get(func.__qualname__, func.__qualname__)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _get_span_name_and_kind(func: Callable[..., Any]) -> Tuple[str, SpanKind]:
|
|
110
|
+
return _get_span_name(func), _get_span_kind(func)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class GetStreamMethod(Protocol):
|
|
114
|
+
def __call__(
|
|
115
|
+
self,
|
|
116
|
+
/,
|
|
117
|
+
stream_name: str,
|
|
118
|
+
*,
|
|
119
|
+
stream_position: Optional[int] = None,
|
|
120
|
+
backwards: bool = False,
|
|
121
|
+
resolve_links: bool = False,
|
|
122
|
+
limit: int = sys.maxsize,
|
|
123
|
+
timeout: Optional[float] = None,
|
|
124
|
+
credentials: Optional[grpc.CallCredentials] = None,
|
|
125
|
+
) -> Sequence[RecordedEvent]:
|
|
126
|
+
pass # pragma: no cover
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class AsyncGetStreamMethod(Protocol):
|
|
130
|
+
async def __call__(
|
|
131
|
+
self,
|
|
132
|
+
/,
|
|
133
|
+
stream_name: str,
|
|
134
|
+
*,
|
|
135
|
+
stream_position: Optional[int] = None,
|
|
136
|
+
backwards: bool = False,
|
|
137
|
+
resolve_links: bool = False,
|
|
138
|
+
limit: int = sys.maxsize,
|
|
139
|
+
timeout: Optional[float] = None,
|
|
140
|
+
credentials: Optional[grpc.CallCredentials] = None,
|
|
141
|
+
) -> Sequence[RecordedEvent]:
|
|
142
|
+
pass # pragma: no cover
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@overload
|
|
146
|
+
def span_get_stream(
|
|
147
|
+
tracer: Tracer,
|
|
148
|
+
instance: BaseKurrentDBClient,
|
|
149
|
+
spanned_func: AsyncGetStreamMethod,
|
|
150
|
+
/,
|
|
151
|
+
stream_name: str,
|
|
152
|
+
*,
|
|
153
|
+
stream_position: Optional[int] = None,
|
|
154
|
+
backwards: bool = False,
|
|
155
|
+
resolve_links: bool = False,
|
|
156
|
+
limit: int = sys.maxsize,
|
|
157
|
+
timeout: Optional[float] = None,
|
|
158
|
+
credentials: Optional[grpc.CallCredentials] = None,
|
|
159
|
+
) -> AsyncSpannerResponse[Sequence[RecordedEvent]]:
|
|
160
|
+
pass # pragma: no cover
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@overload
|
|
164
|
+
def span_get_stream(
|
|
165
|
+
tracer: Tracer,
|
|
166
|
+
instance: BaseKurrentDBClient,
|
|
167
|
+
spanned_func: GetStreamMethod,
|
|
168
|
+
/,
|
|
169
|
+
stream_name: str,
|
|
170
|
+
*,
|
|
171
|
+
stream_position: Optional[int] = None,
|
|
172
|
+
backwards: bool = False,
|
|
173
|
+
resolve_links: bool = False,
|
|
174
|
+
limit: int = sys.maxsize,
|
|
175
|
+
timeout: Optional[float] = None,
|
|
176
|
+
credentials: Optional[grpc.CallCredentials] = None,
|
|
177
|
+
) -> SpannerResponse[Sequence[RecordedEvent]]:
|
|
178
|
+
pass # pragma: no cover
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def span_get_stream(
|
|
182
|
+
tracer: Tracer,
|
|
183
|
+
instance: BaseKurrentDBClient,
|
|
184
|
+
spanned_func: Union[GetStreamMethod, AsyncGetStreamMethod],
|
|
185
|
+
/,
|
|
186
|
+
stream_name: str,
|
|
187
|
+
*,
|
|
188
|
+
stream_position: Optional[int] = None,
|
|
189
|
+
backwards: bool = False,
|
|
190
|
+
resolve_links: bool = False,
|
|
191
|
+
limit: int = sys.maxsize,
|
|
192
|
+
timeout: Optional[float] = None,
|
|
193
|
+
credentials: Optional[grpc.CallCredentials] = None,
|
|
194
|
+
) -> OverloadedSpannerResponse[Sequence[RecordedEvent], Sequence[RecordedEvent]]:
|
|
195
|
+
span_name, span_kind = _get_span_name_and_kind(spanned_func)
|
|
196
|
+
|
|
197
|
+
with _start_span(tracer, span_name, span_kind) as span:
|
|
198
|
+
_enrich_span(
|
|
199
|
+
span=span,
|
|
200
|
+
client=instance,
|
|
201
|
+
db_operation_name=span_name,
|
|
202
|
+
stream_name=stream_name,
|
|
203
|
+
)
|
|
204
|
+
try:
|
|
205
|
+
yield spanned_func(
|
|
206
|
+
stream_name,
|
|
207
|
+
stream_position=stream_position,
|
|
208
|
+
backwards=backwards,
|
|
209
|
+
resolve_links=resolve_links,
|
|
210
|
+
limit=limit,
|
|
211
|
+
timeout=timeout,
|
|
212
|
+
credentials=credentials,
|
|
213
|
+
)
|
|
214
|
+
except Exception as e:
|
|
215
|
+
_set_span_error(span, e)
|
|
216
|
+
raise
|
|
217
|
+
else:
|
|
218
|
+
_set_span_ok(span)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class ReadStreamMethod(Protocol):
|
|
222
|
+
def __call__(
|
|
223
|
+
self,
|
|
224
|
+
/,
|
|
225
|
+
stream_name: str,
|
|
226
|
+
*args: Any,
|
|
227
|
+
**kwargs: Any,
|
|
228
|
+
) -> AbstractReadResponse:
|
|
229
|
+
pass # pragma: no cover
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
class AsyncReadStreamMethod(Protocol):
|
|
233
|
+
async def __call__(
|
|
234
|
+
self,
|
|
235
|
+
/,
|
|
236
|
+
stream_name: str,
|
|
237
|
+
*args: Any,
|
|
238
|
+
**kwargs: Any,
|
|
239
|
+
) -> AsyncReadResponse:
|
|
240
|
+
pass # pragma: no cover
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
@overload
|
|
244
|
+
def span_read_stream(
|
|
245
|
+
tracer: Tracer,
|
|
246
|
+
instance: BaseKurrentDBClient,
|
|
247
|
+
spanned_func: AsyncReadStreamMethod,
|
|
248
|
+
/,
|
|
249
|
+
stream_name: str,
|
|
250
|
+
*args: Any,
|
|
251
|
+
**kwargs: Any,
|
|
252
|
+
) -> AsyncSpannerResponse[AsyncReadResponse]:
|
|
253
|
+
pass # pragma: no cover
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
@overload
|
|
257
|
+
def span_read_stream(
|
|
258
|
+
tracer: Tracer,
|
|
259
|
+
instance: BaseKurrentDBClient,
|
|
260
|
+
spanned_func: ReadStreamMethod,
|
|
261
|
+
/,
|
|
262
|
+
stream_name: str,
|
|
263
|
+
*args: Any,
|
|
264
|
+
**kwargs: Any,
|
|
265
|
+
) -> SpannerResponse[AbstractReadResponse]:
|
|
266
|
+
pass # pragma: no cover
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def span_read_stream(
|
|
270
|
+
tracer: Tracer,
|
|
271
|
+
instance: BaseKurrentDBClient,
|
|
272
|
+
spanned_func: Union[ReadStreamMethod, AsyncReadStreamMethod],
|
|
273
|
+
/,
|
|
274
|
+
stream_name: str,
|
|
275
|
+
*args: Any,
|
|
276
|
+
**kwargs: Any,
|
|
277
|
+
) -> OverloadedSpannerResponse[AbstractReadResponse, AsyncReadResponse]:
|
|
278
|
+
span_name, span_kind = _get_span_name_and_kind(spanned_func)
|
|
279
|
+
with _start_span(tracer, span_name, span_kind) as span:
|
|
280
|
+
|
|
281
|
+
_enrich_span(
|
|
282
|
+
span=span,
|
|
283
|
+
client=instance,
|
|
284
|
+
db_operation_name=span_name,
|
|
285
|
+
stream_name=stream_name,
|
|
286
|
+
)
|
|
287
|
+
try:
|
|
288
|
+
response = spanned_func(stream_name, *args, **kwargs)
|
|
289
|
+
if inspect.iscoroutine(response):
|
|
290
|
+
|
|
291
|
+
async def wrap_response() -> AsyncReadResponse:
|
|
292
|
+
return cast(
|
|
293
|
+
AsyncReadResponse,
|
|
294
|
+
TracedAsyncReadResponse(
|
|
295
|
+
client=instance,
|
|
296
|
+
response=await response,
|
|
297
|
+
tracer=tracer,
|
|
298
|
+
span_name=span_name,
|
|
299
|
+
span_kind=span_kind,
|
|
300
|
+
),
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
yield wrap_response()
|
|
304
|
+
else:
|
|
305
|
+
# Because TypeGuard doesn't do type narrowing in negative case.
|
|
306
|
+
assert isinstance(response, ReadResponse)
|
|
307
|
+
|
|
308
|
+
yield TracedReadResponse(
|
|
309
|
+
client=instance,
|
|
310
|
+
response=response,
|
|
311
|
+
tracer=tracer,
|
|
312
|
+
span_name=span_name,
|
|
313
|
+
span_kind=span_kind,
|
|
314
|
+
)
|
|
315
|
+
except Exception as e:
|
|
316
|
+
_set_span_error(span, e)
|
|
317
|
+
raise
|
|
318
|
+
else:
|
|
319
|
+
_set_span_ok(span)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
class AppendToStreamMethod(Protocol):
|
|
323
|
+
def __call__(
|
|
324
|
+
self,
|
|
325
|
+
/,
|
|
326
|
+
stream_name: str,
|
|
327
|
+
*,
|
|
328
|
+
current_version: Union[int, StreamState],
|
|
329
|
+
events: Union[NewEvent, Iterable[NewEvent]],
|
|
330
|
+
timeout: Optional[float] = None,
|
|
331
|
+
credentials: Optional[grpc.CallCredentials] = None,
|
|
332
|
+
) -> int:
|
|
333
|
+
pass # pragma: no cover
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
class AsyncAppendToStreamMethod(Protocol):
|
|
337
|
+
async def __call__(
|
|
338
|
+
self,
|
|
339
|
+
/,
|
|
340
|
+
stream_name: str,
|
|
341
|
+
*,
|
|
342
|
+
current_version: Union[int, StreamState],
|
|
343
|
+
events: Union[NewEvent, Iterable[NewEvent]],
|
|
344
|
+
timeout: Optional[float] = None,
|
|
345
|
+
credentials: Optional[grpc.CallCredentials] = None,
|
|
346
|
+
) -> int:
|
|
347
|
+
pass # pragma: no cover
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
@overload
|
|
351
|
+
def span_append_to_stream(
|
|
352
|
+
tracer: Tracer,
|
|
353
|
+
instance: BaseKurrentDBClient,
|
|
354
|
+
spanned_func: AsyncAppendToStreamMethod,
|
|
355
|
+
/,
|
|
356
|
+
stream_name: str,
|
|
357
|
+
*,
|
|
358
|
+
current_version: Union[int, StreamState],
|
|
359
|
+
events: Union[NewEvent, Iterable[NewEvent]],
|
|
360
|
+
timeout: Optional[float] = None,
|
|
361
|
+
credentials: Optional[grpc.CallCredentials] = None,
|
|
362
|
+
) -> AsyncSpannerResponse[int]:
|
|
363
|
+
pass # pragma: no cover
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
@overload
|
|
367
|
+
def span_append_to_stream(
|
|
368
|
+
tracer: Tracer,
|
|
369
|
+
instance: BaseKurrentDBClient,
|
|
370
|
+
spanned_func: AppendToStreamMethod,
|
|
371
|
+
/,
|
|
372
|
+
stream_name: str,
|
|
373
|
+
*,
|
|
374
|
+
current_version: Union[int, StreamState],
|
|
375
|
+
events: Union[NewEvent, Iterable[NewEvent]],
|
|
376
|
+
timeout: Optional[float] = None,
|
|
377
|
+
credentials: Optional[grpc.CallCredentials] = None,
|
|
378
|
+
) -> SpannerResponse[int]:
|
|
379
|
+
pass # pragma: no cover
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def span_append_to_stream(
|
|
383
|
+
tracer: Tracer,
|
|
384
|
+
instance: BaseKurrentDBClient,
|
|
385
|
+
spanned_func: Union[AppendToStreamMethod, AsyncAppendToStreamMethod],
|
|
386
|
+
/,
|
|
387
|
+
stream_name: str,
|
|
388
|
+
*,
|
|
389
|
+
current_version: Union[int, StreamState],
|
|
390
|
+
events: Union[NewEvent, Iterable[NewEvent]],
|
|
391
|
+
timeout: Optional[float] = None,
|
|
392
|
+
credentials: Optional[grpc.CallCredentials] = None,
|
|
393
|
+
) -> OverloadedSpannerResponse[int, int]:
|
|
394
|
+
|
|
395
|
+
span_name, span_kind = _get_span_name_and_kind(spanned_func)
|
|
396
|
+
|
|
397
|
+
with _start_span(tracer, span_name, span_kind) as span:
|
|
398
|
+
try:
|
|
399
|
+
_enrich_span(
|
|
400
|
+
span=span,
|
|
401
|
+
client=instance,
|
|
402
|
+
db_operation_name=span_name,
|
|
403
|
+
stream_name=stream_name,
|
|
404
|
+
)
|
|
405
|
+
events = _set_context_in_events(span.get_span_context(), events)
|
|
406
|
+
yield spanned_func(
|
|
407
|
+
stream_name,
|
|
408
|
+
current_version=current_version,
|
|
409
|
+
events=events,
|
|
410
|
+
timeout=timeout,
|
|
411
|
+
credentials=credentials,
|
|
412
|
+
)
|
|
413
|
+
except Exception as e:
|
|
414
|
+
_set_span_error(span, e)
|
|
415
|
+
raise
|
|
416
|
+
else:
|
|
417
|
+
_set_span_ok(span)
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
class CatchupSubscriptionMethod(Protocol):
|
|
421
|
+
def __call__(
|
|
422
|
+
self,
|
|
423
|
+
/,
|
|
424
|
+
*args: Any,
|
|
425
|
+
**kwargs: Any,
|
|
426
|
+
) -> AbstractCatchupSubscription:
|
|
427
|
+
pass # pragma: no cover
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
class AsyncCatchupSubscriptionMethod(Protocol):
|
|
431
|
+
async def __call__(
|
|
432
|
+
self,
|
|
433
|
+
/,
|
|
434
|
+
*args: Any,
|
|
435
|
+
**kwargs: Any,
|
|
436
|
+
) -> AbstractAsyncCatchupSubscription:
|
|
437
|
+
pass # pragma: no cover
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
@overload
|
|
441
|
+
def span_catchup_subscription(
|
|
442
|
+
tracer: Tracer,
|
|
443
|
+
instance: BaseKurrentDBClient,
|
|
444
|
+
spanned_func: AsyncCatchupSubscriptionMethod,
|
|
445
|
+
/,
|
|
446
|
+
*args: Any,
|
|
447
|
+
**kwargs: Any,
|
|
448
|
+
) -> AsyncSpannerResponse[AbstractAsyncCatchupSubscription]:
|
|
449
|
+
pass # pragma: no cover
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
@overload
|
|
453
|
+
def span_catchup_subscription(
|
|
454
|
+
tracer: Tracer,
|
|
455
|
+
instance: BaseKurrentDBClient,
|
|
456
|
+
spanned_func: CatchupSubscriptionMethod,
|
|
457
|
+
/,
|
|
458
|
+
*args: Any,
|
|
459
|
+
**kwargs: Any,
|
|
460
|
+
) -> SpannerResponse[AbstractCatchupSubscription]:
|
|
461
|
+
pass # pragma: no cover
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def span_catchup_subscription(
|
|
465
|
+
tracer: Tracer,
|
|
466
|
+
instance: BaseKurrentDBClient,
|
|
467
|
+
spanned_func: Union[CatchupSubscriptionMethod, AsyncCatchupSubscriptionMethod],
|
|
468
|
+
/,
|
|
469
|
+
*args: Any,
|
|
470
|
+
**kwargs: Any,
|
|
471
|
+
) -> OverloadedSpannerResponse[
|
|
472
|
+
AbstractCatchupSubscription, AbstractAsyncCatchupSubscription
|
|
473
|
+
]:
|
|
474
|
+
span_name, span_kind = _get_span_name_and_kind(spanned_func)
|
|
475
|
+
try:
|
|
476
|
+
response = spanned_func(*args, **kwargs)
|
|
477
|
+
if inspect.isawaitable(response):
|
|
478
|
+
|
|
479
|
+
async def wrap_response() -> AbstractAsyncCatchupSubscription:
|
|
480
|
+
return TracedAsyncCatchupSubscription(
|
|
481
|
+
client=instance,
|
|
482
|
+
response=await response,
|
|
483
|
+
tracer=tracer,
|
|
484
|
+
span_name=span_name,
|
|
485
|
+
span_kind=span_kind,
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
yield wrap_response()
|
|
489
|
+
else:
|
|
490
|
+
# Because TypeGuard doesn't do type narrowing in negative case.
|
|
491
|
+
assert isinstance(response, AbstractCatchupSubscription)
|
|
492
|
+
|
|
493
|
+
yield TracedCatchupSubscription(
|
|
494
|
+
client=instance,
|
|
495
|
+
response=response,
|
|
496
|
+
tracer=tracer,
|
|
497
|
+
span_name=span_name,
|
|
498
|
+
span_kind=span_kind,
|
|
499
|
+
)
|
|
500
|
+
except Exception as e:
|
|
501
|
+
with _start_span(tracer, span_name, span_kind) as span:
|
|
502
|
+
_enrich_span(
|
|
503
|
+
span=span,
|
|
504
|
+
client=instance,
|
|
505
|
+
db_operation_name=span_name,
|
|
506
|
+
)
|
|
507
|
+
_set_span_error(span, e)
|
|
508
|
+
raise
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
class ReadPersistentSubscriptionMethod(Protocol):
|
|
512
|
+
def __call__(
|
|
513
|
+
self,
|
|
514
|
+
/,
|
|
515
|
+
*args: Any,
|
|
516
|
+
**kwargs: Any,
|
|
517
|
+
) -> AbstractPersistentSubscription:
|
|
518
|
+
pass # pragma: no cover
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
class AsyncReadPersistentSubscriptionMethod(Protocol):
|
|
522
|
+
async def __call__(
|
|
523
|
+
self,
|
|
524
|
+
/,
|
|
525
|
+
*args: Any,
|
|
526
|
+
**kwargs: Any,
|
|
527
|
+
) -> AbstractAsyncPersistentSubscription:
|
|
528
|
+
pass # pragma: no cover
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
@overload
|
|
532
|
+
def span_persistent_subscription(
|
|
533
|
+
tracer: Tracer,
|
|
534
|
+
instance: BaseKurrentDBClient,
|
|
535
|
+
spanned_func: AsyncReadPersistentSubscriptionMethod,
|
|
536
|
+
/,
|
|
537
|
+
*args: Any,
|
|
538
|
+
**kwargs: Any,
|
|
539
|
+
) -> AsyncSpannerResponse[AbstractAsyncPersistentSubscription]:
|
|
540
|
+
pass # pragma: no cover
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
@overload
|
|
544
|
+
def span_persistent_subscription(
|
|
545
|
+
tracer: Tracer,
|
|
546
|
+
instance: BaseKurrentDBClient,
|
|
547
|
+
spanned_func: ReadPersistentSubscriptionMethod,
|
|
548
|
+
/,
|
|
549
|
+
*args: Any,
|
|
550
|
+
**kwargs: Any,
|
|
551
|
+
) -> SpannerResponse[AbstractPersistentSubscription]:
|
|
552
|
+
pass # pragma: no cover
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
def span_persistent_subscription(
|
|
556
|
+
tracer: Tracer,
|
|
557
|
+
instance: BaseKurrentDBClient,
|
|
558
|
+
spanned_func: Union[
|
|
559
|
+
ReadPersistentSubscriptionMethod, AsyncReadPersistentSubscriptionMethod
|
|
560
|
+
],
|
|
561
|
+
/,
|
|
562
|
+
*args: Any,
|
|
563
|
+
**kwargs: Any,
|
|
564
|
+
) -> OverloadedSpannerResponse[
|
|
565
|
+
AbstractPersistentSubscription, AbstractAsyncPersistentSubscription
|
|
566
|
+
]:
|
|
567
|
+
span_name, span_kind = _get_span_name_and_kind(spanned_func)
|
|
568
|
+
try:
|
|
569
|
+
response = spanned_func(*args, **kwargs)
|
|
570
|
+
if inspect.isawaitable(response):
|
|
571
|
+
|
|
572
|
+
async def wrap_response() -> AbstractAsyncPersistentSubscription:
|
|
573
|
+
return TracedAsyncPersistentSubscription(
|
|
574
|
+
client=instance,
|
|
575
|
+
response=await response,
|
|
576
|
+
tracer=tracer,
|
|
577
|
+
span_name=span_name,
|
|
578
|
+
span_kind=span_kind,
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
yield wrap_response()
|
|
582
|
+
|
|
583
|
+
else:
|
|
584
|
+
# Because TypeGuard doesn't do type narrowing in negative case.
|
|
585
|
+
assert isinstance(response, AbstractPersistentSubscription)
|
|
586
|
+
|
|
587
|
+
yield TracedPersistentSubscription(
|
|
588
|
+
client=instance,
|
|
589
|
+
response=response,
|
|
590
|
+
tracer=tracer,
|
|
591
|
+
span_name=span_name,
|
|
592
|
+
span_kind=span_kind,
|
|
593
|
+
)
|
|
594
|
+
|
|
595
|
+
except Exception as e:
|
|
596
|
+
with _start_span(tracer, span_name, span_kind) as span:
|
|
597
|
+
_enrich_span(
|
|
598
|
+
span=span,
|
|
599
|
+
client=instance,
|
|
600
|
+
db_operation_name=span_name,
|
|
601
|
+
)
|
|
602
|
+
_set_span_error(span, e)
|
|
603
|
+
raise
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
TRecordedEventIterator = TypeVar("TRecordedEventIterator", bound=RecordedEventIterator)
|
|
607
|
+
|
|
608
|
+
TRecordedEventSubscription = TypeVar(
|
|
609
|
+
"TRecordedEventSubscription", bound=RecordedEventSubscription
|
|
610
|
+
)
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
class TracedRecordedEventIterator(
|
|
614
|
+
RecordedEventIterator, Generic[TRecordedEventIterator]
|
|
615
|
+
):
|
|
616
|
+
def __init__(
|
|
617
|
+
self,
|
|
618
|
+
*,
|
|
619
|
+
client: BaseKurrentDBClient,
|
|
620
|
+
response: TRecordedEventIterator,
|
|
621
|
+
tracer: Tracer,
|
|
622
|
+
span_name: str,
|
|
623
|
+
span_kind: SpanKind,
|
|
624
|
+
) -> None:
|
|
625
|
+
self.client = client
|
|
626
|
+
self.response = response
|
|
627
|
+
self.tracer = tracer
|
|
628
|
+
self.span_name = span_name
|
|
629
|
+
self.span_kind = span_kind
|
|
630
|
+
self._current_span: Optional[Span] = None
|
|
631
|
+
|
|
632
|
+
# self.iterator_span: Optional[Span] = None
|
|
633
|
+
# self.iterator_context: Optional[Context] = None
|
|
634
|
+
|
|
635
|
+
# with _start_span(self.tracer, "ReadResponse", end_on_exit=False) as span:
|
|
636
|
+
# self.iterator_span = span
|
|
637
|
+
# _enrich_span(
|
|
638
|
+
# span=self.iterator_span,
|
|
639
|
+
# client=self.client,
|
|
640
|
+
# )
|
|
641
|
+
# self.iterator_context = set_span_in_context(self.iterator_span, Context())
|
|
642
|
+
|
|
643
|
+
def __next__(self) -> RecordedEvent:
|
|
644
|
+
span_name, span_kind = _get_span_name_and_kind(self.response.__next__)
|
|
645
|
+
|
|
646
|
+
with _start_span(
|
|
647
|
+
self.tracer,
|
|
648
|
+
span_name,
|
|
649
|
+
span_kind,
|
|
650
|
+
# context=self.iterator_context,
|
|
651
|
+
end_on_exit=False,
|
|
652
|
+
) as span:
|
|
653
|
+
self._current_span = span
|
|
654
|
+
try:
|
|
655
|
+
recorded_event = next(self.response)
|
|
656
|
+
except StopIteration:
|
|
657
|
+
_enrich_span(
|
|
658
|
+
span=span,
|
|
659
|
+
client=self.client,
|
|
660
|
+
db_operation_name=span_name,
|
|
661
|
+
)
|
|
662
|
+
_set_span_ok(span)
|
|
663
|
+
raise
|
|
664
|
+
except Exception as e:
|
|
665
|
+
_enrich_span(
|
|
666
|
+
span=span,
|
|
667
|
+
client=self.client,
|
|
668
|
+
db_operation_name=span_name,
|
|
669
|
+
)
|
|
670
|
+
_set_span_error(span, e)
|
|
671
|
+
# if self.iterator_span is not None:
|
|
672
|
+
# _set_span_error(self.iterator_span, e)
|
|
673
|
+
# self.iterator_span.end()
|
|
674
|
+
raise
|
|
675
|
+
else:
|
|
676
|
+
_enrich_span(
|
|
677
|
+
span=span,
|
|
678
|
+
client=self.client,
|
|
679
|
+
db_operation_name=span_name,
|
|
680
|
+
stream_name=recorded_event.stream_name,
|
|
681
|
+
event_id=str(recorded_event.id),
|
|
682
|
+
event_type=recorded_event.type,
|
|
683
|
+
)
|
|
684
|
+
_set_span_ok(span)
|
|
685
|
+
return recorded_event
|
|
686
|
+
finally:
|
|
687
|
+
span.end()
|
|
688
|
+
self._current_span = None
|
|
689
|
+
|
|
690
|
+
def stop(self) -> None:
|
|
691
|
+
self.response.stop()
|
|
692
|
+
|
|
693
|
+
def __enter__(self) -> Self:
|
|
694
|
+
self.response.__enter__()
|
|
695
|
+
return self
|
|
696
|
+
|
|
697
|
+
def __exit__(self, *args: Any, **kwargs: Any) -> None:
|
|
698
|
+
return self.response.__exit__(*args, **kwargs)
|
|
699
|
+
|
|
700
|
+
def __del__(self) -> None:
|
|
701
|
+
current_span = self._current_span
|
|
702
|
+
if current_span and current_span.is_recording(): # pragma: no cover
|
|
703
|
+
_set_span_ok(current_span)
|
|
704
|
+
current_span.end()
|
|
705
|
+
# iterator_span = self.iterator_span
|
|
706
|
+
# if iterator_span and iterator_span.is_recording():
|
|
707
|
+
# _set_span_ok(iterator_span)
|
|
708
|
+
# iterator_span.end()
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
class TracedReadResponse(
|
|
712
|
+
TracedRecordedEventIterator[AbstractReadResponse], AbstractReadResponse
|
|
713
|
+
):
|
|
714
|
+
pass
|
|
715
|
+
|
|
716
|
+
|
|
717
|
+
class TracedRecordedEventSubscription(
|
|
718
|
+
TracedRecordedEventIterator[TRecordedEventSubscription]
|
|
719
|
+
):
|
|
720
|
+
def __next__(self) -> RecordedEvent:
|
|
721
|
+
try:
|
|
722
|
+
recorded_event = next(self.response)
|
|
723
|
+
except StopIteration:
|
|
724
|
+
raise
|
|
725
|
+
except Exception as e:
|
|
726
|
+
with _start_span(self.tracer, self.span_name, self.span_kind) as span:
|
|
727
|
+
self._enrich_span(
|
|
728
|
+
span=span,
|
|
729
|
+
)
|
|
730
|
+
_set_span_error(span, e)
|
|
731
|
+
raise
|
|
732
|
+
else:
|
|
733
|
+
context = _extract_context_from_event(recorded_event)
|
|
734
|
+
|
|
735
|
+
if context is not None:
|
|
736
|
+
with _start_span(
|
|
737
|
+
self.tracer, self.span_name, self.span_kind, context=context
|
|
738
|
+
) as span:
|
|
739
|
+
self._enrich_span(
|
|
740
|
+
span=span,
|
|
741
|
+
stream_name=recorded_event.stream_name,
|
|
742
|
+
event_id=str(recorded_event.id),
|
|
743
|
+
event_type=recorded_event.type,
|
|
744
|
+
)
|
|
745
|
+
|
|
746
|
+
span.set_status(StatusCode.OK)
|
|
747
|
+
|
|
748
|
+
return recorded_event
|
|
749
|
+
else:
|
|
750
|
+
return recorded_event
|
|
751
|
+
|
|
752
|
+
def _enrich_span(
|
|
753
|
+
self,
|
|
754
|
+
*,
|
|
755
|
+
span: Span,
|
|
756
|
+
stream_name: Optional[str] = None,
|
|
757
|
+
event_id: Optional[str] = None,
|
|
758
|
+
event_type: Optional[str] = None,
|
|
759
|
+
) -> None:
|
|
760
|
+
_enrich_span(
|
|
761
|
+
span=span,
|
|
762
|
+
client=self.client,
|
|
763
|
+
db_operation_name=self.span_name,
|
|
764
|
+
stream_name=stream_name,
|
|
765
|
+
subscription_id=self.subscription_id,
|
|
766
|
+
event_id=event_id,
|
|
767
|
+
event_type=event_type,
|
|
768
|
+
)
|
|
769
|
+
|
|
770
|
+
@property
|
|
771
|
+
def subscription_id(self) -> str:
|
|
772
|
+
return self.response.subscription_id
|
|
773
|
+
|
|
774
|
+
|
|
775
|
+
class TracedCatchupSubscription(
|
|
776
|
+
TracedRecordedEventSubscription[AbstractCatchupSubscription],
|
|
777
|
+
AbstractCatchupSubscription,
|
|
778
|
+
):
|
|
779
|
+
pass
|
|
780
|
+
|
|
781
|
+
|
|
782
|
+
class TracedPersistentSubscription(
|
|
783
|
+
TracedRecordedEventSubscription[AbstractPersistentSubscription],
|
|
784
|
+
AbstractPersistentSubscription,
|
|
785
|
+
):
|
|
786
|
+
def ack(self, item: Union[UUID, RecordedEvent]) -> None:
|
|
787
|
+
self.response.ack(item)
|
|
788
|
+
|
|
789
|
+
def nack(
|
|
790
|
+
self,
|
|
791
|
+
item: Union[UUID, RecordedEvent],
|
|
792
|
+
action: Literal["unknown", "park", "retry", "skip", "stop"],
|
|
793
|
+
) -> None:
|
|
794
|
+
self.response.nack(item, action)
|
|
795
|
+
|
|
796
|
+
|
|
797
|
+
TAsyncRecordedEventIterator = TypeVar(
|
|
798
|
+
"TAsyncRecordedEventIterator", bound=AsyncRecordedEventIterator
|
|
799
|
+
)
|
|
800
|
+
|
|
801
|
+
TAsyncRecordedEventSubscription = TypeVar(
|
|
802
|
+
"TAsyncRecordedEventSubscription", bound=AsyncRecordedEventSubscription
|
|
803
|
+
)
|
|
804
|
+
|
|
805
|
+
|
|
806
|
+
class TracedAsyncRecordedEventIterator(
|
|
807
|
+
AsyncRecordedEventIterator, Generic[TAsyncRecordedEventIterator]
|
|
808
|
+
):
|
|
809
|
+
def __init__(
|
|
810
|
+
self,
|
|
811
|
+
*,
|
|
812
|
+
client: BaseKurrentDBClient,
|
|
813
|
+
response: TAsyncRecordedEventIterator,
|
|
814
|
+
tracer: Tracer,
|
|
815
|
+
span_name: str,
|
|
816
|
+
span_kind: SpanKind,
|
|
817
|
+
) -> None:
|
|
818
|
+
self.client = client
|
|
819
|
+
self.response = response
|
|
820
|
+
self.tracer = tracer
|
|
821
|
+
self.span_name = span_name
|
|
822
|
+
self.span_kind = span_kind
|
|
823
|
+
self._current_span: Optional[Span] = None
|
|
824
|
+
|
|
825
|
+
async def __anext__(self) -> RecordedEvent:
|
|
826
|
+
span_name = _get_span_name(self.response.__anext__)
|
|
827
|
+
span_kind = _get_span_kind(self.response.__anext__)
|
|
828
|
+
|
|
829
|
+
with _start_span(
|
|
830
|
+
self.tracer,
|
|
831
|
+
span_name,
|
|
832
|
+
span_kind,
|
|
833
|
+
end_on_exit=False,
|
|
834
|
+
) as span:
|
|
835
|
+
self._current_span = span
|
|
836
|
+
try:
|
|
837
|
+
recorded_event = await self.response.__anext__()
|
|
838
|
+
except StopAsyncIteration:
|
|
839
|
+
_enrich_span(
|
|
840
|
+
span=span,
|
|
841
|
+
client=self.client,
|
|
842
|
+
db_operation_name=span_name,
|
|
843
|
+
)
|
|
844
|
+
_set_span_ok(span)
|
|
845
|
+
raise
|
|
846
|
+
except Exception as e:
|
|
847
|
+
_enrich_span(
|
|
848
|
+
span=span,
|
|
849
|
+
client=self.client,
|
|
850
|
+
db_operation_name=span_name,
|
|
851
|
+
)
|
|
852
|
+
_set_span_error(span, e)
|
|
853
|
+
raise
|
|
854
|
+
else:
|
|
855
|
+
_enrich_span(
|
|
856
|
+
span=span,
|
|
857
|
+
client=self.client,
|
|
858
|
+
db_operation_name=span_name,
|
|
859
|
+
stream_name=recorded_event.stream_name,
|
|
860
|
+
event_id=str(recorded_event.id),
|
|
861
|
+
event_type=recorded_event.type,
|
|
862
|
+
)
|
|
863
|
+
_set_span_ok(span)
|
|
864
|
+
return recorded_event
|
|
865
|
+
finally:
|
|
866
|
+
span.end()
|
|
867
|
+
self._current_span = None
|
|
868
|
+
|
|
869
|
+
async def stop(self) -> None:
|
|
870
|
+
await self.response.stop()
|
|
871
|
+
|
|
872
|
+
async def __aenter__(self) -> Self:
|
|
873
|
+
await self.response.__aenter__()
|
|
874
|
+
return self
|
|
875
|
+
|
|
876
|
+
async def __aexit__(self, *args: Any, **kwargs: Any) -> None:
|
|
877
|
+
return await self.response.__aexit__(*args, **kwargs)
|
|
878
|
+
|
|
879
|
+
def _set_iter_error_for_testing(self) -> None:
|
|
880
|
+
self.response._set_iter_error_for_testing()
|
|
881
|
+
|
|
882
|
+
def __del__(self) -> None:
|
|
883
|
+
current_span = self._current_span
|
|
884
|
+
if current_span and current_span.is_recording(): # pragma: no cover
|
|
885
|
+
_set_span_ok(current_span)
|
|
886
|
+
current_span.end()
|
|
887
|
+
|
|
888
|
+
|
|
889
|
+
class TracedAsyncReadResponse(
|
|
890
|
+
TracedAsyncRecordedEventIterator[AsyncReadResponse],
|
|
891
|
+
AbstractAsyncReadResponse,
|
|
892
|
+
):
|
|
893
|
+
pass
|
|
894
|
+
|
|
895
|
+
|
|
896
|
+
class TracedAsyncRecordedEventSubscription(
|
|
897
|
+
TracedAsyncRecordedEventIterator[TAsyncRecordedEventSubscription]
|
|
898
|
+
):
|
|
899
|
+
async def __anext__(self) -> RecordedEvent:
|
|
900
|
+
try:
|
|
901
|
+
recorded_event = await self.response.__anext__()
|
|
902
|
+
except StopAsyncIteration:
|
|
903
|
+
raise
|
|
904
|
+
except Exception as e:
|
|
905
|
+
with _start_span(self.tracer, self.span_name, self.span_kind) as span:
|
|
906
|
+
self._enrich_span(
|
|
907
|
+
span=span,
|
|
908
|
+
)
|
|
909
|
+
_set_span_error(span, e)
|
|
910
|
+
raise
|
|
911
|
+
else:
|
|
912
|
+
context = _extract_context_from_event(recorded_event)
|
|
913
|
+
|
|
914
|
+
if context is not None:
|
|
915
|
+
with _start_span(
|
|
916
|
+
self.tracer, self.span_name, self.span_kind, context=context
|
|
917
|
+
) as span:
|
|
918
|
+
self._enrich_span(
|
|
919
|
+
span=span,
|
|
920
|
+
stream_name=recorded_event.stream_name,
|
|
921
|
+
event_id=str(recorded_event.id),
|
|
922
|
+
event_type=recorded_event.type,
|
|
923
|
+
)
|
|
924
|
+
_set_span_ok(span)
|
|
925
|
+
return recorded_event
|
|
926
|
+
else:
|
|
927
|
+
return recorded_event
|
|
928
|
+
|
|
929
|
+
def _enrich_span(
|
|
930
|
+
self,
|
|
931
|
+
*,
|
|
932
|
+
span: Span,
|
|
933
|
+
stream_name: Optional[str] = None,
|
|
934
|
+
event_id: Optional[str] = None,
|
|
935
|
+
event_type: Optional[str] = None,
|
|
936
|
+
) -> None:
|
|
937
|
+
_enrich_span(
|
|
938
|
+
span=span,
|
|
939
|
+
client=self.client,
|
|
940
|
+
db_operation_name=self.span_name,
|
|
941
|
+
stream_name=stream_name,
|
|
942
|
+
subscription_id=self.subscription_id,
|
|
943
|
+
event_id=event_id,
|
|
944
|
+
event_type=event_type,
|
|
945
|
+
)
|
|
946
|
+
|
|
947
|
+
@property
|
|
948
|
+
def subscription_id(self) -> str:
|
|
949
|
+
return self.response.subscription_id
|
|
950
|
+
|
|
951
|
+
|
|
952
|
+
class TracedAsyncCatchupSubscription(
|
|
953
|
+
TracedAsyncRecordedEventSubscription[AbstractAsyncCatchupSubscription],
|
|
954
|
+
AbstractAsyncCatchupSubscription,
|
|
955
|
+
):
|
|
956
|
+
pass
|
|
957
|
+
|
|
958
|
+
|
|
959
|
+
class TracedAsyncPersistentSubscription(
|
|
960
|
+
TracedAsyncRecordedEventSubscription[AbstractAsyncPersistentSubscription],
|
|
961
|
+
AbstractAsyncPersistentSubscription,
|
|
962
|
+
):
|
|
963
|
+
async def ack(self, item: Union[UUID, RecordedEvent]) -> None:
|
|
964
|
+
await self.response.ack(item)
|
|
965
|
+
|
|
966
|
+
async def nack(
|
|
967
|
+
self,
|
|
968
|
+
item: Union[UUID, RecordedEvent],
|
|
969
|
+
action: Literal["unknown", "park", "retry", "skip", "stop"],
|
|
970
|
+
) -> None:
|
|
971
|
+
await self.response.nack(item, action)
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
def _enrich_span(
|
|
975
|
+
*,
|
|
976
|
+
span: Span,
|
|
977
|
+
client: BaseKurrentDBClient,
|
|
978
|
+
db_operation_name: Optional[str] = None,
|
|
979
|
+
stream_name: Optional[str] = None,
|
|
980
|
+
subscription_id: Optional[str] = None,
|
|
981
|
+
event_id: Optional[str] = None,
|
|
982
|
+
event_type: Optional[str] = None,
|
|
983
|
+
) -> None:
|
|
984
|
+
if span.is_recording():
|
|
985
|
+
|
|
986
|
+
# Gather attributes.
|
|
987
|
+
attributes: Dict[str, AttributeValue] = {}
|
|
988
|
+
|
|
989
|
+
# Gather db attributes.
|
|
990
|
+
if db_operation_name is not None:
|
|
991
|
+
attributes[Attributes.DB_OPERATION] = db_operation_name
|
|
992
|
+
attributes[Attributes.DB_SYSTEM] = "kurrentdb"
|
|
993
|
+
# Todo: Username from credentials passed in as arg (not just from URI).
|
|
994
|
+
attributes[Attributes.DB_USER] = _extract_db_user(client)
|
|
995
|
+
|
|
996
|
+
# Gather kurrentdb attributes.
|
|
997
|
+
if event_id is not None:
|
|
998
|
+
attributes[Attributes.EVENTSTOREDB_EVENT_ID] = str(event_id)
|
|
999
|
+
if event_type is not None:
|
|
1000
|
+
attributes[Attributes.EVENTSTOREDB_EVENT_TYPE] = event_type
|
|
1001
|
+
if stream_name is not None:
|
|
1002
|
+
attributes[Attributes.EVENTSTOREDB_STREAM] = stream_name
|
|
1003
|
+
if subscription_id is not None:
|
|
1004
|
+
attributes[Attributes.EVENTSTOREDB_SUBSCRIPTION_ID] = subscription_id
|
|
1005
|
+
|
|
1006
|
+
# Gather server attributes.
|
|
1007
|
+
server_address, server_port = _extract_server_address_and_port(client)
|
|
1008
|
+
attributes[Attributes.SERVER_ADDRESS] = server_address
|
|
1009
|
+
attributes[Attributes.SERVER_PORT] = server_port
|
|
1010
|
+
|
|
1011
|
+
# Set attributes on span.
|
|
1012
|
+
span.set_attributes(attributes)
|
|
1013
|
+
|
|
1014
|
+
|
|
1015
|
+
def _extract_db_user(client: BaseKurrentDBClient) -> str:
|
|
1016
|
+
return client.connection_spec.username or ""
|
|
1017
|
+
|
|
1018
|
+
|
|
1019
|
+
def _extract_server_address_and_port(client: BaseKurrentDBClient) -> Tuple[str, str]:
|
|
1020
|
+
# For "quality of life" of readers of observability platforms, try to
|
|
1021
|
+
# maintain a constant server address (when using esdb+discover with
|
|
1022
|
+
# one target only).
|
|
1023
|
+
if (
|
|
1024
|
+
client.connection_spec.scheme in URI_SCHEMES_DISCOVER
|
|
1025
|
+
and len(client.connection_spec.targets) == 1
|
|
1026
|
+
):
|
|
1027
|
+
# Signal server address as the DNS cluster name ("quality of life").
|
|
1028
|
+
server_address, server_port = client.connection_spec.targets[0].split(":")
|
|
1029
|
+
else:
|
|
1030
|
+
# Signal server address as the current connection address.
|
|
1031
|
+
server_address, server_port = client.connection_target.split(":")
|
|
1032
|
+
return server_address, server_port
|
|
1033
|
+
|
|
1034
|
+
|
|
1035
|
+
METADATA_TRACE_ID = "$traceId"
|
|
1036
|
+
METADATA_SPAN_ID = "$spanId"
|
|
1037
|
+
|
|
1038
|
+
|
|
1039
|
+
def _set_context_in_events(
|
|
1040
|
+
context: SpanContext, events: Union[NewEvent, Iterable[NewEvent]]
|
|
1041
|
+
) -> Sequence[NewEvent]:
|
|
1042
|
+
# Kind of propagate OpenTelemetry context in "standard KurrentDB" style.
|
|
1043
|
+
reconstructed_events = []
|
|
1044
|
+
if isinstance(events, NewEvent):
|
|
1045
|
+
events = [events]
|
|
1046
|
+
for event in events:
|
|
1047
|
+
if event.content_type == "application/json":
|
|
1048
|
+
try:
|
|
1049
|
+
d = json.loads((event.metadata or b"{}").decode("utf8"))
|
|
1050
|
+
d[METADATA_SPAN_ID] = _int_to_hex(context.span_id)
|
|
1051
|
+
d[METADATA_TRACE_ID] = _int_to_hex(context.trace_id)
|
|
1052
|
+
metadata = json.dumps(d).encode("utf8")
|
|
1053
|
+
except Exception:
|
|
1054
|
+
pass
|
|
1055
|
+
else:
|
|
1056
|
+
event = NewEvent(
|
|
1057
|
+
id=event.id,
|
|
1058
|
+
type=event.type,
|
|
1059
|
+
data=event.data,
|
|
1060
|
+
content_type=event.content_type,
|
|
1061
|
+
metadata=metadata,
|
|
1062
|
+
)
|
|
1063
|
+
reconstructed_events.append(event)
|
|
1064
|
+
return reconstructed_events
|
|
1065
|
+
|
|
1066
|
+
|
|
1067
|
+
def _extract_context_from_event(
|
|
1068
|
+
recorded_event: Union[NewEvent, RecordedEvent],
|
|
1069
|
+
) -> Optional[Context]:
|
|
1070
|
+
# Extract propagated OpenTelemetry context using "standard KurrentDB" style.
|
|
1071
|
+
try:
|
|
1072
|
+
m = json.loads(recorded_event.metadata.decode("utf8"))
|
|
1073
|
+
parent_span_id = _hex_to_int(m[METADATA_SPAN_ID])
|
|
1074
|
+
trace_id = _hex_to_int(m[METADATA_TRACE_ID])
|
|
1075
|
+
except Exception:
|
|
1076
|
+
context: Optional[Context] = None
|
|
1077
|
+
else:
|
|
1078
|
+
trace_flags = TraceFlags(TraceFlags.SAMPLED)
|
|
1079
|
+
span_context = SpanContext(
|
|
1080
|
+
trace_id=trace_id,
|
|
1081
|
+
span_id=parent_span_id,
|
|
1082
|
+
is_remote=True,
|
|
1083
|
+
trace_flags=trace_flags,
|
|
1084
|
+
)
|
|
1085
|
+
context = set_span_in_context(
|
|
1086
|
+
NonRecordingSpan(span_context),
|
|
1087
|
+
Context(),
|
|
1088
|
+
)
|
|
1089
|
+
return context
|
|
1090
|
+
|
|
1091
|
+
|
|
1092
|
+
def _int_to_hex(i: int) -> str:
|
|
1093
|
+
return f"{i:#x}"
|
|
1094
|
+
|
|
1095
|
+
|
|
1096
|
+
def _hex_to_int(hex_string: str) -> int:
|
|
1097
|
+
return int(hex_string, 16)
|