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.
Files changed (49) hide show
  1. kurrentdbclient/__init__.py +49 -0
  2. kurrentdbclient/asyncio_client.py +1662 -0
  3. kurrentdbclient/client.py +1914 -0
  4. kurrentdbclient/common.py +535 -0
  5. kurrentdbclient/connection.py +107 -0
  6. kurrentdbclient/connection_spec.py +371 -0
  7. kurrentdbclient/events.py +141 -0
  8. kurrentdbclient/exceptions.py +239 -0
  9. kurrentdbclient/gossip.py +104 -0
  10. kurrentdbclient/instrumentation/__init__.py +0 -0
  11. kurrentdbclient/instrumentation/opentelemetry/__init__.py +185 -0
  12. kurrentdbclient/instrumentation/opentelemetry/attributes.py +20 -0
  13. kurrentdbclient/instrumentation/opentelemetry/grpc.py +165 -0
  14. kurrentdbclient/instrumentation/opentelemetry/package.py +2 -0
  15. kurrentdbclient/instrumentation/opentelemetry/spanners.py +1097 -0
  16. kurrentdbclient/instrumentation/opentelemetry/utils.py +199 -0
  17. kurrentdbclient/instrumentation/opentelemetry/version.py +2 -0
  18. kurrentdbclient/persistent.py +1982 -0
  19. kurrentdbclient/projections.py +735 -0
  20. kurrentdbclient/protos/Grpc/cluster_pb2.py +92 -0
  21. kurrentdbclient/protos/Grpc/cluster_pb2.pyi +765 -0
  22. kurrentdbclient/protos/Grpc/cluster_pb2_grpc.py +514 -0
  23. kurrentdbclient/protos/Grpc/code_pb2.py +37 -0
  24. kurrentdbclient/protos/Grpc/code_pb2.pyi +357 -0
  25. kurrentdbclient/protos/Grpc/code_pb2_grpc.py +24 -0
  26. kurrentdbclient/protos/Grpc/gossip_pb2.py +46 -0
  27. kurrentdbclient/protos/Grpc/gossip_pb2.pyi +126 -0
  28. kurrentdbclient/protos/Grpc/gossip_pb2_grpc.py +98 -0
  29. kurrentdbclient/protos/Grpc/persistent_pb2.py +140 -0
  30. kurrentdbclient/protos/Grpc/persistent_pb2.pyi +1135 -0
  31. kurrentdbclient/protos/Grpc/persistent_pb2_grpc.py +399 -0
  32. kurrentdbclient/protos/Grpc/projections_pb2.py +99 -0
  33. kurrentdbclient/protos/Grpc/projections_pb2.pyi +558 -0
  34. kurrentdbclient/protos/Grpc/projections_pb2_grpc.py +485 -0
  35. kurrentdbclient/protos/Grpc/shared_pb2.py +62 -0
  36. kurrentdbclient/protos/Grpc/shared_pb2.pyi +218 -0
  37. kurrentdbclient/protos/Grpc/shared_pb2_grpc.py +24 -0
  38. kurrentdbclient/protos/Grpc/status_pb2.py +39 -0
  39. kurrentdbclient/protos/Grpc/status_pb2.pyi +67 -0
  40. kurrentdbclient/protos/Grpc/status_pb2_grpc.py +24 -0
  41. kurrentdbclient/protos/Grpc/streams_pb2.py +132 -0
  42. kurrentdbclient/protos/Grpc/streams_pb2.pyi +1038 -0
  43. kurrentdbclient/protos/Grpc/streams_pb2_grpc.py +269 -0
  44. kurrentdbclient/py.typed +0 -0
  45. kurrentdbclient/streams.py +1400 -0
  46. kurrentdbclient-0.3.dist-info/LICENSE +29 -0
  47. kurrentdbclient-0.3.dist-info/METADATA +3769 -0
  48. kurrentdbclient-0.3.dist-info/RECORD +49 -0
  49. kurrentdbclient-0.3.dist-info/WHEEL +4 -0
