aws-advanced-python-wrapper 1.0.0__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 (70) hide show
  1. CONTRIBUTING.md +63 -0
  2. aws_advanced_python_wrapper/__init__.py +28 -0
  3. aws_advanced_python_wrapper/aurora_connection_tracker_plugin.py +228 -0
  4. aws_advanced_python_wrapper/aurora_initial_connection_strategy_plugin.py +240 -0
  5. aws_advanced_python_wrapper/aws_secrets_manager_plugin.py +218 -0
  6. aws_advanced_python_wrapper/connect_time_plugin.py +69 -0
  7. aws_advanced_python_wrapper/connection_provider.py +232 -0
  8. aws_advanced_python_wrapper/database_dialect.py +708 -0
  9. aws_advanced_python_wrapper/default_plugin.py +144 -0
  10. aws_advanced_python_wrapper/developer_plugin.py +163 -0
  11. aws_advanced_python_wrapper/driver_configuration_profiles.py +44 -0
  12. aws_advanced_python_wrapper/driver_dialect.py +165 -0
  13. aws_advanced_python_wrapper/driver_dialect_codes.py +19 -0
  14. aws_advanced_python_wrapper/driver_dialect_manager.py +121 -0
  15. aws_advanced_python_wrapper/driver_info.py +18 -0
  16. aws_advanced_python_wrapper/errors.py +47 -0
  17. aws_advanced_python_wrapper/exception_handling.py +73 -0
  18. aws_advanced_python_wrapper/execute_time_plugin.py +58 -0
  19. aws_advanced_python_wrapper/failover_plugin.py +517 -0
  20. aws_advanced_python_wrapper/failover_result.py +42 -0
  21. aws_advanced_python_wrapper/fastest_response_strategy_plugin.py +345 -0
  22. aws_advanced_python_wrapper/federated_plugin.py +382 -0
  23. aws_advanced_python_wrapper/host_availability.py +86 -0
  24. aws_advanced_python_wrapper/host_list_provider.py +645 -0
  25. aws_advanced_python_wrapper/host_monitoring_plugin.py +728 -0
  26. aws_advanced_python_wrapper/host_selector.py +190 -0
  27. aws_advanced_python_wrapper/hostinfo.py +138 -0
  28. aws_advanced_python_wrapper/iam_plugin.py +195 -0
  29. aws_advanced_python_wrapper/mysql_driver_dialect.py +175 -0
  30. aws_advanced_python_wrapper/pep249.py +196 -0
  31. aws_advanced_python_wrapper/pg_driver_dialect.py +176 -0
  32. aws_advanced_python_wrapper/plugin.py +148 -0
  33. aws_advanced_python_wrapper/plugin_service.py +949 -0
  34. aws_advanced_python_wrapper/read_write_splitting_plugin.py +363 -0
  35. aws_advanced_python_wrapper/reader_failover_handler.py +252 -0
  36. aws_advanced_python_wrapper/resources/aws_advanced_python_wrapper_messages.properties +315 -0
  37. aws_advanced_python_wrapper/sql_alchemy_connection_provider.py +196 -0
  38. aws_advanced_python_wrapper/sqlalchemy_driver_dialect.py +127 -0
  39. aws_advanced_python_wrapper/stale_dns_plugin.py +209 -0
  40. aws_advanced_python_wrapper/states/__init__.py +13 -0
  41. aws_advanced_python_wrapper/states/session_state.py +94 -0
  42. aws_advanced_python_wrapper/states/session_state_service.py +221 -0
  43. aws_advanced_python_wrapper/utils/__init__.py +13 -0
  44. aws_advanced_python_wrapper/utils/atomic.py +51 -0
  45. aws_advanced_python_wrapper/utils/cache_map.py +99 -0
  46. aws_advanced_python_wrapper/utils/concurrent.py +100 -0
  47. aws_advanced_python_wrapper/utils/decorators.py +70 -0
  48. aws_advanced_python_wrapper/utils/failover_mode.py +39 -0
  49. aws_advanced_python_wrapper/utils/iamutils.py +75 -0
  50. aws_advanced_python_wrapper/utils/log.py +75 -0
  51. aws_advanced_python_wrapper/utils/messages.py +36 -0
  52. aws_advanced_python_wrapper/utils/mysql_exception_handler.py +73 -0
  53. aws_advanced_python_wrapper/utils/notifications.py +37 -0
  54. aws_advanced_python_wrapper/utils/pg_exception_handler.py +115 -0
  55. aws_advanced_python_wrapper/utils/properties.py +492 -0
  56. aws_advanced_python_wrapper/utils/rds_url_type.py +36 -0
  57. aws_advanced_python_wrapper/utils/rdsutils.py +226 -0
  58. aws_advanced_python_wrapper/utils/sliding_expiration_cache.py +146 -0
  59. aws_advanced_python_wrapper/utils/telemetry/default_telemetry_factory.py +82 -0
  60. aws_advanced_python_wrapper/utils/telemetry/null_telemetry.py +55 -0
  61. aws_advanced_python_wrapper/utils/telemetry/open_telemetry.py +189 -0
  62. aws_advanced_python_wrapper/utils/telemetry/telemetry.py +85 -0
  63. aws_advanced_python_wrapper/utils/telemetry/xray_telemetry.py +126 -0
  64. aws_advanced_python_wrapper/utils/utils.py +89 -0
  65. aws_advanced_python_wrapper/wrapper.py +322 -0
  66. aws_advanced_python_wrapper/writer_failover_handler.py +347 -0
  67. aws_advanced_python_wrapper-1.0.0.dist-info/LICENSE +201 -0
  68. aws_advanced_python_wrapper-1.0.0.dist-info/METADATA +261 -0
  69. aws_advanced_python_wrapper-1.0.0.dist-info/RECORD +70 -0
  70. aws_advanced_python_wrapper-1.0.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,209 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License").
