cadence-python-client 0.1.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 (95) hide show
  1. cadence/__init__.py +18 -0
  2. cadence/_internal/__init__.py +8 -0
  3. cadence/_internal/activity/__init__.py +5 -0
  4. cadence/_internal/activity/_activity_executor.py +113 -0
  5. cadence/_internal/activity/_context.py +58 -0
  6. cadence/_internal/rpc/__init__.py +0 -0
  7. cadence/_internal/rpc/error.py +148 -0
  8. cadence/_internal/rpc/retry.py +104 -0
  9. cadence/_internal/rpc/yarpc.py +42 -0
  10. cadence/_internal/workflow/__init__.py +0 -0
  11. cadence/_internal/workflow/context.py +121 -0
  12. cadence/_internal/workflow/decision_events_iterator.py +161 -0
  13. cadence/_internal/workflow/decisions_helper.py +312 -0
  14. cadence/_internal/workflow/deterministic_event_loop.py +498 -0
  15. cadence/_internal/workflow/history_event_iterator.py +58 -0
  16. cadence/_internal/workflow/statemachine/__init__.py +0 -0
  17. cadence/_internal/workflow/statemachine/activity_state_machine.py +106 -0
  18. cadence/_internal/workflow/statemachine/decision_manager.py +157 -0
  19. cadence/_internal/workflow/statemachine/decision_state_machine.py +87 -0
  20. cadence/_internal/workflow/statemachine/event_dispatcher.py +76 -0
  21. cadence/_internal/workflow/statemachine/timer_state_machine.py +73 -0
  22. cadence/_internal/workflow/workflow_engine.py +245 -0
  23. cadence/_internal/workflow/workflow_intance.py +44 -0
  24. cadence/activity.py +255 -0
  25. cadence/api/v1/__init__.py +92 -0
  26. cadence/api/v1/common_pb2.py +90 -0
  27. cadence/api/v1/common_pb2.pyi +200 -0
  28. cadence/api/v1/common_pb2_grpc.py +24 -0
  29. cadence/api/v1/decision_pb2.py +67 -0
  30. cadence/api/v1/decision_pb2.pyi +225 -0
  31. cadence/api/v1/decision_pb2_grpc.py +24 -0
  32. cadence/api/v1/domain_pb2.py +68 -0
  33. cadence/api/v1/domain_pb2.pyi +145 -0
  34. cadence/api/v1/domain_pb2_grpc.py +24 -0
  35. cadence/api/v1/error_pb2.py +59 -0
  36. cadence/api/v1/error_pb2.pyi +82 -0
  37. cadence/api/v1/error_pb2_grpc.py +24 -0
  38. cadence/api/v1/history_pb2.py +134 -0
  39. cadence/api/v1/history_pb2.pyi +780 -0
  40. cadence/api/v1/history_pb2_grpc.py +24 -0
  41. cadence/api/v1/query_pb2.py +49 -0
  42. cadence/api/v1/query_pb2.pyi +59 -0
  43. cadence/api/v1/query_pb2_grpc.py +24 -0
  44. cadence/api/v1/service_domain_pb2.py +76 -0
  45. cadence/api/v1/service_domain_pb2.pyi +164 -0
  46. cadence/api/v1/service_domain_pb2_grpc.py +327 -0
  47. cadence/api/v1/service_meta_pb2.py +41 -0
  48. cadence/api/v1/service_meta_pb2.pyi +17 -0
  49. cadence/api/v1/service_meta_pb2_grpc.py +97 -0
  50. cadence/api/v1/service_visibility_pb2.py +71 -0
  51. cadence/api/v1/service_visibility_pb2.pyi +149 -0
  52. cadence/api/v1/service_visibility_pb2_grpc.py +362 -0
  53. cadence/api/v1/service_worker_pb2.py +116 -0
  54. cadence/api/v1/service_worker_pb2.pyi +350 -0
  55. cadence/api/v1/service_worker_pb2_grpc.py +743 -0
  56. cadence/api/v1/service_workflow_pb2.py +126 -0
  57. cadence/api/v1/service_workflow_pb2.pyi +395 -0
  58. cadence/api/v1/service_workflow_pb2_grpc.py +861 -0
  59. cadence/api/v1/tasklist_pb2.py +78 -0
  60. cadence/api/v1/tasklist_pb2.pyi +147 -0
  61. cadence/api/v1/tasklist_pb2_grpc.py +24 -0
  62. cadence/api/v1/visibility_pb2.py +47 -0
  63. cadence/api/v1/visibility_pb2.pyi +53 -0
  64. cadence/api/v1/visibility_pb2_grpc.py +24 -0
  65. cadence/api/v1/workflow_pb2.py +89 -0
  66. cadence/api/v1/workflow_pb2.pyi +365 -0
  67. cadence/api/v1/workflow_pb2_grpc.py +24 -0
  68. cadence/client.py +382 -0
  69. cadence/data_converter.py +78 -0
  70. cadence/error.py +111 -0
  71. cadence/metrics/__init__.py +12 -0
  72. cadence/metrics/constants.py +136 -0
  73. cadence/metrics/metrics.py +56 -0
  74. cadence/metrics/prometheus.py +165 -0
  75. cadence/sample/__init__.py +1 -0
  76. cadence/sample/client_example.py +15 -0
  77. cadence/sample/grpc_usage_example.py +230 -0
  78. cadence/sample/simple_usage_example.py +155 -0
  79. cadence/signal.py +174 -0
  80. cadence/worker/__init__.py +13 -0
  81. cadence/worker/_activity.py +60 -0
  82. cadence/worker/_base_task_handler.py +71 -0
  83. cadence/worker/_decision.py +62 -0
  84. cadence/worker/_decision_task_handler.py +285 -0
  85. cadence/worker/_poller.py +64 -0
  86. cadence/worker/_registry.py +245 -0
  87. cadence/worker/_types.py +26 -0
  88. cadence/worker/_worker.py +56 -0
  89. cadence/workflow.py +271 -0
  90. cadence_python_client-0.1.0.dist-info/METADATA +180 -0
  91. cadence_python_client-0.1.0.dist-info/RECORD +95 -0
  92. cadence_python_client-0.1.0.dist-info/WHEEL +5 -0
  93. cadence_python_client-0.1.0.dist-info/licenses/LICENSE +201 -0
  94. cadence_python_client-0.1.0.dist-info/licenses/NOTICE +19 -0
  95. cadence_python_client-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,136 @@