@@ -0,0 +1,1914 @@
1
+ # -*- coding: utf-8 -*-
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ import random
6
+ import sys
7
+ from abc import ABC, abstractmethod
8
+ from functools import wraps
9
+ from threading import Event, Lock
10
+ from time import sleep
11
+ from typing import (
12
+ Any,
13
+ Callable,
14
+ Dict,
15
+ Iterable,
16
+ Optional,
17
+ Sequence,
18
+ Tuple,
19
+ TypeVar,
20
+ Union,
21
+ cast,
22
+ overload,
23
+ )
24
+
25
+ import grpc
26
+ from typing_extensions import Literal
27
+
28
+ from kurrentdbclient.common import (
29
+ DEFAULT_CHECKPOINT_INTERVAL_MULTIPLIER,
30
+ DEFAULT_PERSISTENT_SUBSCRIPTION_CHECKPOINT_AFTER,
31
+ DEFAULT_PERSISTENT_SUBSCRIPTION_EVENT_BUFFER_SIZE,
32
+ DEFAULT_PERSISTENT_SUBSCRIPTION_HISTORY_BUFFER_SIZE,
33
+ DEFAULT_PERSISTENT_SUBSCRIPTION_LIVE_BUFFER_SIZE,
34
+ DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_ACK_BATCH_SIZE,
35
+ DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_ACK_DELAY,
36
+ DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_CHECKPOINT_COUNT,
37
+ DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_RETRY_COUNT,
38
+ DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_SUBSCRIBER_COUNT,
39
+ DEFAULT_PERSISTENT_SUBSCRIPTION_MESSAGE_TIMEOUT,
40
+ DEFAULT_PERSISTENT_SUBSCRIPTION_MIN_CHECKPOINT_COUNT,
41
+ DEFAULT_PERSISTENT_SUBSCRIPTION_READ_BATCH_SIZE,
42
+ DEFAULT_PERSISTENT_SUBSCRIPTION_STOPPING_GRACE,
43
+ DEFAULT_WINDOW_SIZE,
44
+ AbstractCatchupSubscription,
45
+ AbstractPersistentSubscription,
46
+ AbstractReadResponse,
47
+ BasicAuthCallCredentials,
48
+ GrpcOptions,
49
+ )
50
+ from kurrentdbclient.connection import KurrentDBConnection
51
+ from kurrentdbclient.connection_spec import (
52
+ NODE_PREFERENCE_FOLLOWER,
53
+ NODE_PREFERENCE_LEADER,
54
+ NODE_PREFERENCE_RANDOM,
55
+ NODE_PREFERENCE_REPLICA,
56
+ URI_SCHEMES_NON_DISCOVER,
57
+ ConnectionSpec,
58
+ )
59
+ from kurrentdbclient.events import NewEvent, RecordedEvent
60
+ from kurrentdbclient.exceptions import (
61
+ DiscoveryFailed,
62
+ FollowerNotFound,
63
+ GrpcError,
64
+ LeaderNotFound,
65
+ NodeIsNotLeader,
66
+ NotFound,
67
+ ReadOnlyReplicaNotFound,
68
+ ServiceUnavailable,
69
+ )
70
+ from kurrentdbclient.gossip import (
71
+ NODE_STATE_FOLLOWER,
72
+ NODE_STATE_LEADER,
73
+ NODE_STATE_REPLICA,
74
+ ClusterMember,
75
+ GossipService,
76
+ )
77
+ from kurrentdbclient.persistent import (
78
+ ConsumerStrategy,
79
+ PersistentSubscriptionsService,
80
+ SubscriptionInfo,
81
+ )
82
+ from kurrentdbclient.projections import (
83
+ ProjectionsService,
84
+ ProjectionState,
85
+ ProjectionStatistics,
86
+ )
87
+ from kurrentdbclient.streams import StreamsService, StreamState
88
+
89
+ # Matches the 'type' of "system" events.
90
+ KDB_SYSTEM_EVENTS_REGEX = r"\$.+"
91
+ # Matches the 'type' of "PersistentConfig" events.
92
+ KDB_PERSISTENT_CONFIG_EVENTS_REGEX = r"PersistentConfig\d+"
93
+
94
+ DEFAULT_EXCLUDE_FILTER = (
95
+ KDB_SYSTEM_EVENTS_REGEX,
96
+ KDB_PERSISTENT_CONFIG_EVENTS_REGEX,
97
+ )
98
+
99
+ _TCallable = TypeVar("_TCallable", bound=Callable[..., Any])
100
+
101
+
102
+ def autoreconnect(f: _TCallable) -> _TCallable:
103
+ @wraps(f)
104
+ def autoreconnect_decorator(*args: Any, **kwargs: Any) -> Any:
105
+ client = args[0]
106
+ assert isinstance(client, KurrentDBClient)
107
+ try:
108
+ return f(*args, **kwargs)
109
+
110
+ except NodeIsNotLeader as e:
111
+ if (
112
+ client.connection_spec.options.NodePreference == NODE_PREFERENCE_LEADER
113
+ and not (
114
+ client.connection_spec.scheme in URI_SCHEMES_NON_DISCOVER
115
+ and len(client.connection_spec.targets) == 1
116
+ )
117
+ ):
118
+ client.reconnect(e.leader_grpc_target)
119
+ sleep(0.1)
120
+ return f(*args, **kwargs)
121
+ else:
122
+ raise
123
+
124
+ except ValueError as e:
125
+ s = str(e)
126
+ if "Channel closed!" in s or "Cannot invoke RPC on closed channel!" in s:
127
+ client.reconnect()
128
+ sleep(0.1)
129
+ return f(*args, **kwargs)
130
+ else: # pragma: no cover
131
+ raise
132
+
133
+ except ServiceUnavailable:
134
+ client.reconnect()
135
+ sleep(0.1)
136
+ return f(*args, **kwargs)
137
+
138
+ return cast(_TCallable, autoreconnect_decorator)
139
+
140
+
141
+ def retrygrpc(f: _TCallable) -> _TCallable:
142
+ @wraps(f)
143
+ def retrygrpc_decorator(*args: Any, **kwargs: Any) -> Any:
144
+ try:
145
+ return f(*args, **kwargs)
146
+ except GrpcError:
147
+ sleep(0.1)
148
+ return f(*args, **kwargs)
149
+
150
+ return cast(_TCallable, retrygrpc_decorator)
151
+
152
+
153
+ class BaseKurrentDBClient(ABC):
154
+ def __init__(
155
+ self,
156
+ uri: Optional[str] = None,
157
+ *,
158
+ root_certificates: Optional[Union[str, bytes]] = None,
159
+ private_key: Optional[Union[str, bytes]] = None,
160
+ certificate_chain: Optional[Union[str, bytes]] = None,
161
+ ) -> None:
162
+ self._is_closed = False
163
+ self.root_certificates = (
164
+ root_certificates.encode()
165
+ if isinstance(root_certificates, str)
166
+ else root_certificates
167
+ )
168
+ self.private_key = (
169
+ private_key.encode() if isinstance(private_key, str) else private_key
170
+ )
171
+ self.certificate_chain = (
172
+ certificate_chain.encode()
173
+ if isinstance(certificate_chain, str)
174
+ else certificate_chain
175
+ )
176
+ self.connection_spec = ConnectionSpec(uri)
177
+
178
+ # Load private_key from UserKeyFile if specified
179
+ if self.connection_spec.options.UserKeyFile:
180
+ with open(self.connection_spec.options.UserKeyFile, "r+b") as f:
181
+ self.private_key = f.read()
182
+ else:
183
+ self.private_key = None
184
+
185
+ # Load certificate_chain from UserCertFile if specified
186
+ if self.connection_spec.options.UserCertFile:
187
+ with open(self.connection_spec.options.UserCertFile, "r+b") as f:
188
+ self.certificate_chain = f.read()
189
+ else:
190
+ self.certificate_chain = None
191
+
192
+ # Load root_certificates from TlsCaFile if specified
193
+ if self.connection_spec.options.TlsCaFile:
194
+ with open(self.connection_spec.options.TlsCaFile, "r+b") as f:
195
+ self.root_certificates = f.read()
196
+
197
+ self._default_deadline = self.connection_spec.options.DefaultDeadline
198
+
199
+ self.grpc_options: GrpcOptions = (
200
+ ("grpc.max_receive_message_length", 17 * 1024 * 1024),
201
+ )
202
+ if self.connection_spec.options.KeepAliveInterval is not None:
203
+ self.grpc_options += (
204
+ ("grpc.keepalive_ms", self.connection_spec.options.KeepAliveInterval),
205
+ )
206
+ if self.connection_spec.options.KeepAliveTimeout is not None:
207
+ self.grpc_options += (
208
+ (
209
+ "grpc.keepalive_timeout_ms",
210
+ self.connection_spec.options.KeepAliveTimeout,
211
+ ),
212
+ )
213
+
214
+ self._call_metadata = (
215
+ ("connection-name", self.connection_spec.options.ConnectionName),
216
+ )
217
+
218
+ self._call_credentials = self.construct_call_credentials(
219
+ self.connection_spec.username, self.connection_spec.password
220
+ )
221
+
222
+ @property
223
+ @abstractmethod
224
+ def connection_target(self) -> str:
225
+ pass # pragma: no cover
226
+
227
+ @property
228
+ def is_closed(self) -> bool:
229
+ return self._is_closed
230
+
231
+ def construct_call_credentials(
232
+ self, username: Optional[str], password: Optional[str]
233
+ ) -> Optional[grpc.CallCredentials]:
234
+ if username and password and self.connection_spec.options.Tls is True:
235
+ return grpc.metadata_call_credentials(
236
+ BasicAuthCallCredentials(username, password)
237
+ )
238
+ else:
239
+ return None
240
+
241
+ def _select_preferred_member(
242
+ self, cluster_members: Sequence[ClusterMember]
243
+ ) -> ClusterMember:
244
+ node_preference = self.connection_spec.options.NodePreference
245
+ if node_preference == NODE_PREFERENCE_LEADER:
246
+ leaders = [c for c in cluster_members if c.state == NODE_STATE_LEADER]
247
+ if len(leaders) != 1: # pragma: no cover
248
+ # Todo: Cover this with a test.
249
+ raise LeaderNotFound(f"Expected one leader, discovered {len(leaders)}")
250
+ preferred_member = leaders[0]
251
+ elif node_preference == NODE_PREFERENCE_FOLLOWER:
252
+ followers = [c for c in cluster_members if c.state == NODE_STATE_FOLLOWER]
253
+ if len(followers) == 0:
254
+ raise FollowerNotFound()
255
+ preferred_member = random.choice(followers)
256
+ elif node_preference == NODE_PREFERENCE_REPLICA:
257
+ replicas = [c for c in cluster_members if c.state == NODE_STATE_REPLICA]
258
+ if len(replicas) == 0:
259
+ raise ReadOnlyReplicaNotFound()
260
+ # Todo: Cover this with a test.
261
+ preferred_member = random.choice(replicas) # pragma: no cover
262
+ else:
263
+ assert node_preference == NODE_PREFERENCE_RANDOM
264
+ assert len(cluster_members) > 0
265
+ preferred_member = random.choice(cluster_members)
266
+ return preferred_member
267
+
268
+
269
+ class KurrentDBClient(BaseKurrentDBClient):
270
+ """
271
+ Encapsulates the KurrentDB gRPC API.
272
+ """
273
+
274
+ def __init__(
275
+ self,
276
+ uri: Optional[str] = None,
277
+ *,
278
+ root_certificates: Optional[Union[str, bytes]] = None,
279
+ private_key: Optional[Union[str, bytes]] = None,
280
+ certificate_chain: Optional[Union[str, bytes]] = None,
281
+ ) -> None:
282
+ super().__init__(
283
+ uri,
284
+ root_certificates=root_certificates,
285
+ private_key=private_key,
286
+ certificate_chain=certificate_chain,
287
+ )
288
+ self._is_reconnection_required = Event()
289
+ self._reconnection_lock = Lock()
290
+ self._connection = self._connect()
291
+
292
+ # self._batch_append_futures_lock = Lock()
293
+ # self._batch_append_futures_queue = BatchAppendFutureQueue()
294
+ # self._batch_append_thread = Thread(
295
+ # target=self._batch_append_future_result_loop, daemon=True
296
+ # )
297
+ # self._batch_append_thread.start()
298
+
299
+ @property
300
+ def connection_target(self) -> str:
301
+ return self._connection.grpc_target
302
+
303
+ @property
304
+ def streams(self) -> StreamsService:
305
+ return self._connection.streams
306
+
307
+ @property
308
+ def persistent_subscriptions(self) -> PersistentSubscriptionsService:
309
+ return self._connection.persistent_subscriptions
310
+
311
+ @property
312
+ def gossip(self) -> GossipService:
313
+ return self._connection.gossip
314
+
315
+ @property
316
+ def projections(self) -> ProjectionsService:
317
+ return self._connection.projections
318
+
319
+ def _connect(self, grpc_target: Optional[str] = None) -> KurrentDBConnection:
320
+ if grpc_target:
321
+ # Just connect to the given target.
322
+ return self._construct_esdb_connection(grpc_target)
323
+ if (
324
+ self.connection_spec.scheme in URI_SCHEMES_NON_DISCOVER
325
+ and len(self.connection_spec.targets) == 1
326
+ ):
327
+ # Just connect to the specified target.
328
+ return self._construct_esdb_connection(
329
+ grpc_target=self.connection_spec.targets[0],
330
+ )
331
+ # Discover preferred node in cluster.
332
+ return self._discover_preferred_node()
333
+
334
+ def _discover_preferred_node(self) -> KurrentDBConnection:
335
+ attempts = self.connection_spec.options.MaxDiscoverAttempts
336
+ assert attempts > 0
337
+ if self.connection_spec.scheme in URI_SCHEMES_NON_DISCOVER:
338
+ grpc_options: GrpcOptions = ()
339
+ else:
340
+ grpc_options = (("grpc.lb_policy_name", "round_robin"),)
341
+ while True:
342
+ # Attempt to discover preferred node.
343
+ try:
344
+ last_exception: Optional[Exception] = None
345
+ for grpc_target in self.connection_spec.targets:
346
+ connection = self._construct_esdb_connection(
347
+ grpc_target=grpc_target,
348
+ grpc_options=grpc_options,
349
+ )
350
+ try:
351
+ cluster_members = connection.gossip.read(
352
+ timeout=self.connection_spec.options.GossipTimeout,
353
+ metadata=self._call_metadata,
354
+ credentials=self._call_credentials,
355
+ )
356
+ except GrpcError as e:
357
+ last_exception = e
358
+ connection.close()
359
+ else:
360
+ break
361
+ else:
362
+ msg = (
363
+ "Failed to obtain cluster info from"
364
+ f" '{','.join(self.connection_spec.targets)}':"
365
+ f" {str(last_exception)}"
366
+ )
367
+ raise DiscoveryFailed(msg) from last_exception
368
+
369
+ preferred_member = self._select_preferred_member(cluster_members)
370
+
371
+ except DiscoveryFailed:
372
+ attempts -= 1
373
+ if attempts == 0:
374
+ raise
375
+ else:
376
+ sleep(self.connection_spec.options.DiscoveryInterval / 1000)
377
+ else:
378
+ break
379
+
380
+ # Maybe close connection and connect to preferred node.
381
+ if len(cluster_members) > 1: # forgive not "advertising" single node
382
+ preferred_target = f"{preferred_member.address}:{preferred_member.port}"
383
+ if preferred_target != connection.grpc_target:
384
+ connection.close()
385
+ connection = self._construct_esdb_connection(preferred_target)
386
+
387
+ return connection
388
+
389
+ def reconnect(self, grpc_target: Optional[str] = None) -> None:
390
+ self._is_reconnection_required.set()
391
+ with self._reconnection_lock:
392
+ if self._is_reconnection_required.is_set():
393
+ new_conn = self._connect(grpc_target)
394
+ old_conn, self._connection = self._connection, new_conn
395
+ old_conn.close()
396
+ self._is_reconnection_required.clear()
397
+ else: # pragma: no cover
398
+ # Todo: Test with concurrent writes to wrong node state.
399
+ pass
400
+
401
+ def _construct_esdb_connection(
402
+ self, grpc_target: str, grpc_options: GrpcOptions = ()
403
+ ) -> KurrentDBConnection:
404
+ return KurrentDBConnection(
405
+ grpc_channel=self._construct_grpc_channel(grpc_target, grpc_options),
406
+ grpc_target=grpc_target,
407
+ connection_spec=self.connection_spec,
408
+ )
409
+
410
+ def _construct_grpc_channel(
411
+ self, grpc_target: str, grpc_options: GrpcOptions = ()
412
+ ) -> grpc.Channel:
413
+ grpc_options = self.grpc_options + grpc_options
414
+ if self.connection_spec.options.Tls is True:
415
+ channel_credentials = grpc.ssl_channel_credentials(
416
+ root_certificates=self.root_certificates,
417
+ private_key=self.private_key,
418
+ certificate_chain=self.certificate_chain,
419
+ )
420
+ grpc_channel = grpc.secure_channel(
421
+ target=grpc_target,
422
+ credentials=channel_credentials,
423
+ options=grpc_options,
424
+ )
425
+ else:
426
+ grpc_channel = grpc.insecure_channel(
427
+ target=grpc_target, options=grpc_options
428
+ )
429
+ return grpc_channel
430
+
431
+ # def _batch_append_future_result_loop(self) -> None:
432
+ # # while self._channel_connectivity_state is not ChannelConnectivity.SHUTDOWN:
433
+ # credentials = None
434
+ # try:
435
+ # self.streams.batch_append_multiplexed(
436
+ # futures_queue=self._batch_append_futures_queue,
437
+ # timeout=None,
438
+ # metadata=self._call_metadata,
439
+ # credentials=credentials or self._call_credentials,
440
+ # )
441
+ # except KurrentDBClientException as e:
442
+ # self._clear_batch_append_futures_queue(e)
443
+ # else:
444
+ # self._clear_batch_append_futures_queue( # pragma: no cover
445
+ # KurrentDBClientException("Request not sent")
446
+ # )
447
+ #
448
+ # def _clear_batch_append_futures_queue(
449
+ # self, error: KurrentDBClientException
450
+ # ) -> None:
451
+ # with self._batch_append_futures_lock:
452
+ # try:
453
+ # while True:
454
+ # future = self._batch_append_futures_queue.get(block=False)
455
+ # future.set_exception(error) # pragma: no cover
456
+ # except Empty:
457
+ # pass
458
+ #
459
+ # def append_events_multiplexed(
460
+ # self,
461
+ # stream_name: str,
462
+ # *,
463
+ # current_version: Union[int, StreamState],
464
+ # events: Iterable[NewEvent],
465
+ # timeout: Optional[float] = None,
466
+ # ) -> int:
467
+ # timeout = timeout if timeout is not None else self._default_deadline
468
+ # batch_append_request = BatchAppendRequest(
469
+ # stream_name=stream_name, current_version=current_version, events=events
470
+ # )
471
+ # future = BatchAppendFuture(batch_append_request=batch_append_request)
472
+ # with self._batch_append_futures_lock:
473
+ # self._batch_append_futures_queue.put(future)
474
+ # response = future.result(timeout=timeout)
475
+ # assert isinstance(response.commit_position, int)
476
+ # return response.commit_position
477
+
478
+ @retrygrpc
479
+ @autoreconnect
480
+ def append_events(
481
+ self,
482
+ stream_name: str,
483
+ *,
484
+ current_version: Union[int, StreamState],
485
+ events: Iterable[NewEvent],
486
+ timeout: Optional[float] = None,
487
+ credentials: Optional[grpc.CallCredentials] = None,
488
+ ) -> int:
489
+ return self.append_to_stream(
490
+ stream_name=stream_name,
491
+ current_version=current_version,
492
+ events=events,
493
+ timeout=timeout,
494
+ credentials=credentials,
495
+ )
496
+
497
+ def append_event(
498
+ self,
499
+ stream_name: str,
500
+ *,
501
+ current_version: Union[int, StreamState],
502
+ event: NewEvent,
503
+ timeout: Optional[float] = None,
504
+ credentials: Optional[grpc.CallCredentials] = None,
505
+ ) -> int:
506
+ """
507
+ Appends a new event to the named stream.
508
+ """
509
+ return self.append_to_stream(
510
+ stream_name=stream_name,
511
+ current_version=current_version,
512
+ events=event,
513
+ timeout=timeout,
514
+ credentials=credentials,
515
+ )
516
+
517
+ @retrygrpc
518
+ @autoreconnect
519
+ def append_to_stream(
520
+ self,
521
+ /,
522
+ stream_name: str,
523
+ *,
524
+ current_version: Union[int, StreamState],
525
+ events: Union[NewEvent, Iterable[NewEvent]],
526
+ timeout: Optional[float] = None,
527
+ credentials: Optional[grpc.CallCredentials] = None,
528
+ ) -> int:
529
+ """
530
+ Appends a new event or a sequence of new events to the named stream.
531
+ """
532
+ timeout = timeout if timeout is not None else self._default_deadline
533
+ if isinstance(events, NewEvent):
534
+ return self.streams.append(
535
+ stream_name=stream_name,
536
+ current_version=current_version,
537
+ events=[events],
538
+ timeout=timeout,
539
+ metadata=self._call_metadata,
540
+ credentials=credentials or self._call_credentials,
541
+ )
542
+ else:
543
+ return self.streams.batch_append(
544
+ stream_name=stream_name,
545
+ current_version=current_version,
546
+ events=events,
547
+ timeout=timeout,
548
+ metadata=self._call_metadata,
549
+ credentials=credentials or self._call_credentials,
550
+ )
551
+
552
+ @retrygrpc
553
+ @autoreconnect
554
+ def delete_stream(
555
+ self,
556
+ stream_name: str,
557
+ *,
558
+ current_version: Union[int, StreamState],
559
+ timeout: Optional[float] = None,
560
+ credentials: Optional[grpc.CallCredentials] = None,
561
+ ) -> None:
562
+ # Todo: Reconsider using current_version=None to indicate "stream exists"?
563
+ timeout = timeout if timeout is not None else self._default_deadline
564
+ self.streams.delete(
565
+ stream_name=stream_name,
566
+ current_version=current_version,
567
+ timeout=timeout,
568
+ metadata=self._call_metadata,
569
+ credentials=credentials or self._call_credentials,
570
+ )
571
+
572
+ @retrygrpc
573
+ @autoreconnect
574
+ def tombstone_stream(
575
+ self,
576
+ stream_name: str,
577
+ *,
578
+ current_version: Union[int, StreamState],
579
+ timeout: Optional[float] = None,
580
+ credentials: Optional[grpc.CallCredentials] = None,
581
+ ) -> None:
582
+ timeout = timeout if timeout is not None else self._default_deadline
583
+ self.streams.tombstone(
584
+ stream_name=stream_name,
585
+ current_version=current_version,
586
+ timeout=timeout,
587
+ metadata=self._call_metadata,
588
+ credentials=credentials or self._call_credentials,
589
+ )
590
+
591
+ @retrygrpc
592
+ @autoreconnect
593
+ def get_stream(
594
+ self,
595
+ stream_name: str,
596
+ *,
597
+ stream_position: Optional[int] = None,
598
+ backwards: bool = False,
599
+ resolve_links: bool = False,
600
+ limit: int = sys.maxsize,
601
+ timeout: Optional[float] = None,
602
+ credentials: Optional[grpc.CallCredentials] = None,
603
+ ) -> Sequence[RecordedEvent]:
604
+ """
605
+ Returns a sequence of recorded events from the named stream.
606
+ """
607
+ with self.read_stream(
608
+ stream_name=stream_name,
609
+ stream_position=stream_position,
610
+ backwards=backwards,
611
+ resolve_links=resolve_links,
612
+ limit=limit,
613
+ timeout=timeout,
614
+ credentials=credentials or self._call_credentials,
615
+ ) as events:
616
+ return tuple(events)
617
+
618
+ def read_stream(
619
+ self,
620
+ stream_name: str,
621
+ *,
622
+ stream_position: Optional[int] = None,
623
+ backwards: bool = False,
624
+ resolve_links: bool = False,
625
+ limit: int = sys.maxsize,
626
+ timeout: Optional[float] = None,
627
+ credentials: Optional[grpc.CallCredentials] = None,
628
+ ) -> AbstractReadResponse:
629
+ """
630
+ Reads recorded events from the named stream.
631
+ """
632
+ return self.streams.read(
633
+ stream_name=stream_name,
634
+ stream_position=stream_position,
635
+ backwards=backwards,
636
+ resolve_links=resolve_links,
637
+ limit=limit,
638
+ timeout=timeout,
639
+ metadata=self._call_metadata,
640
+ credentials=credentials or self._call_credentials,
641
+ )
642
+
643
+ def read_all(
644
+ self,
645
+ *,
646
+ commit_position: Optional[int] = None,
647
+ backwards: bool = False,
648
+ resolve_links: bool = False,
649
+ filter_exclude: Sequence[str] = DEFAULT_EXCLUDE_FILTER,
650
+ filter_include: Sequence[str] = (),
651
+ filter_by_stream_name: bool = False,
652
+ limit: int = sys.maxsize,
653
+ timeout: Optional[float] = None,
654
+ credentials: Optional[grpc.CallCredentials] = None,
655
+ ) -> AbstractReadResponse:
656
+ """
657
+ Reads recorded events in "all streams" in the database.
658
+ """
659
+ return self.streams.read(
660
+ commit_position=commit_position,
661
+ backwards=backwards,
662
+ resolve_links=resolve_links,
663
+ filter_exclude=filter_exclude,
664
+ filter_include=filter_include,
665
+ filter_by_stream_name=filter_by_stream_name,
666
+ limit=limit,
667
+ timeout=timeout,
668
+ metadata=self._call_metadata,
669
+ credentials=credentials or self._call_credentials,
670
+ )
671
+
672
+ @retrygrpc
673
+ @autoreconnect
674
+ def get_current_version(
675
+ self,
676
+ stream_name: str,
677
+ *,
678
+ timeout: Optional[float] = None,
679
+ credentials: Optional[grpc.CallCredentials] = None,
680
+ ) -> Union[int, Literal[StreamState.NO_STREAM]]:
681
+ """
682
+ Returns the current position of the end of a stream.
683
+ """
684
+ try:
685
+ last_event = list(
686
+ self.streams.read(
687
+ stream_name=stream_name,
688
+ backwards=True,
689
+ limit=1,
690
+ timeout=timeout,
691
+ metadata=self._call_metadata,
692
+ credentials=credentials or self._call_credentials,
693
+ )
694
+ )[0]
695
+ except NotFound:
696
+ # StreamState.NO_STREAM is the correct "current version" both when appending
697
+ # to a stream that never existed and when appending to a stream that has
698
+ # been deleted (in this case of a deleted stream, the "current version"
699
+ # before deletion is also correct).
700
+ return StreamState.NO_STREAM
701
+ else:
702
+ return last_event.stream_position
703
+
704
+ @retrygrpc
705
+ @autoreconnect
706
+ def get_commit_position(
707
+ self,
708
+ *,
709
+ filter_exclude: Sequence[str] = DEFAULT_EXCLUDE_FILTER,
710
+ filter_include: Sequence[str] = (),
711
+ filter_by_stream_name: bool = False,
712
+ timeout: Optional[float] = None,
713
+ credentials: Optional[grpc.CallCredentials] = None,
714
+ ) -> int:
715
+ """
716
+ Returns the current commit position of the database.
717
+ """
718
+ recorded_events = self.streams.read(
719
+ backwards=True,
720
+ filter_exclude=filter_exclude,
721
+ filter_include=filter_include,
722
+ filter_by_stream_name=filter_by_stream_name,
723
+ limit=1,
724
+ timeout=timeout,
725
+ metadata=self._call_metadata,
726
+ credentials=credentials or self._call_credentials,
727
+ )
728
+ for ev in recorded_events:
729
+ assert ev.commit_position is not None
730
+ commit_position = ev.commit_position
731
+ break
732
+ else:
733
+ commit_position = 0
734
+ return commit_position
735
+
736
+ @retrygrpc
737
+ @autoreconnect
738
+ def get_stream_metadata(
739
+ self,
740
+ stream_name: str,
741
+ *,
742
+ timeout: Optional[float] = None,
743
+ credentials: Optional[grpc.CallCredentials] = None,
744
+ ) -> Tuple[Dict[str, Any], Union[int, Literal[StreamState.NO_STREAM]]]:
745
+ """
746
+ Gets the stream metadata.
747
+ """
748
+ metadata_stream_name = f"$${stream_name}"
749
+ try:
750
+ metadata_events = self.get_stream(
751
+ stream_name=metadata_stream_name,
752
+ backwards=True,
753
+ limit=1,
754
+ timeout=timeout,
755
+ credentials=credentials or self._call_credentials,
756
+ )
757
+ except NotFound:
758
+ return {}, StreamState.NO_STREAM
759
+ else:
760
+ metadata_event = metadata_events[0]
761
+ return json.loads(metadata_event.data), metadata_event.stream_position
762
+
763
+ def set_stream_metadata(
764
+ self,
765
+ stream_name: str,
766
+ *,
767
+ metadata: Dict[str, Any],
768
+ current_version: Union[int, StreamState] = StreamState.ANY,
769
+ timeout: Optional[float] = None,
770
+ credentials: Optional[grpc.CallCredentials] = None,
771
+ ) -> None:
772
+ """
773
+ Sets the stream metadata.
774
+ """
775
+ timeout = timeout if timeout is not None else self._default_deadline
776
+
777
+ metadata_stream_name = f"$${stream_name}"
778
+ metadata_event = NewEvent(
779
+ type="$metadata",
780
+ data=json.dumps(metadata).encode("utf8"),
781
+ )
782
+ self.append_event(
783
+ stream_name=metadata_stream_name,
784
+ current_version=current_version,
785
+ event=metadata_event,
786
+ timeout=timeout,
787
+ credentials=credentials or self._call_credentials,
788
+ )
789
+
790
+ @retrygrpc
791
+ @autoreconnect
792
+ def subscribe_to_all(
793
+ self,
794
+ *,
795
+ commit_position: Optional[int] = None,
796
+ from_end: bool = False,
797
+ resolve_links: bool = False,
798
+ filter_exclude: Sequence[str] = DEFAULT_EXCLUDE_FILTER,
799
+ filter_include: Sequence[str] = (),
800
+ filter_by_stream_name: bool = False,
801
+ include_checkpoints: bool = False,
802
+ window_size: int = DEFAULT_WINDOW_SIZE,
803
+ checkpoint_interval_multiplier: int = DEFAULT_CHECKPOINT_INTERVAL_MULTIPLIER,
804
+ include_caught_up: bool = False,
805
+ timeout: Optional[float] = None,
806
+ credentials: Optional[grpc.CallCredentials] = None,
807
+ ) -> AbstractCatchupSubscription:
808
+ """
809
+ Starts a catch-up subscription, from which all
810
+ recorded events in the database can be received.
811
+ """
812
+ return self.streams.read(
813
+ commit_position=commit_position,
814
+ from_end=from_end,
815
+ resolve_links=resolve_links,
816
+ filter_exclude=filter_exclude,
817
+ filter_include=filter_include,
818
+ filter_by_stream_name=filter_by_stream_name,
819
+ subscribe=True,
820
+ include_checkpoints=include_checkpoints,
821
+ window_size=window_size,
822
+ checkpoint_interval_multiplier=checkpoint_interval_multiplier,
823
+ include_caught_up=include_caught_up,
824
+ timeout=timeout,
825
+ metadata=self._call_metadata,
826
+ credentials=credentials or self._call_credentials,
827
+ )
828
+
829
+ # @overload
830
+ # def subscribe_to_stream(
831
+ # self,
832
+ # stream_name: str,
833
+ # *,
834
+ # resolve_links: bool = False,
835
+ # include_caught_up: bool = False,
836
+ # timeout: Optional[float] = None,
837
+ # credentials: Optional[grpc.CallCredentials] = None,
838
+ # ) -> CatchupSubscription:
839
+ # """
840
+ # Signature to start catch-up subscription from the start of the stream.
841
+ # """
842
+ #
843
+ # @overload
844
+ # def subscribe_to_stream(
845
+ # self,
846
+ # stream_name: str,
847
+ # *,
848
+ # stream_position: int,
849
+ # resolve_links: bool = False,
850
+ # include_caught_up: bool = False,
851
+ # timeout: Optional[float] = None,
852
+ # credentials: Optional[grpc.CallCredentials] = None,
853
+ # ) -> CatchupSubscription:
854
+ # """
855
+ # Signature to start catch-up subscription from a particular stream position.
856
+ # """
857
+ #
858
+ # @overload
859
+ # def subscribe_to_stream(
860
+ # self,
861
+ # stream_name: str,
862
+ # *,
863
+ # from_end: Literal[True] = True,
864
+ # resolve_links: bool = False,
865
+ # include_caught_up: bool = False,
866
+ # timeout: Optional[float] = None,
867
+ # credentials: Optional[grpc.CallCredentials] = None,
868
+ # ) -> CatchupSubscription:
869
+ # """
870
+ # Signature to start catch-up subscription from the end of the stream.
871
+ # """
872
+
873
+ @retrygrpc
874
+ @autoreconnect
875
+ def subscribe_to_stream(
876
+ self,
877
+ stream_name: str,
878
+ *,
879
+ stream_position: Optional[int] = None,
880
+ from_end: bool = False,
881
+ resolve_links: bool = False,
882
+ include_caught_up: bool = False,
883
+ timeout: Optional[float] = None,
884
+ credentials: Optional[grpc.CallCredentials] = None,
885
+ ) -> AbstractCatchupSubscription:
886
+ """
887
+ Starts a catch-up subscription from which
888
+ recorded events in a stream can be received.
889
+ """
890
+ return self.streams.read(
891
+ stream_name=stream_name,
892
+ stream_position=stream_position,
893
+ from_end=from_end,
894
+ resolve_links=resolve_links,
895
+ subscribe=True,
896
+ include_caught_up=include_caught_up,
897
+ timeout=timeout,
898
+ metadata=self._call_metadata,
899
+ credentials=credentials or self._call_credentials,
900
+ )
901
+
902
+ @overload
903
+ def create_subscription_to_all(
904
+ self,
905
+ group_name: str,
906
+ *,
907
+ resolve_links: bool = False,
908
+ filter_exclude: Sequence[str] = DEFAULT_EXCLUDE_FILTER,
909
+ filter_include: Sequence[str] = (),
910
+ filter_by_stream_name: bool = False,
911
+ consumer_strategy: ConsumerStrategy = "DispatchToSingle",
912
+ message_timeout: float = DEFAULT_PERSISTENT_SUBSCRIPTION_MESSAGE_TIMEOUT,
913
+ max_retry_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_RETRY_COUNT,
914
+ min_checkpoint_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MIN_CHECKPOINT_COUNT,
915
+ max_checkpoint_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_CHECKPOINT_COUNT,
916
+ checkpoint_after: float = DEFAULT_PERSISTENT_SUBSCRIPTION_CHECKPOINT_AFTER,
917
+ max_subscriber_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_SUBSCRIBER_COUNT,
918
+ live_buffer_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_LIVE_BUFFER_SIZE,
919
+ read_batch_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_READ_BATCH_SIZE,
920
+ history_buffer_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_HISTORY_BUFFER_SIZE,
921
+ extra_statistics: bool = False,
922
+ timeout: Optional[float] = None,
923
+ credentials: Optional[grpc.CallCredentials] = None,
924
+ ) -> None:
925
+ """
926
+ Signature for creating persistent subscription from start of database.
927
+ """
928
+
929
+ @overload
930
+ def create_subscription_to_all(
931
+ self,
932
+ group_name: str,
933
+ *,
934
+ commit_position: int,
935
+ resolve_links: bool = False,
936
+ filter_exclude: Sequence[str] = DEFAULT_EXCLUDE_FILTER,
937
+ filter_include: Sequence[str] = (),
938
+ filter_by_stream_name: bool = False,
939
+ consumer_strategy: ConsumerStrategy = "DispatchToSingle",
940
+ message_timeout: float = DEFAULT_PERSISTENT_SUBSCRIPTION_MESSAGE_TIMEOUT,
941
+ max_retry_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_RETRY_COUNT,
942
+ min_checkpoint_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MIN_CHECKPOINT_COUNT,
943
+ max_checkpoint_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_CHECKPOINT_COUNT,
944
+ checkpoint_after: float = DEFAULT_PERSISTENT_SUBSCRIPTION_CHECKPOINT_AFTER,
945
+ max_subscriber_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_SUBSCRIBER_COUNT,
946
+ live_buffer_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_LIVE_BUFFER_SIZE,
947
+ read_batch_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_READ_BATCH_SIZE,
948
+ history_buffer_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_HISTORY_BUFFER_SIZE,
949
+ extra_statistics: bool = False,
950
+ timeout: Optional[float] = None,
951
+ credentials: Optional[grpc.CallCredentials] = None,
952
+ ) -> None:
953
+ """
954
+ Signature for creating persistent subscription from a commit position.
955
+ """
956
+
957
+ @overload
958
+ def create_subscription_to_all(
959
+ self,
960
+ group_name: str,
961
+ *,
962
+ from_end: bool = True,
963
+ resolve_links: bool = False,
964
+ filter_exclude: Sequence[str] = DEFAULT_EXCLUDE_FILTER,
965
+ filter_include: Sequence[str] = (),
966
+ filter_by_stream_name: bool = False,
967
+ window_size: int = DEFAULT_WINDOW_SIZE,
968
+ checkpoint_interval_multiplier: int = DEFAULT_CHECKPOINT_INTERVAL_MULTIPLIER,
969
+ consumer_strategy: ConsumerStrategy = "DispatchToSingle",
970
+ message_timeout: float = DEFAULT_PERSISTENT_SUBSCRIPTION_MESSAGE_TIMEOUT,
971
+ max_retry_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_RETRY_COUNT,
972
+ min_checkpoint_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MIN_CHECKPOINT_COUNT,
973
+ max_checkpoint_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_CHECKPOINT_COUNT,
974
+ checkpoint_after: float = DEFAULT_PERSISTENT_SUBSCRIPTION_CHECKPOINT_AFTER,
975
+ max_subscriber_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_SUBSCRIBER_COUNT,
976
+ live_buffer_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_LIVE_BUFFER_SIZE,
977
+ read_batch_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_READ_BATCH_SIZE,
978
+ history_buffer_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_HISTORY_BUFFER_SIZE,
979
+ extra_statistics: bool = False,
980
+ timeout: Optional[float] = None,
981
+ credentials: Optional[grpc.CallCredentials] = None,
982
+ ) -> None:
983
+ """
984
+ Signature for creating persistent subscription from end of database.
985
+ """
986
+
987
+ @retrygrpc
988
+ @autoreconnect
989
+ def create_subscription_to_all(
990
+ self,
991
+ group_name: str,
992
+ *,
993
+ from_end: bool = False,
994
+ commit_position: Optional[int] = None,
995
+ resolve_links: bool = False,
996
+ filter_exclude: Sequence[str] = DEFAULT_EXCLUDE_FILTER,
997
+ filter_include: Sequence[str] = (),
998
+ filter_by_stream_name: bool = False,
999
+ window_size: int = DEFAULT_WINDOW_SIZE,
1000
+ checkpoint_interval_multiplier: int = DEFAULT_CHECKPOINT_INTERVAL_MULTIPLIER,
1001
+ consumer_strategy: ConsumerStrategy = "DispatchToSingle",
1002
+ message_timeout: float = DEFAULT_PERSISTENT_SUBSCRIPTION_MESSAGE_TIMEOUT,
1003
+ max_retry_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_RETRY_COUNT,
1004
+ min_checkpoint_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MIN_CHECKPOINT_COUNT,
1005
+ max_checkpoint_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_CHECKPOINT_COUNT,
1006
+ checkpoint_after: float = DEFAULT_PERSISTENT_SUBSCRIPTION_CHECKPOINT_AFTER,
1007
+ max_subscriber_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_SUBSCRIBER_COUNT,
1008
+ live_buffer_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_LIVE_BUFFER_SIZE,
1009
+ read_batch_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_READ_BATCH_SIZE,
1010
+ history_buffer_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_HISTORY_BUFFER_SIZE,
1011
+ extra_statistics: bool = False,
1012
+ timeout: Optional[float] = None,
1013
+ credentials: Optional[grpc.CallCredentials] = None,
1014
+ ) -> None:
1015
+ """
1016
+ Creates a persistent subscription on all streams.
1017
+ """
1018
+ timeout = timeout if timeout is not None else self._default_deadline
1019
+
1020
+ self.persistent_subscriptions.create(
1021
+ group_name=group_name,
1022
+ from_end=from_end,
1023
+ commit_position=commit_position,
1024
+ resolve_links=resolve_links,
1025
+ consumer_strategy=consumer_strategy,
1026
+ filter_exclude=filter_exclude,
1027
+ filter_include=filter_include,
1028
+ filter_by_stream_name=filter_by_stream_name,
1029
+ window_size=window_size,
1030
+ checkpoint_interval_multiplier=checkpoint_interval_multiplier,
1031
+ message_timeout=message_timeout,
1032
+ max_retry_count=max_retry_count,
1033
+ min_checkpoint_count=min_checkpoint_count,
1034
+ max_checkpoint_count=max_checkpoint_count,
1035
+ checkpoint_after=checkpoint_after,
1036
+ max_subscriber_count=max_subscriber_count,
1037
+ live_buffer_size=live_buffer_size,
1038
+ read_batch_size=read_batch_size,
1039
+ history_buffer_size=history_buffer_size,
1040
+ extra_statistics=extra_statistics,
1041
+ timeout=timeout,
1042
+ metadata=self._call_metadata,
1043
+ credentials=credentials or self._call_credentials,
1044
+ )
1045
+
1046
+ @overload
1047
+ def create_subscription_to_stream(
1048
+ self,
1049
+ group_name: str,
1050
+ stream_name: str,
1051
+ *,
1052
+ resolve_links: bool = False,
1053
+ consumer_strategy: ConsumerStrategy = "DispatchToSingle",
1054
+ message_timeout: float = DEFAULT_PERSISTENT_SUBSCRIPTION_MESSAGE_TIMEOUT,
1055
+ max_retry_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_RETRY_COUNT,
1056
+ min_checkpoint_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MIN_CHECKPOINT_COUNT,
1057
+ max_checkpoint_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_CHECKPOINT_COUNT,
1058
+ checkpoint_after: float = DEFAULT_PERSISTENT_SUBSCRIPTION_CHECKPOINT_AFTER,
1059
+ max_subscriber_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_SUBSCRIBER_COUNT,
1060
+ live_buffer_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_LIVE_BUFFER_SIZE,
1061
+ read_batch_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_READ_BATCH_SIZE,
1062
+ history_buffer_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_HISTORY_BUFFER_SIZE,
1063
+ extra_statistics: bool = False,
1064
+ timeout: Optional[float] = None,
1065
+ credentials: Optional[grpc.CallCredentials] = None,
1066
+ ) -> None:
1067
+ """
1068
+ Signature for creating stream subscription from start of stream.
1069
+ """
1070
+
1071
+ @overload
1072
+ def create_subscription_to_stream(
1073
+ self,
1074
+ group_name: str,
1075
+ stream_name: str,
1076
+ *,
1077
+ stream_position: int,
1078
+ resolve_links: bool = False,
1079
+ consumer_strategy: ConsumerStrategy = "DispatchToSingle",
1080
+ message_timeout: float = DEFAULT_PERSISTENT_SUBSCRIPTION_MESSAGE_TIMEOUT,
1081
+ max_retry_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_RETRY_COUNT,
1082
+ min_checkpoint_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MIN_CHECKPOINT_COUNT,
1083
+ max_checkpoint_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_CHECKPOINT_COUNT,
1084
+ checkpoint_after: float = DEFAULT_PERSISTENT_SUBSCRIPTION_CHECKPOINT_AFTER,
1085
+ max_subscriber_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_SUBSCRIBER_COUNT,
1086
+ live_buffer_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_LIVE_BUFFER_SIZE,
1087
+ read_batch_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_READ_BATCH_SIZE,
1088
+ history_buffer_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_HISTORY_BUFFER_SIZE,
1089
+ extra_statistics: bool = False,
1090
+ timeout: Optional[float] = None,
1091
+ credentials: Optional[grpc.CallCredentials] = None,
1092
+ ) -> None:
1093
+ """
1094
+ Signature for creating stream subscription from stream position.
1095
+ """
1096
+
1097
+ @overload
1098
+ def create_subscription_to_stream(
1099
+ self,
1100
+ group_name: str,
1101
+ stream_name: str,
1102
+ *,
1103
+ from_end: bool = True,
1104
+ resolve_links: bool = False,
1105
+ consumer_strategy: ConsumerStrategy = "DispatchToSingle",
1106
+ message_timeout: float = DEFAULT_PERSISTENT_SUBSCRIPTION_MESSAGE_TIMEOUT,
1107
+ max_retry_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_RETRY_COUNT,
1108
+ min_checkpoint_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MIN_CHECKPOINT_COUNT,
1109
+ max_checkpoint_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_CHECKPOINT_COUNT,
1110
+ checkpoint_after: float = DEFAULT_PERSISTENT_SUBSCRIPTION_CHECKPOINT_AFTER,
1111
+ max_subscriber_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_SUBSCRIBER_COUNT,
1112
+ live_buffer_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_LIVE_BUFFER_SIZE,
1113
+ read_batch_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_READ_BATCH_SIZE,
1114
+ history_buffer_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_HISTORY_BUFFER_SIZE,
1115
+ extra_statistics: bool = False,
1116
+ timeout: Optional[float] = None,
1117
+ credentials: Optional[grpc.CallCredentials] = None,
1118
+ ) -> None:
1119
+ """
1120
+ Signature for creating stream subscription from end of stream.
1121
+ """
1122
+
1123
+ @retrygrpc
1124
+ @autoreconnect
1125
+ def create_subscription_to_stream(
1126
+ self,
1127
+ group_name: str,
1128
+ stream_name: str,
1129
+ *,
1130
+ from_end: bool = False,
1131
+ stream_position: Optional[int] = None,
1132
+ resolve_links: bool = False,
1133
+ consumer_strategy: ConsumerStrategy = "DispatchToSingle",
1134
+ message_timeout: float = DEFAULT_PERSISTENT_SUBSCRIPTION_MESSAGE_TIMEOUT,
1135
+ max_retry_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_RETRY_COUNT,
1136
+ min_checkpoint_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MIN_CHECKPOINT_COUNT,
1137
+ max_checkpoint_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_CHECKPOINT_COUNT,
1138
+ checkpoint_after: float = DEFAULT_PERSISTENT_SUBSCRIPTION_CHECKPOINT_AFTER,
1139
+ max_subscriber_count: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_SUBSCRIBER_COUNT,
1140
+ live_buffer_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_LIVE_BUFFER_SIZE,
1141
+ read_batch_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_READ_BATCH_SIZE,
1142
+ history_buffer_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_HISTORY_BUFFER_SIZE,
1143
+ extra_statistics: bool = False,
1144
+ timeout: Optional[float] = None,
1145
+ credentials: Optional[grpc.CallCredentials] = None,
1146
+ ) -> None:
1147
+ """
1148
+ Creates a persistent subscription on one stream.
1149
+ """
1150
+ timeout = timeout if timeout is not None else self._default_deadline
1151
+
1152
+ self.persistent_subscriptions.create(
1153
+ group_name=group_name,
1154
+ stream_name=stream_name,
1155
+ from_end=from_end,
1156
+ stream_position=stream_position,
1157
+ resolve_links=resolve_links,
1158
+ consumer_strategy=consumer_strategy,
1159
+ message_timeout=message_timeout,
1160
+ max_retry_count=max_retry_count,
1161
+ min_checkpoint_count=min_checkpoint_count,
1162
+ max_checkpoint_count=max_checkpoint_count,
1163
+ checkpoint_after=checkpoint_after,
1164
+ max_subscriber_count=max_subscriber_count,
1165
+ live_buffer_size=live_buffer_size,
1166
+ read_batch_size=read_batch_size,
1167
+ history_buffer_size=history_buffer_size,
1168
+ extra_statistics=extra_statistics,
1169
+ timeout=timeout,
1170
+ metadata=self._call_metadata,
1171
+ credentials=credentials or self._call_credentials,
1172
+ )
1173
+
1174
+ @retrygrpc
1175
+ @autoreconnect
1176
+ def read_subscription_to_all(
1177
+ self,
1178
+ group_name: str,
1179
+ *,
1180
+ event_buffer_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_EVENT_BUFFER_SIZE,
1181
+ max_ack_batch_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_ACK_BATCH_SIZE,
1182
+ max_ack_delay: float = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_ACK_DELAY,
1183
+ stopping_grace: float = DEFAULT_PERSISTENT_SUBSCRIPTION_STOPPING_GRACE,
1184
+ timeout: Optional[float] = None,
1185
+ credentials: Optional[grpc.CallCredentials] = None,
1186
+ ) -> AbstractPersistentSubscription:
1187
+ """
1188
+ Reads a persistent subscription on all streams.
1189
+ """
1190
+ return self.persistent_subscriptions.read(
1191
+ group_name=group_name,
1192
+ event_buffer_size=event_buffer_size,
1193
+ max_ack_batch_size=max_ack_batch_size,
1194
+ max_ack_delay=max_ack_delay,
1195
+ stopping_grace=stopping_grace,
1196
+ timeout=timeout,
1197
+ metadata=self._call_metadata,
1198
+ credentials=credentials or self._call_credentials,
1199
+ )
1200
+
1201
+ @retrygrpc
1202
+ @autoreconnect
1203
+ def read_subscription_to_stream(
1204
+ self,
1205
+ group_name: str,
1206
+ stream_name: str,
1207
+ *,
1208
+ event_buffer_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_EVENT_BUFFER_SIZE,
1209
+ max_ack_batch_size: int = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_ACK_BATCH_SIZE,
1210
+ max_ack_delay: float = DEFAULT_PERSISTENT_SUBSCRIPTION_MAX_ACK_DELAY,
1211
+ stopping_grace: float = DEFAULT_PERSISTENT_SUBSCRIPTION_STOPPING_GRACE,
1212
+ timeout: Optional[float] = None,
1213
+ credentials: Optional[grpc.CallCredentials] = None,
1214
+ ) -> AbstractPersistentSubscription:
1215
+ """
1216
+ Reads a persistent subscription on one stream.
1217
+ """
1218
+ return self.persistent_subscriptions.read(
1219
+ group_name=group_name,
1220
+ stream_name=stream_name,
1221
+ event_buffer_size=event_buffer_size,
1222
+ max_ack_batch_size=max_ack_batch_size,
1223
+ max_ack_delay=max_ack_delay,
1224
+ stopping_grace=stopping_grace,
1225
+ timeout=timeout,
1226
+ metadata=self._call_metadata,
1227
+ credentials=credentials or self._call_credentials,
1228
+ )
1229
+
1230
+ @retrygrpc
1231
+ @autoreconnect
1232
+ def get_subscription_info(
1233
+ self,
1234
+ group_name: str,
1235
+ stream_name: Optional[str] = None,
1236
+ *,
1237
+ timeout: Optional[float] = None,
1238
+ credentials: Optional[grpc.CallCredentials] = None,
1239
+ ) -> SubscriptionInfo:
1240
+ """
1241
+ Gets info for a persistent subscription.
1242
+ """
1243
+ return self.persistent_subscriptions.get_info(
1244
+ group_name=group_name,
1245
+ stream_name=stream_name,
1246
+ timeout=timeout,
1247
+ metadata=self._call_metadata,
1248
+ credentials=credentials or self._call_credentials,
1249
+ )
1250
+
1251
+ @retrygrpc
1252
+ @autoreconnect
1253
+ def list_subscriptions(
1254
+ self,
1255
+ *,
1256
+ timeout: Optional[float] = None,
1257
+ credentials: Optional[grpc.CallCredentials] = None,
1258
+ ) -> Sequence[SubscriptionInfo]:
1259
+ """
1260
+ Lists all persistent subscriptions.
1261
+ """
1262
+ return self.persistent_subscriptions.list(
1263
+ timeout=timeout,
1264
+ metadata=self._call_metadata,
1265
+ credentials=credentials or self._call_credentials,
1266
+ )
1267
+
1268
+ @retrygrpc
1269
+ @autoreconnect
1270
+ def list_subscriptions_to_stream(
1271
+ self,
1272
+ stream_name: str,
1273
+ *,
1274
+ timeout: Optional[float] = None,
1275
+ credentials: Optional[grpc.CallCredentials] = None,
1276
+ ) -> Sequence[SubscriptionInfo]:
1277
+ """
1278
+ Lists persistent stream subscriptions.
1279
+ """
1280
+ return self.persistent_subscriptions.list(
1281
+ stream_name=stream_name,
1282
+ timeout=timeout,
1283
+ metadata=self._call_metadata,
1284
+ credentials=credentials or self._call_credentials,
1285
+ )
1286
+
1287
+ @overload
1288
+ def update_subscription_to_all(
1289
+ self,
1290
+ group_name: str,
1291
+ *,
1292
+ resolve_links: Optional[bool] = None,
1293
+ consumer_strategy: Optional[ConsumerStrategy] = None,
1294
+ message_timeout: Optional[float] = None,
1295
+ max_retry_count: Optional[int] = None,
1296
+ min_checkpoint_count: Optional[int] = None,
1297
+ max_checkpoint_count: Optional[int] = None,
1298
+ checkpoint_after: Optional[float] = None,
1299
+ max_subscriber_count: Optional[int] = None,
1300
+ live_buffer_size: Optional[int] = None,
1301
+ read_batch_size: Optional[int] = None,
1302
+ history_buffer_size: Optional[int] = None,
1303
+ extra_statistics: Optional[bool] = None,
1304
+ timeout: Optional[float] = None,
1305
+ credentials: Optional[grpc.CallCredentials] = None,
1306
+ ) -> None:
1307
+ """
1308
+ Signature for updating subscription to run from same position.
1309
+ """
1310
+
1311
+ @overload
1312
+ def update_subscription_to_all(
1313
+ self,
1314
+ group_name: str,
1315
+ *,
1316
+ from_end: Literal[False],
1317
+ resolve_links: Optional[bool] = None,
1318
+ consumer_strategy: Optional[ConsumerStrategy] = None,
1319
+ message_timeout: Optional[float] = None,
1320
+ max_retry_count: Optional[int] = None,
1321
+ min_checkpoint_count: Optional[int] = None,
1322
+ max_checkpoint_count: Optional[int] = None,
1323
+ checkpoint_after: Optional[float] = None,
1324
+ max_subscriber_count: Optional[int] = None,
1325
+ live_buffer_size: Optional[int] = None,
1326
+ read_batch_size: Optional[int] = None,
1327
+ history_buffer_size: Optional[int] = None,
1328
+ extra_statistics: Optional[bool] = None,
1329
+ timeout: Optional[float] = None,
1330
+ credentials: Optional[grpc.CallCredentials] = None,
1331
+ ) -> None:
1332
+ """
1333
+ Signature for updating subscription to run from start of database.
1334
+ """
1335
+
1336
+ @overload
1337
+ def update_subscription_to_all(
1338
+ self,
1339
+ group_name: str,
1340
+ *,
1341
+ from_end: Literal[True],
1342
+ resolve_links: Optional[bool] = None,
1343
+ consumer_strategy: Optional[ConsumerStrategy] = None,
1344
+ message_timeout: Optional[float] = None,
1345
+ max_retry_count: Optional[int] = None,
1346
+ min_checkpoint_count: Optional[int] = None,
1347
+ max_checkpoint_count: Optional[int] = None,
1348
+ checkpoint_after: Optional[float] = None,
1349
+ max_subscriber_count: Optional[int] = None,
1350
+ live_buffer_size: Optional[int] = None,
1351
+ read_batch_size: Optional[int] = None,
1352
+ history_buffer_size: Optional[int] = None,
1353
+ extra_statistics: Optional[bool] = None,
1354
+ timeout: Optional[float] = None,
1355
+ credentials: Optional[grpc.CallCredentials] = None,
1356
+ ) -> None:
1357
+ """
1358
+ Signature for updating subscription to run from end of database.
1359
+ """
1360
+
1361
+ @overload
1362
+ def update_subscription_to_all(
1363
+ self,
1364
+ group_name: str,
1365
+ *,
1366
+ commit_position: int,
1367
+ resolve_links: Optional[bool] = None,
1368
+ consumer_strategy: Optional[ConsumerStrategy] = None,
1369
+ message_timeout: Optional[float] = None,
1370
+ max_retry_count: Optional[int] = None,
1371
+ min_checkpoint_count: Optional[int] = None,
1372
+ max_checkpoint_count: Optional[int] = None,
1373
+ checkpoint_after: Optional[float] = None,
1374
+ max_subscriber_count: Optional[int] = None,
1375
+ live_buffer_size: Optional[int] = None,
1376
+ read_batch_size: Optional[int] = None,
1377
+ history_buffer_size: Optional[int] = None,
1378
+ extra_statistics: Optional[bool] = None,
1379
+ timeout: Optional[float] = None,
1380
+ credentials: Optional[grpc.CallCredentials] = None,
1381
+ ) -> None:
1382
+ """
1383
+ Signature for updating persistent subscription to run from a commit position.
1384
+ """
1385
+
1386
+ @retrygrpc
1387
+ @autoreconnect
1388
+ def update_subscription_to_all(
1389
+ self,
1390
+ group_name: str,
1391
+ *,
1392
+ from_end: Optional[bool] = None,
1393
+ commit_position: Optional[int] = None,
1394
+ resolve_links: Optional[bool] = None,
1395
+ consumer_strategy: Optional[ConsumerStrategy] = None,
1396
+ message_timeout: Optional[float] = None,
1397
+ max_retry_count: Optional[int] = None,
1398
+ min_checkpoint_count: Optional[int] = None,
1399
+ max_checkpoint_count: Optional[int] = None,
1400
+ checkpoint_after: Optional[float] = None,
1401
+ max_subscriber_count: Optional[int] = None,
1402
+ live_buffer_size: Optional[int] = None,
1403
+ read_batch_size: Optional[int] = None,
1404
+ history_buffer_size: Optional[int] = None,
1405
+ extra_statistics: Optional[bool] = None,
1406
+ timeout: Optional[float] = None,
1407
+ credentials: Optional[grpc.CallCredentials] = None,
1408
+ ) -> None:
1409
+ """
1410
+ Updates a persistent subscription on all streams.
1411
+ """
1412
+
1413
+ info = self.get_subscription_info(
1414
+ group_name=group_name, timeout=timeout, credentials=credentials
1415
+ )
1416
+ kwargs = info.update_all_kwargs(
1417
+ from_end=from_end,
1418
+ commit_position=commit_position,
1419
+ resolve_links=resolve_links,
1420
+ consumer_strategy=consumer_strategy,
1421
+ message_timeout=message_timeout,
1422
+ max_retry_count=max_retry_count,
1423
+ min_checkpoint_count=min_checkpoint_count,
1424
+ max_checkpoint_count=max_checkpoint_count,
1425
+ checkpoint_after=checkpoint_after,
1426
+ max_subscriber_count=max_subscriber_count,
1427
+ live_buffer_size=live_buffer_size,
1428
+ read_batch_size=read_batch_size,
1429
+ history_buffer_size=history_buffer_size,
1430
+ extra_statistics=extra_statistics,
1431
+ )
1432
+
1433
+ self.persistent_subscriptions.update(
1434
+ group_name=group_name,
1435
+ **kwargs,
1436
+ timeout=timeout if timeout is not None else self._default_deadline,
1437
+ metadata=self._call_metadata,
1438
+ credentials=credentials or self._call_credentials,
1439
+ )
1440
+
1441
+ @overload
1442
+ def update_subscription_to_stream(
1443
+ self,
1444
+ group_name: str,
1445
+ stream_name: str,
1446
+ *,
1447
+ resolve_links: Optional[bool] = None,
1448
+ consumer_strategy: Optional[ConsumerStrategy] = None,
1449
+ message_timeout: Optional[float] = None,
1450
+ max_retry_count: Optional[int] = None,
1451
+ min_checkpoint_count: Optional[int] = None,
1452
+ max_checkpoint_count: Optional[int] = None,
1453
+ checkpoint_after: Optional[float] = None,
1454
+ max_subscriber_count: Optional[int] = None,
1455
+ live_buffer_size: Optional[int] = None,
1456
+ read_batch_size: Optional[int] = None,
1457
+ history_buffer_size: Optional[int] = None,
1458
+ extra_statistics: Optional[bool] = None,
1459
+ timeout: Optional[float] = None,
1460
+ credentials: Optional[grpc.CallCredentials] = None,
1461
+ ) -> None:
1462
+ """
1463
+ Signature for updating subscription to run from same stream position.
1464
+ """
1465
+
1466
+ @overload
1467
+ def update_subscription_to_stream(
1468
+ self,
1469
+ group_name: str,
1470
+ stream_name: str,
1471
+ *,
1472
+ from_end: Literal[False],
1473
+ resolve_links: Optional[bool] = None,
1474
+ consumer_strategy: Optional[ConsumerStrategy] = None,
1475
+ message_timeout: Optional[float] = None,
1476
+ max_retry_count: Optional[int] = None,
1477
+ min_checkpoint_count: Optional[int] = None,
1478
+ max_checkpoint_count: Optional[int] = None,
1479
+ checkpoint_after: Optional[float] = None,
1480
+ max_subscriber_count: Optional[int] = None,
1481
+ live_buffer_size: Optional[int] = None,
1482
+ read_batch_size: Optional[int] = None,
1483
+ history_buffer_size: Optional[int] = None,
1484
+ extra_statistics: Optional[bool] = None,
1485
+ timeout: Optional[float] = None,
1486
+ credentials: Optional[grpc.CallCredentials] = None,
1487
+ ) -> None:
1488
+ """
1489
+ Signature for updating subscription to run from start of stream.
1490
+ """
1491
+
1492
+ @overload
1493
+ def update_subscription_to_stream(
1494
+ self,
1495
+ group_name: str,
1496
+ stream_name: str,
1497
+ *,
1498
+ from_end: Literal[True],
1499
+ resolve_links: Optional[bool] = None,
1500
+ consumer_strategy: Optional[ConsumerStrategy] = None,
1501
+ message_timeout: Optional[float] = None,
1502
+ max_retry_count: Optional[int] = None,
1503
+ min_checkpoint_count: Optional[int] = None,
1504
+ max_checkpoint_count: Optional[int] = None,
1505
+ checkpoint_after: Optional[float] = None,
1506
+ max_subscriber_count: Optional[int] = None,
1507
+ live_buffer_size: Optional[int] = None,
1508
+ read_batch_size: Optional[int] = None,
1509
+ history_buffer_size: Optional[int] = None,
1510
+ extra_statistics: Optional[bool] = None,
1511
+ timeout: Optional[float] = None,
1512
+ credentials: Optional[grpc.CallCredentials] = None,
1513
+ ) -> None:
1514
+ """
1515
+ Signature for updating subscription to run from end of stream.
1516
+ """
1517
+
1518
+ @overload
1519
+ def update_subscription_to_stream(
1520
+ self,
1521
+ group_name: str,
1522
+ stream_name: str,
1523
+ *,
1524
+ stream_position: int,
1525
+ resolve_links: Optional[bool] = None,
1526
+ consumer_strategy: Optional[ConsumerStrategy] = None,
1527
+ message_timeout: Optional[float] = None,
1528
+ max_retry_count: Optional[int] = None,
1529
+ min_checkpoint_count: Optional[int] = None,
1530
+ max_checkpoint_count: Optional[int] = None,
1531
+ checkpoint_after: Optional[float] = None,
1532
+ max_subscriber_count: Optional[int] = None,
1533
+ live_buffer_size: Optional[int] = None,
1534
+ read_batch_size: Optional[int] = None,
1535
+ history_buffer_size: Optional[int] = None,
1536
+ extra_statistics: Optional[bool] = None,
1537
+ timeout: Optional[float] = None,
1538
+ credentials: Optional[grpc.CallCredentials] = None,
1539
+ ) -> None:
1540
+ """
1541
+ Signature for updating subscription to run from stream position.
1542
+ """
1543
+
1544
+ @retrygrpc
1545
+ @autoreconnect
1546
+ def update_subscription_to_stream(
1547
+ self,
1548
+ group_name: str,
1549
+ stream_name: str,
1550
+ *,
1551
+ from_end: Optional[bool] = None,
1552
+ stream_position: Optional[int] = None,
1553
+ resolve_links: Optional[bool] = None,
1554
+ consumer_strategy: Optional[ConsumerStrategy] = None,
1555
+ message_timeout: Optional[float] = None,
1556
+ max_retry_count: Optional[int] = None,
1557
+ min_checkpoint_count: Optional[int] = None,
1558
+ max_checkpoint_count: Optional[int] = None,
1559
+ checkpoint_after: Optional[float] = None,
1560
+ max_subscriber_count: Optional[int] = None,
1561
+ live_buffer_size: Optional[int] = None,
1562
+ read_batch_size: Optional[int] = None,
1563
+ history_buffer_size: Optional[int] = None,
1564
+ extra_statistics: Optional[bool] = None,
1565
+ timeout: Optional[float] = None,
1566
+ credentials: Optional[grpc.CallCredentials] = None,
1567
+ ) -> None:
1568
+ """
1569
+ Updates a persistent subscription on one stream.
1570
+ """
1571
+
1572
+ info = self.get_subscription_info(
1573
+ group_name=group_name,
1574
+ stream_name=stream_name,
1575
+ timeout=timeout,
1576
+ credentials=credentials,
1577
+ )
1578
+ kwargs = info.update_stream_kwargs(
1579
+ from_end=from_end,
1580
+ stream_position=stream_position,
1581
+ resolve_links=resolve_links,
1582
+ consumer_strategy=consumer_strategy,
1583
+ message_timeout=message_timeout,
1584
+ max_retry_count=max_retry_count,
1585
+ min_checkpoint_count=min_checkpoint_count,
1586
+ max_checkpoint_count=max_checkpoint_count,
1587
+ checkpoint_after=checkpoint_after,
1588
+ max_subscriber_count=max_subscriber_count,
1589
+ live_buffer_size=live_buffer_size,
1590
+ read_batch_size=read_batch_size,
1591
+ history_buffer_size=history_buffer_size,
1592
+ extra_statistics=extra_statistics,
1593
+ )
1594
+
1595
+ self.persistent_subscriptions.update(
1596
+ group_name=group_name,
1597
+ stream_name=stream_name,
1598
+ **kwargs,
1599
+ timeout=timeout if timeout is not None else self._default_deadline,
1600
+ metadata=self._call_metadata,
1601
+ credentials=credentials or self._call_credentials,
1602
+ )
1603
+
1604
+ @retrygrpc
1605
+ @autoreconnect
1606
+ def replay_parked_events(
1607
+ self,
1608
+ group_name: str,
1609
+ stream_name: Optional[str] = None,
1610
+ *,
1611
+ timeout: Optional[float] = None,
1612
+ credentials: Optional[grpc.CallCredentials] = None,
1613
+ ) -> None:
1614
+ timeout = timeout if timeout is not None else self._default_deadline
1615
+
1616
+ self.persistent_subscriptions.replay_parked(
1617
+ group_name=group_name,
1618
+ stream_name=stream_name,
1619
+ timeout=timeout,
1620
+ metadata=self._call_metadata,
1621
+ credentials=credentials or self._call_credentials,
1622
+ )
1623
+
1624
+ @retrygrpc
1625
+ @autoreconnect
1626
+ def delete_subscription(
1627
+ self,
1628
+ group_name: str,
1629
+ stream_name: Optional[str] = None,
1630
+ *,
1631
+ timeout: Optional[float] = None,
1632
+ credentials: Optional[grpc.CallCredentials] = None,
1633
+ ) -> None:
1634
+ """
1635
+ Deletes a persistent subscription.
1636
+ """
1637
+ timeout = timeout if timeout is not None else self._default_deadline
1638
+
1639
+ self.persistent_subscriptions.delete(
1640
+ group_name=group_name,
1641
+ stream_name=stream_name,
1642
+ timeout=timeout,
1643
+ metadata=self._call_metadata,
1644
+ credentials=credentials or self._call_credentials,
1645
+ )
1646
+
1647
+ @retrygrpc
1648
+ @autoreconnect
1649
+ def read_gossip(
1650
+ self,
1651
+ *,
1652
+ timeout: Optional[float] = None,
1653
+ credentials: Optional[grpc.CallCredentials] = None,
1654
+ ) -> Sequence[ClusterMember]:
1655
+ timeout = (
1656
+ timeout
1657
+ if timeout is not None
1658
+ else self.connection_spec.options.GossipTimeout
1659
+ )
1660
+ return self.gossip.read(
1661
+ timeout=timeout,
1662
+ metadata=self._call_metadata,
1663
+ credentials=credentials or self._call_credentials,
1664
+ )
1665
+
1666
+ @retrygrpc
1667
+ @autoreconnect
1668
+ def create_projection(
1669
+ self,
1670
+ *,
1671
+ name: str,
1672
+ query: str,
1673
+ emit_enabled: bool = False,
1674
+ track_emitted_streams: bool = False,
1675
+ timeout: Optional[float] = None,
1676
+ credentials: Optional[grpc.CallCredentials] = None,
1677
+ ) -> None:
1678
+ """
1679
+ Creates a projection.
1680
+ """
1681
+ timeout = timeout if timeout is not None else self._default_deadline
1682
+
1683
+ self.projections.create(
1684
+ query=query,
1685
+ name=name,
1686
+ emit_enabled=emit_enabled,
1687
+ track_emitted_streams=track_emitted_streams,
1688
+ timeout=timeout,
1689
+ metadata=self._call_metadata,
1690
+ credentials=credentials or self._call_credentials,
1691
+ )
1692
+
1693
+ @retrygrpc
1694
+ @autoreconnect
1695
+ def update_projection(
1696
+ self,
1697
+ name: str,
1698
+ *,
1699
+ query: str,
1700
+ emit_enabled: bool = False,
1701
+ timeout: Optional[float] = None,
1702
+ credentials: Optional[grpc.CallCredentials] = None,
1703
+ ) -> None:
1704
+ """
1705
+ Updates a projection.
1706
+ """
1707
+ timeout = timeout if timeout is not None else self._default_deadline
1708
+
1709
+ self.projections.update(
1710
+ name=name,
1711
+ query=query,
1712
+ emit_enabled=emit_enabled,
1713
+ timeout=timeout,
1714
+ metadata=self._call_metadata,
1715
+ credentials=credentials or self._call_credentials,
1716
+ )
1717
+
1718
+ @retrygrpc
1719
+ @autoreconnect
1720
+ def delete_projection(
1721
+ self,
1722
+ name: str,
1723
+ *,
1724
+ delete_emitted_streams: bool = False,
1725
+ delete_state_stream: bool = False,
1726
+ delete_checkpoint_stream: bool = False,
1727
+ timeout: Optional[float] = None,
1728
+ credentials: Optional[grpc.CallCredentials] = None,
1729
+ ) -> None:
1730
+ """
1731
+ Deletes a projection.
1732
+ """
1733
+ timeout = timeout if timeout is not None else self._default_deadline
1734
+
1735
+ self.projections.delete(
1736
+ name=name,
1737
+ delete_emitted_streams=delete_emitted_streams,
1738
+ delete_state_stream=delete_state_stream,
1739
+ delete_checkpoint_stream=delete_checkpoint_stream,
1740
+ timeout=timeout,
1741
+ metadata=self._call_metadata,
1742
+ credentials=credentials or self._call_credentials,
1743
+ )
1744
+
1745
+ @retrygrpc
1746
+ @autoreconnect
1747
+ def get_projection_statistics(
1748
+ self,
1749
+ *,
1750
+ name: str,
1751
+ timeout: Optional[float] = None,
1752
+ credentials: Optional[grpc.CallCredentials] = None,
1753
+ ) -> ProjectionStatistics:
1754
+ """
1755
+ Gets projection statistics.
1756
+ """
1757
+ timeout = timeout if timeout is not None else self._default_deadline
1758
+
1759
+ return self.projections.get_statistics(
1760
+ name=name,
1761
+ timeout=timeout,
1762
+ metadata=self._call_metadata,
1763
+ credentials=credentials or self._call_credentials,
1764
+ )
1765
+
1766
+ @retrygrpc
1767
+ @autoreconnect
1768
+ def disable_projection(
1769
+ self,
1770
+ name: str,
1771
+ *,
1772
+ timeout: Optional[float] = None,
1773
+ credentials: Optional[grpc.CallCredentials] = None,
1774
+ ) -> None:
1775
+ """
1776
+ Disables a projection.
1777
+ """
1778
+ timeout = timeout if timeout is not None else self._default_deadline
1779
+
1780
+ self.projections.disable(
1781
+ name=name,
1782
+ write_checkpoint=True,
1783
+ timeout=timeout,
1784
+ metadata=self._call_metadata,
1785
+ credentials=credentials or self._call_credentials,
1786
+ )
1787
+
1788
+ @retrygrpc
1789
+ @autoreconnect
1790
+ def enable_projection(
1791
+ self,
1792
+ name: str,
1793
+ *,
1794
+ timeout: Optional[float] = None,
1795
+ credentials: Optional[grpc.CallCredentials] = None,
1796
+ ) -> None:
1797
+ """
1798
+ Disables a projection.
1799
+ """
1800
+ timeout = timeout if timeout is not None else self._default_deadline
1801
+
1802
+ self.projections.enable(
1803
+ name=name,
1804
+ timeout=timeout,
1805
+ metadata=self._call_metadata,
1806
+ credentials=credentials or self._call_credentials,
1807
+ )
1808
+
1809
+ @retrygrpc
1810
+ @autoreconnect
1811
+ def reset_projection(
1812
+ self,
1813
+ name: str,
1814
+ *,
1815
+ timeout: Optional[float] = None,
1816
+ credentials: Optional[grpc.CallCredentials] = None,
1817
+ ) -> None:
1818
+ """
1819
+ Resets a projection.
1820
+ """
1821
+ timeout = timeout if timeout is not None else self._default_deadline
1822
+
1823
+ self.projections.reset(
1824
+ name=name,
1825
+ write_checkpoint=True,
1826
+ timeout=timeout,
1827
+ metadata=self._call_metadata,
1828
+ credentials=credentials or self._call_credentials,
1829
+ )
1830
+
1831
+ @retrygrpc
1832
+ @autoreconnect
1833
+ def get_projection_state(
1834
+ self,
1835
+ name: str,
1836
+ *,
1837
+ timeout: Optional[float] = None,
1838
+ credentials: Optional[grpc.CallCredentials] = None,
1839
+ ) -> ProjectionState:
1840
+ """
1841
+ Gets projection state.
1842
+ """
1843
+ timeout = timeout if timeout is not None else self._default_deadline
1844
+
1845
+ return self.projections.get_state(
1846
+ name=name,
1847
+ partition="",
1848
+ timeout=timeout,
1849
+ metadata=self._call_metadata,
1850
+ credentials=credentials or self._call_credentials,
1851
+ )
1852
+
1853
+ # @retrygrpc
1854
+ # @autoreconnect
1855
+ # def get_projection_result(
1856
+ # self,
1857
+ # name: str,
1858
+ # *,
1859
+ # timeout: Optional[float] = None,
1860
+ # credentials: Optional[grpc.CallCredentials] = None,
1861
+ # ) -> ProjectionResult:
1862
+ # """
1863
+ # Gets projection result.
1864
+ # """
1865
+ # timeout = timeout if timeout is not None else self._default_deadline
1866
+ #
1867
+ # return self.projections.get_result(
1868
+ # name=name,
1869
+ # partition="",
1870
+ # timeout=timeout,
1871
+ # metadata=self._call_metadata,
1872
+ # credentials=credentials or self._call_credentials,
1873
+ # )
1874
+
1875
+ @retrygrpc
1876
+ @autoreconnect
1877
+ def restart_projections_subsystem(
1878
+ self,
1879
+ *,
1880
+ timeout: Optional[float] = None,
1881
+ credentials: Optional[grpc.CallCredentials] = None,
1882
+ ) -> None:
1883
+ """
1884
+ Restarts projections subsystem.
1885
+ """
1886
+ timeout = timeout if timeout is not None else self._default_deadline
1887
+
1888
+ return self.projections.restart_subsystem(
1889
+ timeout=timeout,
1890
+ metadata=self._call_metadata,
1891
+ credentials=credentials or self._call_credentials,
1892
+ )
1893
+
1894
+ def close(self) -> None:
1895
+ """
1896
+ Closes the gRPC channel.
1897
+ """
1898
+ if not self._is_closed:
1899
+ try:
1900
+ esdb_connection = self._connection
1901
+ except AttributeError:
1902
+ pass
1903
+ else:
1904
+ esdb_connection.close()
1905
+ self._is_closed = True
1906
+
1907
+ def __enter__(self) -> KurrentDBClient:
1908
+ return self
1909
+
1910
+ def __exit__(self, *args: Any, **kwargs: Any) -> None:
1911
+ self.close()
1912
+
1913
+ def __del__(self) -> None:
1914
+ self.close()