atlan-application-sdk 0.1.1rc45__py3-none-any.whl → 0.1.1rc46__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.
@@ -5,15 +5,15 @@ allowing the workflow to orchestrate locking without hitting Temporal's
5
5
  deadlock timeout.
6
6
  """
7
7
 
8
- import asyncio
9
8
  import random
10
9
  from typing import Any, Dict
11
10
 
12
11
  from temporalio import activity
12
+ from temporalio.exceptions import ApplicationError
13
13
 
14
14
  from application_sdk.clients.redis import RedisClientAsync
15
15
  from application_sdk.common.error_codes import ActivityError
16
- from application_sdk.constants import APPLICATION_NAME, LOCK_RETRY_INTERVAL
16
+ from application_sdk.constants import APPLICATION_NAME
17
17
  from application_sdk.observability.logger_adaptor import get_logger
18
18
 
19
19
  logger = get_logger(__name__)
@@ -45,40 +45,41 @@ async def acquire_distributed_lock(
45
45
  """
46
46
  # Input validation
47
47
  if max_locks <= 0:
48
- raise ActivityError(
49
- f"{ActivityError.LOCK_ACQUISITION_ERROR}: max_locks must be greater than 0, got {max_locks}"
48
+ raise ApplicationError(
49
+ f"{ActivityError.LOCK_ACQUISITION_ERROR}: max_locks must be greater than 0, got {max_locks}",
50
+ non_retryable=True,
51
+ )
52
+ slot = random.randint(0, max_locks - 1)
53
+ resource_id = f"{APPLICATION_NAME}:{lock_name}:{slot}"
54
+
55
+ try:
56
+ async with RedisClientAsync() as redis_client:
57
+ # Acquire lock - connection will stay open until context exits
58
+ acquired = await redis_client._acquire_lock(
59
+ resource_id, owner_id, ttl_seconds
60
+ )
61
+ if acquired:
62
+ logger.info(f"Lock acquired for slot {slot}, resource: {resource_id}")
63
+ return {
64
+ "status": True,
65
+ "slot_id": slot,
66
+ "resource_id": resource_id,
67
+ "owner_id": owner_id,
68
+ }
69
+
70
+ raise ActivityError(
71
+ f"{ActivityError.LOCK_ACQUISITION_ERROR}: Lock not acquired for {resource_id}, will retry after some time"
72
+ )
73
+ except Exception as e:
74
+ # Redis connection or operation failed - propagate as activity error
75
+ if isinstance(e, (ActivityError)):
76
+ raise e
77
+ logger.error(f"Redis error during lock acquisition: {e}")
78
+ raise ApplicationError(
79
+ f"Redis error during lock acquisition for {resource_id}, error: {e}",
80
+ non_retryable=True,
81
+ type=type(e).__name__,
50
82
  )
51
-
52
- async with RedisClientAsync() as redis_client:
53
- while True:
54
- slot = random.randint(0, max_locks - 1)
55
- resource_id = f"{APPLICATION_NAME}:{lock_name}:{slot}"
56
-
57
- try:
58
- # Acquire lock - connection will stay open until context exits
59
- acquired = await redis_client._acquire_lock(
60
- resource_id, owner_id, ttl_seconds
61
- )
62
- if acquired:
63
- logger.info(
64
- f"Lock acquired for slot {slot}, resource: {resource_id}"
65
- )
66
- return {
67
- "slot_id": slot,
68
- "resource_id": resource_id,
69
- "owner_id": owner_id,
70
- }
71
- # If not acquired, continue retrying (lock held by another owner)
72
-
73
- except Exception as e:
74
- # Redis connection or operation failed - propagate as activity error
75
- logger.error(f"Redis error during lock acquisition: {e}")
76
- raise ActivityError(
77
- f"{ActivityError.LOCK_ACQUISITION_ERROR}: Redis error during lock acquisition for {resource_id}"
78
- )
79
-
80
- # Wait before retrying
81
- await asyncio.sleep(LOCK_RETRY_INTERVAL)
82
83
 
83
84
 
84
85
  @activity.defn
@@ -94,8 +95,8 @@ async def release_distributed_lock(
94
95
  Returns:
95
96
  True if lock was released successfully, False otherwise
96
97
  """
97
- async with RedisClientAsync() as redis_client:
98
- try:
98
+ try:
99
+ async with RedisClientAsync() as redis_client:
99
100
  released, result = await redis_client._release_lock(resource_id, owner_id)
100
101
  if released:
101
102
  logger.info(
@@ -103,8 +104,8 @@ async def release_distributed_lock(
103
104
  )
104
105
  return released
105
106
 
106
- except Exception as e:
107
- logger.error(f"Redis error during lock release for {resource_id}: {e}")
108
- # Don't raise exception for lock release failures - log and return False
109
- # Lock release is best-effort and shouldn't fail the workflow
110
- return False
107
+ except Exception as e:
108
+ logger.error(f"Redis error during lock release for {resource_id}: {e}")
109
+ # Don't raise exception for lock release failures - log and return False
110
+ # Lock release is best-effort and shouldn't fail the workflow
111
+ return False
@@ -22,6 +22,7 @@ from application_sdk.constants import (
22
22
  APPLICATION_NAME,
23
23
  IS_LOCKING_DISABLED,
24
24
  LOCK_METADATA_KEY,
25
+ LOCK_RETRY_INTERVAL,
25
26
  )
26
27
  from application_sdk.observability.logger_adaptor import get_logger
27
28
 
@@ -106,13 +107,17 @@ class RedisLockOutboundInterceptor(WorkflowOutboundInterceptor):
106
107
  lock_result = None
107
108
 
108
109
  try:
109
- # Step 1: Acquire lock via dedicated activity (can take >2s safely)
110
- start_to_close_timeout = workflow.info().execution_timeout
111
- lock_result = await workflow.execute_activity(
110
+ # Step 1: Acquire lock via dedicated activity with Temporal retry policy
111
+ schedule_to_close_timeout = workflow.info().execution_timeout
112
+ lock_result = await workflow.execute_local_activity(
112
113
  "acquire_distributed_lock",
113
114
  args=[lock_name, max_locks, ttl_seconds, owner_id],
114
- start_to_close_timeout=start_to_close_timeout,
115
- retry_policy=RetryPolicy(maximum_attempts=1),
115
+ start_to_close_timeout=timedelta(seconds=30),
116
+ retry_policy=RetryPolicy(
117
+ initial_interval=timedelta(seconds=int(LOCK_RETRY_INTERVAL)),
118
+ backoff_coefficient=1.0,
119
+ ),
120
+ schedule_to_close_timeout=schedule_to_close_timeout,
116
121
  )
117
122
 
118
123
  logger.debug(f"Lock acquired: {lock_result}, executing {input.activity}")
@@ -2,4 +2,4 @@
2
2
  Version information for the application_sdk package.
3
3
  """
4
4
 
5
- __version__ = "0.1.1rc45"
5
+ __version__ = "0.1.1rc46"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: atlan-application-sdk
3
- Version: 0.1.1rc45
3
+ Version: 0.1.1rc46
4
4
  Summary: Atlan Application SDK is a Python library for developing applications on the Atlan Platform
5
5
  Project-URL: Repository, https://github.com/atlanhq/application-sdk
6
6
  Project-URL: Documentation, https://github.com/atlanhq/application-sdk/README.md
@@ -1,9 +1,9 @@
1
1
  application_sdk/__init__.py,sha256=2e2mvmLJ5dxmJGPELtb33xwP-j6JMdoIuqKycEn7hjg,151
2
2
  application_sdk/constants.py,sha256=eLHmH9GukXCKK-u5a4bAqz8BeCOCusCM0eW0Q0Bwjns,10947
3
- application_sdk/version.py,sha256=p3JLlDmx39Ch7lMX6DS5ZKpVpATBikUiznrMlVxdpTA,88
3
+ application_sdk/version.py,sha256=TTHbof7z1rqMmnKkkw-wrzn3nRqtDcXfc_kbnHfnflc,88
4
4
  application_sdk/worker.py,sha256=i5f0AeKI39IfsLO05QkwC6uMz0zDPSJqP7B2byri1VI,7489
5
5
  application_sdk/activities/__init__.py,sha256=QaXLOBYbb0zPOY5kfDQh56qbXQFaYNXOjJ5PCvatiZ4,9530
6
- application_sdk/activities/lock_management.py,sha256=L__GZ9BsArwU1ntYwAgCKsSjCqN6QBeOfT-OT4WyD4Y,3983
6
+ application_sdk/activities/lock_management.py,sha256=oX2qPpfEu_xP0MiaCakVGk9ivZDvG4EddVZag1DuHSE,3976
7
7
  application_sdk/activities/.cursor/BUGBOT.md,sha256=FNykX5aMkdOhzgpiGqstOnSp9JN63iR2XP3onU4AGh8,15843
8
8
  application_sdk/activities/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  application_sdk/activities/common/models.py,sha256=LIZfWvTtgtbAUvvn-rwrPQgD7fP2J0Gxdxr_ITgw-jM,1243
@@ -67,7 +67,7 @@ application_sdk/inputs/.cursor/BUGBOT.md,sha256=hwKGDbopv3NU0bpC_ElpAPDFcS59GWS3
67
67
  application_sdk/interceptors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
68
68
  application_sdk/interceptors/cleanup.py,sha256=JlFcM_2Y5AIEfGTSNe0aoon7eoE68MIXI0rA3LHsSeY,5966
69
69
  application_sdk/interceptors/events.py,sha256=TeStWmBbc4v1-dm2DWeKYsUfUhJLR8CtTQhu3TWOZWM,6524
70
- application_sdk/interceptors/lock.py,sha256=Xe9TSjYKtDZUB94hbV7rHG_9rgKUJPTACeB8z8xsJ0w,5577
70
+ application_sdk/interceptors/lock.py,sha256=K1e1p11OYDDTy5TFMHcKXAvY4H86yXgpAZiuncEyH2M,5810
71
71
  application_sdk/interceptors/.cursor/BUGBOT.md,sha256=pxmUF2c7dtaXAX8yAa1-LBa6FCrj_uw7aQcHrppjf1A,14570
72
72
  application_sdk/observability/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
73
  application_sdk/observability/logger_adaptor.py,sha256=WTqnNg78W2SRGOQVhELVLn6KMRsurkG1kc7essL08Lk,29529
@@ -156,8 +156,8 @@ application_sdk/workflows/metadata_extraction/__init__.py,sha256=jHUe_ZBQ66jx8bg
156
156
  application_sdk/workflows/metadata_extraction/sql.py,sha256=6ZaVt84n-8U2ZvR9GR7uIJKv5v8CuyQjhlnoRJvDszc,12435
157
157
  application_sdk/workflows/query_extraction/__init__.py,sha256=n066_CX5RpJz6DIxGMkKS3eGSRg03ilaCtsqfJWQb7Q,117
158
158
  application_sdk/workflows/query_extraction/sql.py,sha256=kT_JQkLCRZ44ZpaC4QvPL6DxnRIIVh8gYHLqRbMI-hA,4826
159
- atlan_application_sdk-0.1.1rc45.dist-info/METADATA,sha256=nuX1LdMTNoLQW3eKYU9KdNp2vXPiLG3RnKxZeu5ZcpM,5567
160
- atlan_application_sdk-0.1.1rc45.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
161
- atlan_application_sdk-0.1.1rc45.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
162
- atlan_application_sdk-0.1.1rc45.dist-info/licenses/NOTICE,sha256=A-XVVGt3KOYuuMmvSMIFkg534F1vHiCggEBp4Ez3wGk,1041
163
- atlan_application_sdk-0.1.1rc45.dist-info/RECORD,,
159
+ atlan_application_sdk-0.1.1rc46.dist-info/METADATA,sha256=lFNImf3iOTzlw4bxeegOBO1YNjNfQ3yhAFzluCASQNI,5567
160
+ atlan_application_sdk-0.1.1rc46.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
161
+ atlan_application_sdk-0.1.1rc46.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
162
+ atlan_application_sdk-0.1.1rc46.dist-info/licenses/NOTICE,sha256=A-XVVGt3KOYuuMmvSMIFkg534F1vHiCggEBp4Ez3wGk,1041
163
+ atlan_application_sdk-0.1.1rc46.dist-info/RECORD,,