omnata-plugin-runtime 0.8.0a187__py3-none-any.whl → 0.8.0a188__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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._session = session
54
- import_dir = sys._xoptions[IMPORT_DIRECTORY_NAME]
55
- sys.path.append(os.path.join(import_dir, "app.zip"))
56
- module = importlib.import_module(module_name)
57
- class_obj = getattr(module, class_name)
58
- self._plugin_instance: OmnataPlugin = class_obj()
59
- self._plugin_instance._session = session # pylint: disable=protected-access
60
- # logging defaults
61
- snowflake_logger = logging.getLogger("snowflake")
62
- snowflake_logger.setLevel(logging.WARN) # we don't want snowflake queries being logged by default
63
- # the sync engine can tell the plugin to override log level via a session variable
64
- if session is not None:
65
- try:
66
- v = session.sql("select getvariable('LOG_LEVEL_OVERRIDES')").collect()
67
- result = v[0][0]
68
- if result is not None:
69
- log_level_overrides:Dict[str,str] = json.loads(result)
70
- for logger_name,level in log_level_overrides.items():
71
- logger_override = logging.getLogger(logger_name)
72
- logger_override.setLevel(level)
73
- logger_override.propagate = False
74
- for handler in logger_override.handlers:
75
- handler.setLevel(level)
76
- except Exception as e:
77
- logger.error(f"Error setting log level overrides: {str(e)}")
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
- omnata_log_handler.register(
102
- request.logging_level, self._plugin_instance.additional_loggers()
103
- )
104
- # construct some connection parameters for the purpose of getting the api limits
105
- connection_parameters = ConnectionConfigurationParameters(
106
- connection_method=request.connection_method,
107
- connectivity_option=request.connectivity_option,
108
- connection_parameters=request.connection_parameters,
109
- connection_secrets=connection_secrets
110
- )
111
- if request.oauth_secret_name is not None:
112
- connection_parameters.access_token_secret_name = request.oauth_secret_name
113
- all_api_limits = self._plugin_instance.api_limits(connection_parameters)
114
- logger.info(
115
- f"Default API limits: {json.dumps(to_jsonable_python(all_api_limits))}"
116
- )
117
- all_api_limits_by_category = {
118
- api_limit.endpoint_category: api_limit for api_limit in all_api_limits
119
- }
120
- all_api_limits_by_category.update(
121
- {
122
- k: v
123
- for k, v in [
124
- (x.endpoint_category, x) for x in request.api_limit_overrides
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
- api_limits = list(all_api_limits_by_category.values())
129
- return_dict = {}
130
- logger.info(
131
- f"Rate limits state: {json.dumps(to_jsonable_python(request.rate_limits_state))}"
132
- )
133
- (rate_limit_state_all, rate_limit_state_this_branch) = RateLimitState.collapse(request.rate_limits_state,request.sync_id, request.sync_branch_name)
134
- # if any endpoint categories have no state, give them an empty state
135
- for api_limit in api_limits:
136
- if api_limit.endpoint_category not in rate_limit_state_all:
137
- rate_limit_state_all[api_limit.endpoint_category] = RateLimitState(
138
- wait_until=None, previous_request_timestamps=[]
139
- )
140
- if api_limit.endpoint_category not in rate_limit_state_this_branch:
141
- rate_limit_state_this_branch[api_limit.endpoint_category] = RateLimitState(
142
- wait_until=None, previous_request_timestamps=[]
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 HttpRateLimiting(outbound_sync_request, parameters):
178
- self._plugin_instance.sync_outbound(parameters, outbound_sync_request)
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
- outbound_sync_request.apply_results_queue()
181
- outbound_sync_request.apply_rate_limit_state()
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 HttpRateLimiting(inbound_sync_request, parameters):
236
- self._plugin_instance.sync_inbound(parameters, inbound_sync_request)
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
- inbound_sync_request.update_activity("Staging remaining records")
240
- logger.info("Calling apply_results_queue")
241
- inbound_sync_request.apply_results_queue()
242
- try:
243
- # this is not critical, we wouldn't fail the sync over rate limit usage capture
244
- logger.info("Calling apply_rate_limit_state")
245
- inbound_sync_request.apply_rate_limit_state()
246
- except Exception as e:
247
- logger.error(f"Error applying rate limit state: {str(e)}")
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omnata-plugin-runtime
3
- Version: 0.8.0a187
3
+ Version: 0.8.0a188
4
4
  Summary: Classes and common runtime components for building and running Omnata Plugins
5
5
  Author: James Weakley
6
6
  Author-email: james.weakley@omnata.com
@@ -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=LM4FHMW0N3N13oxsUqkSEbUhH95uFw-yNIJ5kNGDgWE,30633
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.0a187.dist-info/LICENSE,sha256=rGaMQG3R3F5-JGDp_-rlMKpDIkg5n0SI4kctTk8eZSI,56
10
- omnata_plugin_runtime-0.8.0a187.dist-info/METADATA,sha256=uVZoK32EmYnNrEAi7mDHt6x9iVZ2IvrBXWBqpmmbNeo,2148
11
- omnata_plugin_runtime-0.8.0a187.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
12
- omnata_plugin_runtime-0.8.0a187.dist-info/RECORD,,
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,,