omnata-plugin-runtime 0.8.0a187__py3-none-any.whl → 0.8.0a188__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.
- omnata_plugin_runtime/plugin_entrypoints.py +110 -94
- {omnata_plugin_runtime-0.8.0a187.dist-info → omnata_plugin_runtime-0.8.0a188.dist-info}/METADATA +1 -1
- {omnata_plugin_runtime-0.8.0a187.dist-info → omnata_plugin_runtime-0.8.0a188.dist-info}/RECORD +5 -5
- {omnata_plugin_runtime-0.8.0a187.dist-info → omnata_plugin_runtime-0.8.0a188.dist-info}/LICENSE +0 -0
- {omnata_plugin_runtime-0.8.0a187.dist-info → omnata_plugin_runtime-0.8.0a188.dist-info}/WHEEL +0 -0
@@ -36,6 +36,7 @@ from .omnata_plugin import (
|
|
36
36
|
)
|
37
37
|
from pydantic import TypeAdapter
|
38
38
|
from .rate_limiting import ApiLimits, RateLimitState
|
39
|
+
from opentelemetry import trace
|
39
40
|
|
40
41
|
IMPORT_DIRECTORY_NAME = "snowflake_import_directory"
|
41
42
|
|
@@ -50,46 +51,40 @@ class PluginEntrypoint:
|
|
50
51
|
self, plugin_fqn: str, session: Session, module_name: str, class_name: str
|
51
52
|
):
|
52
53
|
logger.info(f"Initialising plugin entrypoint for {plugin_fqn}")
|
53
|
-
self.
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
54
|
+
self.tracer = trace.get_tracer(__name__)
|
55
|
+
with self.tracer.start_as_current_span("plugin_initialization") as span:
|
56
|
+
self._session = session
|
57
|
+
import_dir = sys._xoptions[IMPORT_DIRECTORY_NAME]
|
58
|
+
span.add_event("Adding plugin zip to path")
|
59
|
+
sys.path.append(os.path.join(import_dir, "app.zip"))
|
60
|
+
span.add_event("Importing plugin module")
|
61
|
+
module = importlib.import_module(module_name)
|
62
|
+
class_obj = getattr(module, class_name)
|
63
|
+
self._plugin_instance: OmnataPlugin = class_obj()
|
64
|
+
self._plugin_instance._session = session # pylint: disable=protected-access
|
65
|
+
# logging defaults
|
66
|
+
snowflake_logger = logging.getLogger("snowflake")
|
67
|
+
snowflake_logger.setLevel(logging.WARN) # we don't want snowflake queries being logged by default
|
68
|
+
# the sync engine can tell the plugin to override log level via a session variable
|
69
|
+
if session is not None:
|
70
|
+
try:
|
71
|
+
span.add_event("Checking log level overrides")
|
72
|
+
v = session.sql("select getvariable('LOG_LEVEL_OVERRIDES')").collect()
|
73
|
+
result = v[0][0]
|
74
|
+
if result is not None:
|
75
|
+
log_level_overrides:Dict[str,str] = json.loads(result)
|
76
|
+
span.add_event("Applying log level overrides",log_level_overrides)
|
77
|
+
for logger_name,level in log_level_overrides.items():
|
78
|
+
logger_override = logging.getLogger(logger_name)
|
79
|
+
logger_override.setLevel(level)
|
80
|
+
logger_override.propagate = False
|
81
|
+
for handler in logger_override.handlers:
|
82
|
+
handler.setLevel(level)
|
83
|
+
except Exception as e:
|
84
|
+
logger.error(f"Error setting log level overrides: {str(e)}")
|
78
85
|
|
79
86
|
|
80
87
|
def sync(self, sync_request: Dict):
|
81
|
-
logger.info("Entered sync method")
|
82
|
-
request = TypeAdapter(SyncRequestPayload).validate_python(sync_request)
|
83
|
-
connection_secrets = get_secrets(
|
84
|
-
request.oauth_secret_name, request.other_secrets_name
|
85
|
-
)
|
86
|
-
omnata_log_handler = OmnataPluginLogHandler(
|
87
|
-
session=self._session,
|
88
|
-
sync_id=request.sync_id,
|
89
|
-
sync_branch_id=request.sync_branch_id,
|
90
|
-
connection_id=request.connection_id,
|
91
|
-
sync_run_id=request.run_id,
|
92
|
-
)
|
93
88
|
logger.add_extra('omnata.operation', 'sync')
|
94
89
|
logger.add_extra('omnata.sync.id', request.sync_id)
|
95
90
|
logger.add_extra('omnata.sync.direction', request.sync_direction)
|
@@ -97,50 +92,67 @@ class PluginEntrypoint:
|
|
97
92
|
logger.add_extra('omnata.sync.run_id', request.run_id)
|
98
93
|
logger.add_extra('omnata.sync_branch.id', request.sync_branch_id)
|
99
94
|
logger.add_extra('omnata.sync_branch.name', request.sync_branch_name)
|
95
|
+
logger.info("Entered sync method")
|
96
|
+
with self.tracer.start_as_current_span("sync_initialization") as span:
|
97
|
+
span.add_event("Fetching secrets")
|
100
98
|
|
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
|
-
|
99
|
+
request = TypeAdapter(SyncRequestPayload).validate_python(sync_request)
|
100
|
+
connection_secrets = get_secrets(
|
101
|
+
request.oauth_secret_name, request.other_secrets_name
|
102
|
+
)
|
103
|
+
span.add_event("Configuring log handler")
|
104
|
+
omnata_log_handler = OmnataPluginLogHandler(
|
105
|
+
session=self._session,
|
106
|
+
sync_id=request.sync_id,
|
107
|
+
sync_branch_id=request.sync_branch_id,
|
108
|
+
connection_id=request.connection_id,
|
109
|
+
sync_run_id=request.run_id,
|
110
|
+
)
|
111
|
+
|
112
|
+
omnata_log_handler.register(
|
113
|
+
request.logging_level, self._plugin_instance.additional_loggers()
|
114
|
+
)
|
115
|
+
# construct some connection parameters for the purpose of getting the api limits
|
116
|
+
connection_parameters = ConnectionConfigurationParameters(
|
117
|
+
connection_method=request.connection_method,
|
118
|
+
connectivity_option=request.connectivity_option,
|
119
|
+
connection_parameters=request.connection_parameters,
|
120
|
+
connection_secrets=connection_secrets
|
121
|
+
)
|
122
|
+
if request.oauth_secret_name is not None:
|
123
|
+
connection_parameters.access_token_secret_name = request.oauth_secret_name
|
124
|
+
span.add_event("Configuring API Limits")
|
125
|
+
all_api_limits = self._plugin_instance.api_limits(connection_parameters)
|
126
|
+
logger.info(
|
127
|
+
f"Default API limits: {json.dumps(to_jsonable_python(all_api_limits))}"
|
128
|
+
)
|
129
|
+
all_api_limits_by_category = {
|
130
|
+
api_limit.endpoint_category: api_limit for api_limit in all_api_limits
|
126
131
|
}
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
132
|
+
all_api_limits_by_category.update(
|
133
|
+
{
|
134
|
+
k: v
|
135
|
+
for k, v in [
|
136
|
+
(x.endpoint_category, x) for x in request.api_limit_overrides
|
137
|
+
]
|
138
|
+
}
|
139
|
+
)
|
140
|
+
api_limits = list(all_api_limits_by_category.values())
|
141
|
+
return_dict = {}
|
142
|
+
logger.info(
|
143
|
+
f"Rate limits state: {json.dumps(to_jsonable_python(request.rate_limits_state))}"
|
144
|
+
)
|
145
|
+
(rate_limit_state_all, rate_limit_state_this_branch) = RateLimitState.collapse(request.rate_limits_state,request.sync_id, request.sync_branch_name)
|
146
|
+
# if any endpoint categories have no state, give them an empty state
|
147
|
+
for api_limit in api_limits:
|
148
|
+
if api_limit.endpoint_category not in rate_limit_state_all:
|
149
|
+
rate_limit_state_all[api_limit.endpoint_category] = RateLimitState(
|
150
|
+
wait_until=None, previous_request_timestamps=[]
|
151
|
+
)
|
152
|
+
if api_limit.endpoint_category not in rate_limit_state_this_branch:
|
153
|
+
rate_limit_state_this_branch[api_limit.endpoint_category] = RateLimitState(
|
154
|
+
wait_until=None, previous_request_timestamps=[]
|
155
|
+
)
|
144
156
|
|
145
157
|
if request.sync_direction == "outbound":
|
146
158
|
parameters = OutboundSyncConfigurationParameters(
|
@@ -174,11 +186,13 @@ class PluginEntrypoint:
|
|
174
186
|
)
|
175
187
|
try:
|
176
188
|
self._plugin_instance._configuration_parameters = parameters
|
177
|
-
with
|
178
|
-
|
189
|
+
with self.tracer.start_as_current_span("sync_execution") as span:
|
190
|
+
with HttpRateLimiting(outbound_sync_request, parameters):
|
191
|
+
self._plugin_instance.sync_outbound(parameters, outbound_sync_request)
|
179
192
|
if self._plugin_instance.disable_background_workers is False:
|
180
|
-
|
181
|
-
|
193
|
+
with self.tracer.start_as_current_span("results_finalization") as span:
|
194
|
+
outbound_sync_request.apply_results_queue()
|
195
|
+
outbound_sync_request.apply_rate_limit_state()
|
182
196
|
if outbound_sync_request.deadline_reached:
|
183
197
|
# if we actually hit the deadline, this is flagged by the cancellation checking worker and the cancellation
|
184
198
|
# token is set. We throw it here as an error since that's currently how it flows back to the engine with a DELAYED state
|
@@ -232,19 +246,21 @@ class PluginEntrypoint:
|
|
232
246
|
inbound_sync_request.update_activity("Invoking plugin")
|
233
247
|
logger.info(f"inbound sync request: {inbound_sync_request}")
|
234
248
|
# plugin_instance._inbound_sync_request = outbound_sync_request
|
235
|
-
with
|
236
|
-
|
249
|
+
with self.tracer.start_as_current_span("sync_execution") as span:
|
250
|
+
with HttpRateLimiting(inbound_sync_request, parameters):
|
251
|
+
self._plugin_instance.sync_inbound(parameters, inbound_sync_request)
|
237
252
|
logger.info("Finished invoking plugin")
|
238
253
|
if self._plugin_instance.disable_background_workers is False:
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
254
|
+
with self.tracer.start_as_current_span("results_finalization") as span:
|
255
|
+
inbound_sync_request.update_activity("Staging remaining records")
|
256
|
+
logger.info("Calling apply_results_queue")
|
257
|
+
inbound_sync_request.apply_results_queue()
|
258
|
+
try:
|
259
|
+
# this is not critical, we wouldn't fail the sync over rate limit usage capture
|
260
|
+
logger.info("Calling apply_rate_limit_state")
|
261
|
+
inbound_sync_request.apply_rate_limit_state()
|
262
|
+
except Exception as e:
|
263
|
+
logger.error(f"Error applying rate limit state: {str(e)}")
|
248
264
|
# here we used to do a final inbound_sync_request.apply_progress_updates(ignore_errors=False)
|
249
265
|
# but it was erroring too much since there was usually a lot of DDL activity on the Snowflake side
|
250
266
|
# so instead, we'll provide a final progress update via a return value from the proc
|
{omnata_plugin_runtime-0.8.0a187.dist-info → omnata_plugin_runtime-0.8.0a188.dist-info}/RECORD
RENAMED
@@ -4,9 +4,9 @@ omnata_plugin_runtime/configuration.py,sha256=5M7dmQu9BO2msES9Foa8fOKfWui7iPSExA
|
|
4
4
|
omnata_plugin_runtime/forms.py,sha256=ueodN2GIMS5N9fqebpY4uNGJnjEb9HcuaVQVfWH-cGg,19838
|
5
5
|
omnata_plugin_runtime/logging.py,sha256=u_Bo2v4jS3C_2E_Y8a7yfZZcIP-h5Mak_FPnFHUwFbU,4378
|
6
6
|
omnata_plugin_runtime/omnata_plugin.py,sha256=aggjb_CTTjhgqjS8CHPOm4ENU0jNcYoT6LC8yI1IeF4,130048
|
7
|
-
omnata_plugin_runtime/plugin_entrypoints.py,sha256=
|
7
|
+
omnata_plugin_runtime/plugin_entrypoints.py,sha256=z1NJpWvEj5TizCEU8YLnO5cWmeU8iSEsMK9CQaL47RA,32021
|
8
8
|
omnata_plugin_runtime/rate_limiting.py,sha256=JKtyz8mA9D0FSZgQplPusmk2rVclcjkwtE59fQQrQ_I,25610
|
9
|
-
omnata_plugin_runtime-0.8.
|
10
|
-
omnata_plugin_runtime-0.8.
|
11
|
-
omnata_plugin_runtime-0.8.
|
12
|
-
omnata_plugin_runtime-0.8.
|
9
|
+
omnata_plugin_runtime-0.8.0a188.dist-info/LICENSE,sha256=rGaMQG3R3F5-JGDp_-rlMKpDIkg5n0SI4kctTk8eZSI,56
|
10
|
+
omnata_plugin_runtime-0.8.0a188.dist-info/METADATA,sha256=OrGtX56IyJCjpFTsxkVu0IZI3Z9zJ5aHSzpgI0Mg3Bk,2148
|
11
|
+
omnata_plugin_runtime-0.8.0a188.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
12
|
+
omnata_plugin_runtime-0.8.0a188.dist-info/RECORD,,
|
{omnata_plugin_runtime-0.8.0a187.dist-info → omnata_plugin_runtime-0.8.0a188.dist-info}/LICENSE
RENAMED
File without changes
|
{omnata_plugin_runtime-0.8.0a187.dist-info → omnata_plugin_runtime-0.8.0a188.dist-info}/WHEEL
RENAMED
File without changes
|