forked from ravendb/ravendb-python-client
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver_operation_executor.py
More file actions
175 lines (138 loc) Β· 6.14 KB
/
Copy pathserver_operation_executor.py
File metadata and controls
175 lines (138 loc) Β· 6.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
from __future__ import annotations
import enum
from typing import TYPE_CHECKING, TypeVar, Optional
from ravendb.http.request_executor import ClusterRequestExecutor
from ravendb.http.topology import Topology
from ravendb.serverwide.operations.common import (
GetBuildNumberOperation,
ServerOperation,
ServerWideOperation,
)
from ravendb.tools.utils import CaseInsensitiveDict
if TYPE_CHECKING:
from ravendb.documents.store.definition import DocumentStore
from ravendb.documents.operations.operation import Operation
from ravendb.documents.operations.definitions import OperationIdResult
_T_OperationResult = TypeVar("_T_OperationResult")
class ConnectionStringType(enum.Enum):
NONE = "None"
RAVEN = "Raven"
SQL = "Sql"
OLAP = "Olap"
class ServerOperationExecutor:
def __init__(
self,
store: DocumentStore,
request_executor: ClusterRequestExecutor,
initial_request_executor: ClusterRequestExecutor = None,
cache: CaseInsensitiveDict = None,
node_tag: str = None,
):
if store is None:
raise ValueError("Store cannot be None")
if request_executor is None:
raise ValueError("Request Executor cannot be None")
if cache is None:
cache = CaseInsensitiveDict()
self._store = store
self._request_executor = request_executor
self._initial_request_executor = initial_request_executor
self._node_tag = node_tag
self._cache = cache
store.register_events_for_request_executor(self._request_executor)
if self._node_tag is None:
self._store.add_after_close(lambda: self._request_executor.close())
@classmethod
def from_store(cls, store: DocumentStore):
return cls(store, cls.create_request_executor(store), None, CaseInsensitiveDict(), None)
def for_node(self, node_tag: str) -> ServerOperationExecutor:
if not node_tag or node_tag.isspace():
raise ValueError("Value cannot be None or whitespace")
if self._node_tag and self._node_tag.lower() == node_tag.lower():
return self
if self._store.conventions.disable_topology_updates:
raise RuntimeError(
"Cannot switch server operation executor, because conventions.disable_topology_updates is set to 'True'"
)
if node_tag in self._cache:
return self._cache[node_tag]
request_executor = self._initial_request_executor or self._request_executor
topology = self._get_topology(self._request_executor)
node = next((node for node in topology.nodes if node_tag.lower() == node.cluster_tag.lower()), None)
if node is None:
available_nodes = str.join(", ", [node.cluster_tag for node in topology.nodes])
raise RuntimeError(f"Could not find node '{node_tag}' in the topology. Available nodes: {available_nodes}")
cluster_executor = ClusterRequestExecutor.create_for_single_node(
node.url,
self._store.thread_pool_executor,
self._store.conventions,
self._store.certificate_pem_path,
self._store.trust_store_path,
)
return ServerOperationExecutor(self._store, cluster_executor, request_executor, self._cache, node.cluster_tag)
def send(self, operation: ServerOperation[_T_OperationResult]) -> Optional[_T_OperationResult]:
command = operation.get_command(self._request_executor.conventions)
self._request_executor.execute_command(command)
if isinstance(operation, ServerOperation):
return command.result
def send_async(self, operation: ServerOperation[OperationIdResult]) -> Operation:
command = operation.get_command(self._request_executor.conventions)
self._request_executor.execute_command(command)
return ServerWideOperation(
self._request_executor,
self._request_executor.conventions,
command.result.operation_id,
command.selected_node_tag if command.selected_node_tag else command.result.operation_node_tag,
)
def close(self) -> None:
if self._node_tag is not None:
return
if self._request_executor is not None:
self._request_executor.close()
cache = self._cache
if cache is not None:
for key, value in cache.items():
request_executor = value._request_executor
if request_executor is not None:
request_executor.close()
cache.clear()
def _get_topology(self, request_executor: ClusterRequestExecutor) -> Topology:
topology: Optional[Topology] = None
try:
topology = request_executor.topology
if topology is None:
# a bit rude way to make sure that topology was refreshed
# but it handles a case when first topology update failed
operation = GetBuildNumberOperation()
command = operation.get_command(request_executor.conventions)
request_executor.execute_command(command)
topology = request_executor.topology
except Exception:
... # ignored
if topology is None:
raise RuntimeError("Could not fetch the topology")
return topology
@staticmethod
def create_request_executor(store: DocumentStore) -> ClusterRequestExecutor:
return (
ClusterRequestExecutor.create_for_single_node(
store.urls[0],
store.thread_pool_executor,
store.conventions,
store.certificate_pem_path,
store.trust_store_path,
)
if store.conventions.disable_topology_updates
else ClusterRequestExecutor.create_without_database_name(
store.urls,
store.thread_pool_executor,
store.conventions,
store.certificate_pem_path,
store.trust_store_path,
)
)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
return