1
+ """Metric name constants for Cadence client matching Go client naming convention."""
2
+
3
+ # Metric name prefix
4
+ CADENCE_METRICS_PREFIX = "cadence-"
5
+
6
+ # Workflow Creation metrics
7
+ WORKFLOW_START_COUNTER = CADENCE_METRICS_PREFIX + "workflow-start"
8
+ WORKFLOW_START_ASYNC_COUNTER = CADENCE_METRICS_PREFIX + "workflow-start-async"
9
+ WORKFLOW_COMPLETED_COUNTER = CADENCE_METRICS_PREFIX + "workflow-completed"
10
+ WORKFLOW_CANCELED_COUNTER = CADENCE_METRICS_PREFIX + "workflow-canceled"
11
+ WORKFLOW_FAILED_COUNTER = CADENCE_METRICS_PREFIX + "workflow-failed"
12
+ WORKFLOW_CONTINUE_AS_NEW_COUNTER = CADENCE_METRICS_PREFIX + "workflow-continue-as-new"
13
+ WORKFLOW_END_TO_END_LATENCY = CADENCE_METRICS_PREFIX + "workflow-endtoend-latency"
14
+ WORKFLOW_GET_HISTORY_COUNTER = CADENCE_METRICS_PREFIX + "workflow-get-history-total"
15
+ WORKFLOW_GET_HISTORY_FAILED_COUNTER = (
16
+ CADENCE_METRICS_PREFIX + "workflow-get-history-failed"
17
+ )
18
+ WORKFLOW_GET_HISTORY_SUCCEED_COUNTER = (
19
+ CADENCE_METRICS_PREFIX + "workflow-get-history-succeed"
20
+ )
21
+ WORKFLOW_GET_HISTORY_LATENCY = CADENCE_METRICS_PREFIX + "workflow-get-history-latency"
22
+ WORKFLOW_SIGNAL_WITH_START_COUNTER = (
23
+ CADENCE_METRICS_PREFIX + "workflow-signal-with-start"
24
+ )
25
+ WORKFLOW_SIGNAL_WITH_START_ASYNC_COUNTER = (
26
+ CADENCE_METRICS_PREFIX + "workflow-signal-with-start-async"
27
+ )
28
+ DECISION_TIMEOUT_COUNTER = CADENCE_METRICS_PREFIX + "decision-timeout"
29
+
30
+ # Decision Poll metrics
31
+ DECISION_POLL_COUNTER = CADENCE_METRICS_PREFIX + "decision-poll-total"
32
+ DECISION_POLL_FAILED_COUNTER = CADENCE_METRICS_PREFIX + "decision-poll-failed"
33
+ DECISION_POLL_TRANSIENT_FAILED_COUNTER = (
34
+ CADENCE_METRICS_PREFIX + "decision-poll-transient-failed"
35
+ )
36
+ DECISION_POLL_NO_TASK_COUNTER = CADENCE_METRICS_PREFIX + "decision-poll-no-task"
37
+ DECISION_POLL_SUCCEED_COUNTER = CADENCE_METRICS_PREFIX + "decision-poll-succeed"
38
+ DECISION_POLL_LATENCY = CADENCE_METRICS_PREFIX + "decision-poll-latency"
39
+ DECISION_POLL_INVALID_COUNTER = CADENCE_METRICS_PREFIX + "decision-poll-invalid"
40
+ DECISION_SCHEDULED_TO_START_LATENCY = (
41
+ CADENCE_METRICS_PREFIX + "decision-scheduled-to-start-latency"
42
+ )
43
+ DECISION_EXECUTION_FAILED_COUNTER = CADENCE_METRICS_PREFIX + "decision-execution-failed"
44
+ DECISION_EXECUTION_LATENCY = CADENCE_METRICS_PREFIX + "decision-execution-latency"
45
+ DECISION_RESPONSE_FAILED_COUNTER = CADENCE_METRICS_PREFIX + "decision-response-failed"
46
+ DECISION_RESPONSE_LATENCY = CADENCE_METRICS_PREFIX + "decision-response-latency"
47
+ DECISION_TASK_PANIC_COUNTER = CADENCE_METRICS_PREFIX + "decision-task-panic"
48
+ DECISION_TASK_COMPLETED_COUNTER = CADENCE_METRICS_PREFIX + "decision-task-completed"
49
+ DECISION_TASK_FORCE_COMPLETED_COUNTER = (
50
+ CADENCE_METRICS_PREFIX + "decision-task-force-completed"
51
+ )
52
+
53
+ # Activity Poll metrics
54
+ ACTIVITY_POLL_COUNTER = CADENCE_METRICS_PREFIX + "activity-poll-total"
55
+ ACTIVITY_POLL_FAILED_COUNTER = CADENCE_METRICS_PREFIX + "activity-poll-failed"
56
+ ACTIVITY_POLL_TRANSIENT_FAILED_COUNTER = (
57
+ CADENCE_METRICS_PREFIX + "activity-poll-transient-failed"
58
+ )
59
+ ACTIVITY_POLL_NO_TASK_COUNTER = CADENCE_METRICS_PREFIX + "activity-poll-no-task"
60
+ ACTIVITY_POLL_SUCCEED_COUNTER = CADENCE_METRICS_PREFIX + "activity-poll-succeed"
61
+ ACTIVITY_POLL_LATENCY = CADENCE_METRICS_PREFIX + "activity-poll-latency"
62
+ ACTIVITY_SCHEDULED_TO_START_LATENCY = (
63
+ CADENCE_METRICS_PREFIX + "activity-scheduled-to-start-latency"
64
+ )
65
+ ACTIVITY_EXECUTION_FAILED_COUNTER = CADENCE_METRICS_PREFIX + "activity-execution-failed"
66
+ ACTIVITY_EXECUTION_LATENCY = CADENCE_METRICS_PREFIX + "activity-execution-latency"
67
+ ACTIVITY_RESPONSE_LATENCY = CADENCE_METRICS_PREFIX + "activity-response-latency"
68
+ ACTIVITY_RESPONSE_FAILED_COUNTER = CADENCE_METRICS_PREFIX + "activity-response-failed"
69
+ ACTIVITY_END_TO_END_LATENCY = CADENCE_METRICS_PREFIX + "activity-endtoend-latency"
70
+ ACTIVITY_TASK_PANIC_COUNTER = CADENCE_METRICS_PREFIX + "activity-task-panic"
71
+ ACTIVITY_TASK_COMPLETED_COUNTER = CADENCE_METRICS_PREFIX + "activity-task-completed"
72
+ ACTIVITY_TASK_FAILED_COUNTER = CADENCE_METRICS_PREFIX + "activity-task-failed"
73
+ ACTIVITY_TASK_CANCELED_COUNTER = CADENCE_METRICS_PREFIX + "activity-task-canceled"
74
+ ACTIVITY_TASK_COMPLETED_BY_ID_COUNTER = (
75
+ CADENCE_METRICS_PREFIX + "activity-task-completed-by-id"
76
+ )
77
+ ACTIVITY_TASK_FAILED_BY_ID_COUNTER = (
78
+ CADENCE_METRICS_PREFIX + "activity-task-failed-by-id"
79
+ )
80
+ ACTIVITY_TASK_CANCELED_BY_ID_COUNTER = (
81
+ CADENCE_METRICS_PREFIX + "activity-task-canceled-by-id"
82
+ )
83
+
84
+ # Local Activity metrics
85
+ LOCAL_ACTIVITY_TOTAL_COUNTER = CADENCE_METRICS_PREFIX + "local-activity-total"
86
+ LOCAL_ACTIVITY_TIMEOUT_COUNTER = CADENCE_METRICS_PREFIX + "local-activity-timeout"
87
+ LOCAL_ACTIVITY_CANCELED_COUNTER = CADENCE_METRICS_PREFIX + "local-activity-canceled"
88
+ LOCAL_ACTIVITY_FAILED_COUNTER = CADENCE_METRICS_PREFIX + "local-activity-failed"
89
+ LOCAL_ACTIVITY_PANIC_COUNTER = CADENCE_METRICS_PREFIX + "local-activity-panic"
90
+ LOCAL_ACTIVITY_EXECUTION_LATENCY = (
91
+ CADENCE_METRICS_PREFIX + "local-activity-execution-latency"
92
+ )
93
+ LOCALLY_DISPATCHED_ACTIVITY_POLL_COUNTER = (
94
+ CADENCE_METRICS_PREFIX + "locally-dispatched-activity-poll-total"
95
+ )
96
+ LOCALLY_DISPATCHED_ACTIVITY_POLL_NO_TASK_COUNTER = (
97
+ CADENCE_METRICS_PREFIX + "locally-dispatched-activity-poll-no-task"
98
+ )
99
+ LOCALLY_DISPATCHED_ACTIVITY_POLL_SUCCEED_COUNTER = (
100
+ CADENCE_METRICS_PREFIX + "locally-dispatched-activity-poll-succeed"
101
+ )
102
+ ACTIVITY_LOCAL_DISPATCH_FAILED_COUNTER = (
103
+ CADENCE_METRICS_PREFIX + "activity-local-dispatch-failed"
104
+ )
105
+ ACTIVITY_LOCAL_DISPATCH_SUCCEED_COUNTER = (
106
+ CADENCE_METRICS_PREFIX + "activity-local-dispatch-succeed"
107
+ )
108
+ WORKER_PANIC_COUNTER = CADENCE_METRICS_PREFIX + "worker-panic"
109
+
110
+ # Signal metrics
111
+ UNHANDLED_SIGNALS_COUNTER = CADENCE_METRICS_PREFIX + "unhandled-signals"
112
+ CORRUPTED_SIGNALS_COUNTER = CADENCE_METRICS_PREFIX + "corrupted-signals"
113
+
114
+ # Worker metrics
115
+ WORKER_START_COUNTER = CADENCE_METRICS_PREFIX + "worker-start"
116
+ POLLER_START_COUNTER = CADENCE_METRICS_PREFIX + "poller-start"
117
+
118
+ # Client metrics
119
+ CADENCE_REQUEST = CADENCE_METRICS_PREFIX + "request"
120
+ CADENCE_ERROR = CADENCE_METRICS_PREFIX + "error"
121
+ CADENCE_LATENCY = CADENCE_METRICS_PREFIX + "latency"
122
+ CADENCE_INVALID_REQUEST = CADENCE_METRICS_PREFIX + "invalid-request"
123
+
124
+ # Sticky Cache metrics
125
+ STICKY_CACHE_HIT = CADENCE_METRICS_PREFIX + "sticky-cache-hit"
126
+ STICKY_CACHE_MISS = CADENCE_METRICS_PREFIX + "sticky-cache-miss"
127
+ STICKY_CACHE_EVICT = CADENCE_METRICS_PREFIX + "sticky-cache-evict"
128
+ STICKY_CACHE_STALL = CADENCE_METRICS_PREFIX + "sticky-cache-stall"
129
+ STICKY_CACHE_SIZE = CADENCE_METRICS_PREFIX + "sticky-cache-size"
130
+
131
+ # Replay metrics
132
+ NON_DETERMINISTIC_ERROR = CADENCE_METRICS_PREFIX + "non-deterministic-error"
133
+ REPLAY_SUCCEED_COUNTER = CADENCE_METRICS_PREFIX + "replay-succeed"
134
+ REPLAY_FAILED_COUNTER = CADENCE_METRICS_PREFIX + "replay-failed"
135
+ REPLAY_SKIPPED_COUNTER = CADENCE_METRICS_PREFIX + "replay-skipped"
136
+ REPLAY_LATENCY = CADENCE_METRICS_PREFIX + "replay-latency"
@@ -0,0 +1,56 @@
1
+ """Core metrics collection interface and registry for Cadence client."""
2
+
3
+ import logging
4
+ from enum import Enum
5
+ from typing import Dict, Optional, Protocol
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ class MetricType(Enum):
11
+ """Types of metrics that can be collected."""
12
+
13
+ COUNTER = "counter"
14
+ GAUGE = "gauge"
15
+ HISTOGRAM = "histogram"
16
+
17
+
18
+ class MetricsEmitter(Protocol):
19
+ """Protocol for metrics collection backends."""
20
+
21
+ def counter(
22
+ self, key: str, n: int = 1, tags: Optional[Dict[str, str]] = None
23
+ ) -> None:
24
+ """Send a counter metric."""
25
+ ...
26
+
27
+ def gauge(
28
+ self, key: str, value: float, tags: Optional[Dict[str, str]] = None
29
+ ) -> None:
30
+ """Send a gauge metric."""
31
+ ...
32
+
33
+ def histogram(
34
+ self, key: str, value: float, tags: Optional[Dict[str, str]] = None
35
+ ) -> None:
36
+ """Send a histogram metric."""
37
+ ...
38
+
39
+
40
+ class NoOpMetricsEmitter:
41
+ """No-op metrics emitter that discards all metrics."""
42
+
43
+ def counter(
44
+ self, key: str, n: int = 1, tags: Optional[Dict[str, str]] = None
45
+ ) -> None:
46
+ pass
47
+
48
+ def gauge(
49
+ self, key: str, value: float, tags: Optional[Dict[str, str]] = None
50
+ ) -> None:
51
+ pass
52
+
53
+ def histogram(
54
+ self, key: str, value: float, tags: Optional[Dict[str, str]] = None
55
+ ) -> None:
56
+ pass
@@ -0,0 +1,165 @@
1
+ """Prometheus metrics integration for Cadence client."""
2
+
3
+ import logging
4
+ from dataclasses import dataclass, field
5
+ from typing import Dict, Optional
6
+
7
+ from prometheus_client import ( # type: ignore[import-not-found]
8
+ REGISTRY,
9
+ CollectorRegistry,
10
+ Counter,
11
+ Gauge,
12
+ Histogram,
13
+ generate_latest,
14
+ )
15
+
16
+ from .metrics import MetricsEmitter
17
+
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ @dataclass
23
+ class PrometheusConfig:
24
+ """Configuration for Prometheus metrics."""
25
+
26
+ # Default labels to apply to all metrics
27
+ default_labels: Dict[str, str] = field(default_factory=dict)
28
+
29
+ # Custom registry (if None, uses default global registry)
30
+ registry: Optional[CollectorRegistry] = None
31
+
32
+
33
+ class PrometheusMetrics(MetricsEmitter):
34
+ """Prometheus metrics collector implementation."""
35
+
36
+ def __init__(self, config: Optional[PrometheusConfig] = None):
37
+ self.config = config or PrometheusConfig()
38
+ self.registry = self.config.registry or REGISTRY
39
+
40
+ # Track created metrics to avoid duplicates
41
+ self._counters: Dict[str, Counter] = {}
42
+ self._gauges: Dict[str, Gauge] = {}
43
+ self._histograms: Dict[str, Histogram] = {}
44
+
45
+ def _get_metric_name(self, name: str) -> str:
46
+ """Get the metric name."""
47
+ return name
48
+
49
+ def _merge_labels(self, labels: Optional[Dict[str, str]]) -> Dict[str, str]:
50
+ """Merge provided labels with default labels."""
51
+ merged = self.config.default_labels.copy()
52
+ if labels:
53
+ merged.update(labels)
54
+ return merged
55
+
56
+ def _get_or_create_counter(
57
+ self, name: str, labels: Optional[Dict[str, str]]
58
+ ) -> Counter:
59
+ """Get or create a Counter metric."""
60
+ metric_name = self._get_metric_name(name)
61
+
62
+ if metric_name not in self._counters:
63
+ label_names = list(self._merge_labels(labels).keys()) if labels else []
64
+ self._counters[metric_name] = Counter(
65
+ metric_name,
66
+ f"Counter metric for {name}",
67
+ labelnames=label_names,
68
+ registry=self.registry,
69
+ )
70
+ logger.debug(f"Created counter metric: {metric_name}")
71
+
72
+ return self._counters[metric_name]
73
+
74
+ def _get_or_create_gauge(
75
+ self, name: str, labels: Optional[Dict[str, str]]
76
+ ) -> Gauge:
77
+ """Get or create a Gauge metric."""
78
+ metric_name = self._get_metric_name(name)
79
+
80
+ if metric_name not in self._gauges:
81
+ label_names = list(self._merge_labels(labels).keys()) if labels else []
82
+ self._gauges[metric_name] = Gauge(
83
+ metric_name,
84
+ f"Gauge metric for {name}",
85
+ labelnames=label_names,
86
+ registry=self.registry,
87
+ )
88
+ logger.debug(f"Created gauge metric: {metric_name}")
89
+
90
+ return self._gauges[metric_name]
91
+
92
+ def _get_or_create_histogram(
93
+ self, name: str, labels: Optional[Dict[str, str]]
94
+ ) -> Histogram:
95
+ """Get or create a Histogram metric."""
96
+ metric_name = self._get_metric_name(name)
97
+
98
+ if metric_name not in self._histograms:
99
+ label_names = list(self._merge_labels(labels).keys()) if labels else []
100
+ self._histograms[metric_name] = Histogram(
101
+ metric_name,
102
+ f"Histogram metric for {name}",
103
+ labelnames=label_names,
104
+ registry=self.registry,
105
+ )
106
+ logger.debug(f"Created histogram metric: {metric_name}")
107
+
108
+ return self._histograms[metric_name]
109
+
110
+ def counter(
111
+ self, key: str, n: int = 1, tags: Optional[Dict[str, str]] = None
112
+ ) -> None:
113
+ """Send a counter metric."""
114
+ try:
115
+ counter = self._get_or_create_counter(key, tags)
116
+ merged_tags = self._merge_labels(tags)
117
+
118
+ if merged_tags:
119
+ counter.labels(**merged_tags).inc(n)
120
+ else:
121
+ counter.inc(n)
122
+
123
+ except Exception as e:
124
+ logger.error(f"Failed to send counter {key}: {e}")
125
+
126
+ def gauge(
127
+ self, key: str, value: float, tags: Optional[Dict[str, str]] = None
128
+ ) -> None:
129
+ """Send a gauge metric."""
130
+ try:
131
+ gauge = self._get_or_create_gauge(key, tags)
132
+ merged_tags = self._merge_labels(tags)
133
+
134
+ if merged_tags:
135
+ gauge.labels(**merged_tags).set(value)
136
+ else:
137
+ gauge.set(value)
138
+
139
+ except Exception as e:
140
+ logger.error(f"Failed to send gauge {key}: {e}")
141
+
142
+ def histogram(
143
+ self, key: str, value: float, tags: Optional[Dict[str, str]] = None
144
+ ) -> None:
145
+ """Send a histogram metric."""
146
+ try:
147
+ histogram = self._get_or_create_histogram(key, tags)
148
+ merged_tags = self._merge_labels(tags)
149
+
150
+ if merged_tags:
151
+ histogram.labels(**merged_tags).observe(value)
152
+ else:
153
+ histogram.observe(value)
154
+
155
+ except Exception as e:
156
+ logger.error(f"Failed to send histogram {key}: {e}")
157
+
158
+ def get_metrics_text(self) -> str:
159
+ """Get metrics in Prometheus text format."""
160
+ try:
161
+ metrics_bytes = generate_latest(self.registry)
162
+ return metrics_bytes.decode("utf-8") # type: ignore[no-any-return]
163
+ except Exception as e:
164
+ logger.error(f"Failed to generate metrics text: {e}")
165
+ return ""
@@ -0,0 +1 @@
1
+ # Sample directory for cadence protobuf import tests
@@ -0,0 +1,15 @@
1
+ import asyncio
2
+
3
+
4
+ from cadence.client import Client
5
+ from cadence.worker import Worker, Registry
6
+
7
+
8
+ async def main():
9
+ async with Client(target="localhost:7833", domain="foo") as client:
10
+ worker = Worker(client, "task_list", Registry())
11
+ await worker.run()
12
+
13
+
14
+ if __name__ == "__main__":
15
+ asyncio.run(main())
@@ -0,0 +1,230 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Example demonstrating how to use the generated gRPC code for Cadence services.
4
+ This example shows how to create a gRPC client and make calls to Cadence workflow services.
5
+ """
6
+
7
+ import grpc
8
+ from cadence.api.v1 import service_workflow_grpc, service_workflow, common
9
+
10
+
11
+ def create_grpc_channel(
12
+ server_address: str = "localhost:7833", use_ssl: bool = False
13
+ ) -> grpc.Channel:
14
+ """
15
+ Create a gRPC channel to connect to Cadence server.
16
+
17
+ Args:
18
+ server_address: The address of the Cadence server (host:port)
19
+ use_ssl: Whether to use SSL/TLS for the connection
20
+
21
+ Returns:
22
+ grpc.Channel: The gRPC channel
23
+ """
24
+ if use_ssl:
25
+ # For SSL connections, you would typically use credentials
26
+ credentials = grpc.ssl_channel_credentials()
27
+ return grpc.secure_channel(server_address, credentials)
28
+ else:
29
+ # For insecure connections (development)
30
+ return grpc.insecure_channel(server_address)
31
+
32
+
33
+ def create_workflow_client(
34
+ channel: grpc.Channel,
35
+ ) -> service_workflow_grpc.WorkflowAPIStub:
36
+ """
37
+ Create a gRPC client for the WorkflowAPI service.
38
+
39
+ Args:
40
+ channel: The gRPC channel
41
+
42
+ Returns:
43
+ WorkflowAPIStub: The gRPC client stub
44
+ """
45
+ return service_workflow_grpc.WorkflowAPIStub(channel)
46
+
47
+
48
+ def example_start_workflow(
49
+ client: service_workflow_grpc.WorkflowAPIStub, domain: str, workflow_id: str
50
+ ):
51
+ """
52
+ Example of starting a workflow execution using gRPC.
53
+
54
+ Args:
55
+ client: The gRPC client
56
+ domain: The Cadence domain
57
+ workflow_id: The workflow ID
58
+ """
59
+ # Create the request message
60
+ request = service_workflow.StartWorkflowExecutionRequest()
61
+ request.domain = domain
62
+ request.workflow_id = workflow_id
63
+ request.workflow_type.name = "MyWorkflow"
64
+ request.task_list.name = "my-task-list"
65
+ request.input.data = b"workflow input data" # Serialized workflow input
66
+ request.execution_start_to_close_timeout.seconds = 3600 # 1 hour
67
+ request.task_start_to_close_timeout.seconds = 60 # 1 minute
68
+ request.identity = "python-client"
69
+
70
+ try:
71
+ # Make the gRPC call
72
+ response = client.StartWorkflowExecution(request)
73
+ print(f"✓ Workflow started successfully: {response}")
74
+ return response
75
+ except grpc.RpcError as e:
76
+ print(f"✗ Failed to start workflow: {e}")
77
+ return None
78
+
79
+
80
+ def example_describe_workflow(
81
+ client: service_workflow_grpc.WorkflowAPIStub,
82
+ domain: str,
83
+ workflow_id: str,
84
+ run_id: str,
85
+ ):
86
+ """
87
+ Example of describing a workflow execution using gRPC.
88
+
89
+ Args:
90
+ client: The gRPC client
91
+ domain: The Cadence domain
92
+ workflow_id: The workflow ID
93
+ run_id: The workflow run ID
94
+ """
95
+ # Create the request message
96
+ request = service_workflow.DescribeWorkflowExecutionRequest()
97
+ request.domain = domain
98
+ execution = common.WorkflowExecution()
99
+ execution.workflow_id = workflow_id
100
+ execution.run_id = run_id
101
+ request.workflow_execution.CopyFrom(execution)
102
+
103
+ try:
104
+ # Make the gRPC call
105
+ response = client.DescribeWorkflowExecution(request)
106
+ print(f"✓ Workflow description: {response}")
107
+ return response
108
+ except grpc.RpcError as e:
109
+ print(f"✗ Failed to describe workflow: {e}")
110
+ return None
111
+
112
+
113
+ def example_get_workflow_history(
114
+ client: service_workflow_grpc.WorkflowAPIStub,
115
+ domain: str,
116
+ workflow_id: str,
117
+ run_id: str,
118
+ ):
119
+ """
120
+ Example of getting workflow execution history using gRPC.
121
+
122
+ Args:
123
+ client: The gRPC client
124
+ domain: The Cadence domain
125
+ workflow_id: The workflow ID
126
+ run_id: The workflow run ID
127
+ """
128
+ # Create the request message
129
+ request = service_workflow.GetWorkflowExecutionHistoryRequest()
130
+ request.domain = domain
131
+ execution = common.WorkflowExecution()
132
+ execution.workflow_id = workflow_id
133
+ execution.run_id = run_id
134
+ request.workflow_execution.CopyFrom(execution)
135
+ request.page_size = 100
136
+
137
+ try:
138
+ # Make the gRPC call
139
+ response = client.GetWorkflowExecutionHistory(request)
140
+ print(f"✓ Workflow history retrieved: {len(response.history.events)} events")
141
+ return response
142
+ except grpc.RpcError as e:
143
+ print(f"✗ Failed to get workflow history: {e}")
144
+ return None
145
+
146
+
147
+ def example_query_workflow(
148
+ client: service_workflow_grpc.WorkflowAPIStub,
149
+ domain: str,
150
+ workflow_id: str,
151
+ run_id: str,
152
+ query_type: str,
153
+ ):
154
+ """
155
+ Example of querying a workflow using gRPC.
156
+
157
+ Args:
158
+ client: The gRPC client
159
+ domain: The Cadence domain
160
+ workflow_id: The workflow ID
161
+ run_id: The workflow run ID
162
+ query_type: The type of query to execute
163
+ """
164
+ # Create the request message
165
+ request = service_workflow.QueryWorkflowRequest()
166
+ request.domain = domain
167
+ execution = common.WorkflowExecution()
168
+ execution.workflow_id = workflow_id
169
+ execution.run_id = run_id
170
+ request.workflow_execution.CopyFrom(execution)
171
+ request.query.query_type = query_type
172
+ request.query.query_args.data = b"query arguments" # Serialized query arguments
173
+
174
+ try:
175
+ # Make the gRPC call
176
+ response = client.QueryWorkflow(request)
177
+ print(f"✓ Workflow query result: {response}")
178
+ return response
179
+ except grpc.RpcError as e:
180
+ print(f"✗ Failed to query workflow: {e}")
181
+ return None
182
+
183
+
184
+ def main():
185
+ """Main example function."""
186
+ print("Cadence gRPC Client Example")
187
+ print("=" * 40)
188
+
189
+ # Configuration
190
+ server_address = "localhost:7833" # Default Cadence gRPC port
191
+ domain = "test-domain"
192
+ workflow_id = "example-workflow-123"
193
+ run_id = "example-run-456"
194
+
195
+ try:
196
+ # Create gRPC channel
197
+ print(f"Connecting to Cadence server at {server_address}...")
198
+ channel = create_grpc_channel(server_address)
199
+
200
+ # Create gRPC client
201
+ client = create_workflow_client(channel)
202
+ print("✓ gRPC client created successfully")
203
+
204
+ # Example 1: Start a workflow
205
+ print("\n1. Starting a workflow...")
206
+ example_start_workflow(client, domain, workflow_id)
207
+
208
+ # Example 2: Describe a workflow
209
+ print("\n2. Describing a workflow...")
210
+ example_describe_workflow(client, domain, workflow_id, run_id)
211
+
212
+ # Example 3: Get workflow history
213
+ print("\n3. Getting workflow history...")
214
+ example_get_workflow_history(client, domain, workflow_id, run_id)
215
+
216
+ # Example 4: Query a workflow
217
+ print("\n4. Querying a workflow...")
218
+ example_query_workflow(client, domain, workflow_id, run_id, "status")
219
+
220
+ except Exception as e:
221
+ print(f"✗ Error: {e}")
222
+ finally:
223
+ # Close the channel
224
+ if "channel" in locals():
225
+ channel.close()
226
+ print("\n✓ gRPC channel closed")
227
+
228
+
229
+ if __name__ == "__main__":
230
+ main()