4
+ # You may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ import socket
18
+ from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Set
19
+
20
+ if TYPE_CHECKING:
21
+ from aws_advanced_python_wrapper.driver_dialect import DriverDialect
22
+ from aws_advanced_python_wrapper.host_list_provider import HostListProviderService
23
+ from aws_advanced_python_wrapper.hostinfo import HostInfo
24
+ from aws_advanced_python_wrapper.pep249 import Connection
25
+ from aws_advanced_python_wrapper.plugin_service import PluginService
26
+ from aws_advanced_python_wrapper.utils.properties import Properties
27
+
28
+ from aws_advanced_python_wrapper.hostinfo import HostRole
29
+ from aws_advanced_python_wrapper.plugin import Plugin, PluginFactory
30
+ from aws_advanced_python_wrapper.utils.log import Logger
31
+ from aws_advanced_python_wrapper.utils.messages import Messages
32
+ from aws_advanced_python_wrapper.utils.notifications import HostEvent
33
+ from aws_advanced_python_wrapper.utils.rdsutils import RdsUtils
34
+ from aws_advanced_python_wrapper.utils.utils import LogUtils
35
+
36
+ logger = Logger(__name__)
37
+
38
+
39
+ class StaleDnsHelper:
40
+ RETRIES: int = 3
41
+
42
+ def __init__(self, plugin_service: PluginService) -> None:
43
+ self._plugin_service = plugin_service
44
+ self._rds_helper = RdsUtils()
45
+ self._writer_host_info: Optional[HostInfo] = None
46
+ self._writer_host_address: Optional[str] = None
47
+
48
+ def get_verified_connection(self, is_initial_connection: bool, host_list_provider_service: HostListProviderService, host_info: HostInfo,
49
+ props: Properties, connect_func: Callable) -> Connection:
50
+ """
51
+ Ensure the connection created is not a stale writer connection that
52
+ :param is_initial_connection:
53
+ :param host_list_provider_service:
54
+ :param host_info:
55
+ :param props:
56
+ :param connect_func:
57
+ :return:
58
+ """
59
+ if not self._rds_helper.is_writer_cluster_dns(host_info.host):
60
+ return connect_func()
61
+
62
+ conn: Connection = connect_func()
63
+
64
+ cluster_inet_address: Optional[str] = None
65
+ try:
66
+ cluster_inet_address = socket.gethostbyname(host_info.host)
67
+ except socket.gaierror:
68
+ pass
69
+
70
+ host_inet_address: Optional[str] = cluster_inet_address
71
+
72
+ logger.debug("StaleDnsHelper.ClusterEndpointDns", host_info.host, host_inet_address)
73
+
74
+ if cluster_inet_address is None:
75
+ return conn
76
+
77
+ if self._plugin_service.get_host_role(conn) == HostRole.READER:
78
+ # This if-statement is only reached if the connection url is a writer cluster endpoint.
79
+ # If the new connection resolves to a reader instance, this means the topology is outdated.
80
+ # Force refresh to update the topology.
81
+ self._plugin_service.force_refresh_host_list(conn)
82
+ else:
83
+ self._plugin_service.refresh_host_list(conn)
84
+
85
+ logger.debug("LogUtils.Topology", LogUtils.log_topology(self._plugin_service.hosts))
86
+
87
+ if self._writer_host_info is None:
88
+ writer_candidate: Optional[HostInfo] = self._get_writer()
89
+ if writer_candidate is not None and self._rds_helper.is_rds_cluster_dns(writer_candidate.host):
90
+ return conn
91
+
92
+ self._writer_host_info = writer_candidate
93
+
94
+ logger.debug("StaleDnsHelper.WriterHostSpec", self._writer_host_info)
95
+
96
+ if self._writer_host_info is None:
97
+ return conn
98
+
99
+ if self._writer_host_address is None:
100
+ try:
101
+ self._writer_host_address = socket.gethostbyname(self._writer_host_info.host)
102
+ except socket.gaierror:
103
+ pass
104
+
105
+ logger.debug("StaleDnsHelper.WriterInetAddress", self._writer_host_address)
106
+
107
+ if self._writer_host_address is None:
108
+ return conn
109
+
110
+ if self._writer_host_address != cluster_inet_address:
111
+ logger.debug("StaleDnsHelper.StaleDnsDetected", self._writer_host_info)
112
+
113
+ writer_conn: Connection = self._plugin_service.connect(self._writer_host_info, props)
114
+ if is_initial_connection:
115
+ host_list_provider_service.initial_connection_host_info = self._writer_host_info
116
+
117
+ if conn is not None:
118
+ try:
119
+ conn.close()
120
+ except Exception:
121
+ pass
122
+ return writer_conn
123
+
124
+ return conn
125
+
126
+ def notify_host_list_changed(self, changes: Dict[str, Set[HostEvent]]) -> None:
127
+ if self._writer_host_info is None:
128
+ return
129
+
130
+ writer_changes = changes.get(self._writer_host_info.url, None)
131
+ if writer_changes is not None and HostEvent.CONVERTED_TO_READER in writer_changes:
132
+ logger.debug("StaleDnsHelper.Reset")
133
+ self._writer_host_info = None
134
+ self._writer_host_address = None
135
+
136
+ def _get_writer(self) -> Optional[HostInfo]:
137
+ for host in self._plugin_service.hosts:
138
+ if host.role == HostRole.WRITER:
139
+ return host
140
+ return None
141
+
142
+
143
+ class StaleDnsPlugin(Plugin):
144
+
145
+ _SUBSCRIBED_METHODS: Set[str] = {"init_host_provider",
146
+ "connect",
147
+ "force_connect",
148
+ "notify_host_list_changed"}
149
+
150
+ def __init__(self, plugin_service: PluginService) -> None:
151
+ self._plugin_service = plugin_service
152
+ self._stale_dns_helper = StaleDnsHelper(self._plugin_service)
153
+
154
+ StaleDnsPlugin._SUBSCRIBED_METHODS.update(self._plugin_service.network_bound_methods)
155
+
156
+ @property
157
+ def subscribed_methods(self) -> Set[str]:
158
+ return self._SUBSCRIBED_METHODS
159
+
160
+ def connect(
161
+ self,
162
+ target_driver_func: Callable,
163
+ driver_dialect: DriverDialect,
164
+ host_info: HostInfo,
165
+ props: Properties,
166
+ is_initial_connection: bool,
167
+ connect_func: Callable) -> Connection:
168
+ return self._stale_dns_helper.get_verified_connection(
169
+ is_initial_connection, self._host_list_provider_service, host_info, props, connect_func)
170
+
171
+ def force_connect(
172
+ self,
173
+ target_driver_func: Callable,
174
+ driver_dialect: DriverDialect,
175
+ host_info: HostInfo,
176
+ props: Properties,
177
+ is_initial_connection: bool,
178
+ force_connect_func: Callable) -> Connection:
179
+ return self._stale_dns_helper.get_verified_connection(
180
+ is_initial_connection, self._host_list_provider_service, host_info, props, force_connect_func)
181
+
182
+ def execute(self, target: type, method_name: str, execute_func: Callable, *args: Any, **kwargs: Any) -> Any:
183
+ try:
184
+ self._plugin_service.refresh_host_list()
185
+ except Exception:
186
+ pass
187
+
188
+ return execute_func()
189
+
190
+ def init_host_provider(
191
+ self,
192
+ properties: Properties,
193
+ host_list_provider_service: HostListProviderService,
194
+ init_host_provider_func: Callable):
195
+
196
+ self._host_list_provider_service = host_list_provider_service
197
+
198
+ init_host_provider_func()
199
+
200
+ if self._host_list_provider_service.is_static_host_list_provider():
201
+ raise Exception(Messages.get_formatted("StaleDnsPlugin.RequireDynamicProvider"))
202
+
203
+ def notify_host_list_changed(self, changes: Dict[str, Set[HostEvent]]):
204
+ self._stale_dns_helper.notify_host_list_changed(changes)
205
+
206
+
207
+ class StaleDnsPluginFactory(PluginFactory):
208
+ def get_instance(self, plugin_service: PluginService, props: Properties) -> Plugin:
209
+ return StaleDnsPlugin(plugin_service)
@@ -0,0 +1,13 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License").
4
+ # You may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
@@ -0,0 +1,94 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License").
4
+ # You may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ from typing import Generic, Optional, TypeVar
18
+
19
+ T = TypeVar("T")
20
+
21
+
22
+ class SessionStateField(Generic[T]):
23
+ _value: Optional[T] = None
24
+ _pristine_value: Optional[T] = None
25
+
26
+ def copy(self):
27
+ new_field: SessionStateField[T] = SessionStateField()
28
+ if self._value is not None:
29
+ new_field._value = self._value
30
+ if self._pristine_value is not None:
31
+ new_field._pristine_value = self._pristine_value
32
+ return new_field
33
+
34
+ @property
35
+ def value(self):
36
+ return self._value
37
+
38
+ @value.setter
39
+ def value(self, new_value: Optional[T]):
40
+ self._value = new_value
41
+
42
+ @property
43
+ def pristine_value(self):
44
+ return self._pristine_value
45
+
46
+ @pristine_value.setter
47
+ def pristine_value(self, new_value: Optional[T]):
48
+ self._pristine_value = new_value
49
+
50
+ def reset_value(self):
51
+ self._value = None
52
+
53
+ def reset_pristine_value(self):
54
+ self.pristine_value = None
55
+
56
+ def reset(self):
57
+ self.reset_value()
58
+ self.reset_pristine_value()
59
+
60
+ def is_pristine(self) -> bool:
61
+ if self.value is None:
62
+ # the value has never been set up so the session state has pristine value
63
+ return True
64
+ return self._pristine_value is not None and self._pristine_value == self.value
65
+
66
+ def can_restore_pristine(self):
67
+ if self._pristine_value is None:
68
+ return False
69
+
70
+ if self._value is not None:
71
+ # it's necessary to restore pristine value only if current session value is not the same as pristine value.
72
+ return self.value != self.pristine_value
73
+
74
+ # it's inconclusive if the current value is the same as pristine value, so we need to take the safest path.
75
+ return True
76
+
77
+ def __str__(self):
78
+ return f"{self.pristine_value if self.pristine_value is not None else '(blank)'} -> {self.value if self.value is not None else '(blank)'}"
79
+
80
+ def __repr__(self):
81
+ return f"{self.pristine_value if self.pristine_value is not None else '(blank)'} -> {self.value if self.value is not None else '(blank)'}"
82
+
83
+
84
+ class SessionState:
85
+ def __init__(self):
86
+ self.auto_commit: SessionStateField[bool] = SessionStateField()
87
+ self.readonly: SessionStateField[bool] = SessionStateField()
88
+
89
+ def copy(self):
90
+ new_session_state: SessionState = SessionState()
91
+ new_session_state.auto_commit = self.auto_commit.copy()
92
+ new_session_state.readonly = self.readonly.copy()
93
+
94
+ return new_session_state
@@ -0,0 +1,221 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License").
4
+ # You may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ from __future__ import annotations
17
+
18
+ from typing import TYPE_CHECKING, Callable, Optional, Protocol
19
+
20
+ if TYPE_CHECKING:
21
+ from aws_advanced_python_wrapper.plugin_service import PluginService
22
+ from aws_advanced_python_wrapper.pep249 import Connection
23
+
24
+ from aws_advanced_python_wrapper.errors import AwsWrapperError
25
+ from aws_advanced_python_wrapper.states.session_state import SessionState
26
+ from aws_advanced_python_wrapper.utils.log import Logger
27
+ from aws_advanced_python_wrapper.utils.properties import (Properties,
28
+ WrapperProperties)
29
+
30
+ logger = Logger(__name__)
31
+
32
+
33
+ class SessionStateService(Protocol):
34
+
35
+ def get_autocommit(self) -> Optional[bool]:
36
+ ...
37
+
38
+ def set_autocommit(self, autocommit: bool):
39
+ ...
40
+
41
+ def setup_pristine_autocommit(self, autocommit: Optional[bool] = None):
42
+ ...
43
+
44
+ def get_readonly(self):
45
+ ...
46
+
47
+ def set_read_only(self, readonly: bool):
48
+ ...
49
+
50
+ def setup_pristine_readonly(self, readonly: Optional[bool] = None):
51
+ ...
52
+
53
+ def reset(self):
54
+ ...
55
+
56
+ def begin(self):
57
+ """
58
+ Begin session transfer process
59
+ """
60
+ ...
61
+
62
+ def complete(self):
63
+ """
64
+ Complete session transfer process. This method should be called despite whether session transfer is successful or not.
65
+ """
66
+ ...
67
+
68
+ def apply_current_session_state(self, new_connection: Connection):
69
+ ...
70
+
71
+ def apply_pristine_session_state(self, new_connection: Connection):
72
+ ...
73
+
74
+
75
+ class SessionStateTransferHandlers:
76
+ reset_session_state_on_close_callable: Optional[Callable] = None
77
+ transfer_session_state_on_switch_callable: Optional[Callable] = None
78
+
79
+ @staticmethod
80
+ def set_reset_session_state_on_close_func(func: Callable):
81
+ SessionStateTransferHandlers.reset_session_state_on_close_callable = func
82
+
83
+ @staticmethod
84
+ def clear_reset_session_state_on_close_func():
85
+ SessionStateTransferHandlers.reset_session_state_on_close_callable = None
86
+
87
+ @staticmethod
88
+ def get_reset_session_state_on_close_func() -> Optional[Callable]:
89
+ return SessionStateTransferHandlers.reset_session_state_on_close_callable
90
+
91
+ @staticmethod
92
+ def get_transfer_session_state_on_switch_func() -> Optional[Callable]:
93
+ return SessionStateTransferHandlers.transfer_session_state_on_switch_callable
94
+
95
+ @staticmethod
96
+ def set_transfer_session_state_on_switch_func(func: Callable):
97
+ SessionStateTransferHandlers.transfer_session_state_on_switch_callable = func
98
+
99
+ @staticmethod
100
+ def reset_transfer_session_state_on_switch_func():
101
+ SessionStateTransferHandlers.reset_transfer_session_state_on_switch_callable = None
102
+
103
+
104
+ class SessionStateServiceImpl(SessionStateService):
105
+ def __init__(self, plugin_service: PluginService, props: Properties):
106
+ self._session_state: SessionState = SessionState()
107
+ self._copy_session_state: Optional[SessionState] = None
108
+ self._plugin_service: PluginService = plugin_service
109
+ self._props: Properties = props
110
+
111
+ def log_current_state(self):
112
+ logger.debug(f"Current session state: \n{self._session_state}")
113
+
114
+ def _transfer_state_enabled_setting(self) -> bool:
115
+ return WrapperProperties.TRANSFER_SESSION_STATE_ON_SWITCH.get_bool(self._props)
116
+
117
+ def _reset_state_enabled_setting(self) -> bool:
118
+ return WrapperProperties.RESET_SESSION_STATE_ON_CLOSE.get_bool(self._props)
119
+
120
+ def get_autocommit(self) -> Optional[bool]:
121
+ return self._session_state.auto_commit.value
122
+
123
+ def set_autocommit(self, autocommit: bool):
124
+ if not self._transfer_state_enabled_setting():
125
+ return
126
+ self._session_state.auto_commit.value = autocommit
127
+
128
+ def setup_pristine_autocommit(self, autocommit: Optional[bool] = None):
129
+ if not self._transfer_state_enabled_setting():
130
+ return
131
+ if self._session_state.auto_commit.pristine_value is not None:
132
+ return
133
+
134
+ if autocommit is None and self._plugin_service.current_connection is not None:
135
+ autocommit = self._plugin_service.driver_dialect.get_autocommit(self._plugin_service.current_connection)
136
+
137
+ self._session_state.auto_commit.pristine_value = autocommit
138
+ self.log_current_state()
139
+
140
+ def get_readonly(self):
141
+ return self._session_state.readonly.value
142
+
143
+ def set_read_only(self, readonly: bool):
144
+ if not self._transfer_state_enabled_setting():
145
+ return
146
+ self._session_state.readonly.value = readonly
147
+
148
+ def setup_pristine_readonly(self, readonly: Optional[bool] = None):
149
+ if not self._transfer_state_enabled_setting():
150
+ return
151
+ if self._session_state.readonly.pristine_value is not None:
152
+ return
153
+
154
+ if readonly is None and self._plugin_service.current_connection is not None:
155
+ readonly = self._plugin_service.driver_dialect.is_read_only(self._plugin_service.current_connection)
156
+
157
+ self._session_state.readonly.pristine_value = readonly
158
+ self.log_current_state()
159
+
160
+ def reset(self):
161
+ self._session_state.auto_commit.reset()
162
+ self._session_state.readonly.reset()
163
+
164
+ def begin(self):
165
+ self.log_current_state()
166
+ if not self._transfer_state_enabled_setting() and not self._reset_state_enabled_setting():
167
+ return
168
+ if self._copy_session_state is not None:
169
+ raise AwsWrapperError("Previous session state transfer is not completed.")
170
+ self._copy_session_state = self._session_state.copy()
171
+
172
+ def complete(self):
173
+ self._copy_session_state = None
174
+
175
+ def apply_current_session_state(self, new_connection: Connection):
176
+ if not self._transfer_state_enabled_setting():
177
+ return
178
+
179
+ func: Optional[Callable] = SessionStateTransferHandlers.get_transfer_session_state_on_switch_func()
180
+ if func is not None:
181
+ is_handled: bool = func(self._session_state, new_connection)
182
+ if is_handled:
183
+ # Custom function has handled session transfer
184
+ return
185
+
186
+ if self._session_state.auto_commit.value is not None:
187
+ self._session_state.auto_commit.reset_pristine_value()
188
+ self.setup_pristine_autocommit()
189
+ self._plugin_service.driver_dialect.set_autocommit(new_connection, self._session_state.auto_commit.value)
190
+
191
+ if self._session_state.readonly.value is not None:
192
+ self._session_state.readonly.reset_pristine_value()
193
+ self.setup_pristine_readonly()
194
+ self._plugin_service.driver_dialect.set_read_only(new_connection, self._session_state.readonly.value)
195
+
196
+ def apply_pristine_session_state(self, new_connection: Connection):
197
+ if not self._transfer_state_enabled_setting():
198
+ return
199
+
200
+ func: Optional[Callable] = SessionStateTransferHandlers.get_transfer_session_state_on_switch_func()
201
+ if func is not None:
202
+ is_handled: bool = func(self._session_state, new_connection)
203
+ if is_handled:
204
+ # Custom function has handled session transfer
205
+ return
206
+ if self._copy_session_state is None:
207
+ return
208
+
209
+ if self._copy_session_state.auto_commit.can_restore_pristine():
210
+ try:
211
+ self._plugin_service.driver_dialect.set_autocommit(new_connection, self._copy_session_state.auto_commit.pristine_value)
212
+ except Exception:
213
+ # Ignore any exception.
214
+ pass
215
+
216
+ if self._copy_session_state.readonly.can_restore_pristine():
217
+ try:
218
+ self._plugin_service.driver_dialect.set_read_only(new_connection, self._copy_session_state.readonly.pristine_value)
219
+ except Exception:
220
+ # Ignore any exception.
221
+ pass
@@ -0,0 +1,13 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License").
4
+ # You may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
@@ -0,0 +1,51 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License").
4
+ # You may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from threading import Lock
16
+
17
+
18
+ class AtomicInt:
19
+ def __init__(self, initial_value: int = 0):
20
+ self._value = initial_value
21
+ self._lock: Lock = Lock()
22
+
23
+ def get(self):
24
+ with self._lock:
25
+ return self._value
26
+
27
+ def set(self, value: int):
28
+ with self._lock:
29
+ self._value = value
30
+
31
+ def get_and_increment(self):
32
+ with self._lock:
33
+ value = self._value
34
+ self._value += 1
35
+ return value
36
+
37
+ def increment_and_get(self):
38
+ with self._lock:
39
+ self._value += 1
40
+ return self._value
41
+
42
+ def get_and_decrement(self):
43
+ with self._lock:
44
+ value = self._value
45
+ self._value -= 1
46
+ return value
47
+
48
+ def decrement_and_get(self):
49
+ with self._lock:
50
+ self._value -= 1
51
+ return self._value