Skip to content

Commit e1dfe5c

Browse files
committed
Update readme
1 parent 759e650 commit e1dfe5c

File tree

1 file changed

+135
-0
lines changed

1 file changed

+135
-0
lines changed

README.md

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ informal introduction to the features and their implementation.
9595
- [Worker Shutdown](#worker-shutdown)
9696
- [Testing](#testing-1)
9797
- [Nexus](#nexus)
98+
- [Plugins](#plugins)
99+
- [Client Plugins](#client-plugins)
100+
- [Worker Plugins](#worker-plugins)
98101
- [Workflow Replay](#workflow-replay)
99102
- [Observability](#observability)
100103
- [Metrics](#metrics)
@@ -1416,6 +1419,138 @@ https://github.com/temporalio/samples-python/tree/nexus/hello_nexus).
14161419
```
14171420
14181421
1422+
### Plugins
1423+
1424+
Plugins provide a way to extend and customize the behavior of Temporal clients and workers through a chain of
1425+
responsibility pattern. They allow you to intercept and modify client creation, service connections, worker
1426+
configuration, and worker execution. Common customizations may include but are not limited to:
1427+
1428+
1. DataConverter
1429+
2. Activities
1430+
3. Workflows
1431+
4. Interceptors
1432+
1433+
A single plugin class can implement both client and worker plugin interfaces to share common logic between both
1434+
contexts. When used with a client, it will automatically be propagated to any workers created with that client.
1435+
1436+
#### Client Plugins
1437+
1438+
Client plugins can intercept and modify client configuration and service connections. They are useful for adding
1439+
authentication, modifying connection parameters, or adding custom behavior during client creation.
1440+
1441+
Here's an example of a client plugin that adds custom authentication:
1442+
1443+
```python
1444+
from temporalio.client import Plugin, ClientConfig
1445+
import temporalio.service
1446+
1447+
class AuthenticationPlugin(Plugin):
1448+
def __init__(self, api_key: str):
1449+
self.api_key = api_key
1450+
1451+
def on_create_client(self, config: ClientConfig) -> ClientConfig:
1452+
# Modify client configuration
1453+
config["namespace"] = "my-secure-namespace"
1454+
return super().on_create_client(config)
1455+
1456+
async def connect_service_client(
1457+
self, config: temporalio.service.ConnectConfig
1458+
) -> temporalio.service.ServiceClient:
1459+
# Add authentication to the connection
1460+
config.api_key = self.api_key
1461+
return await super().connect_service_client(config)
1462+
1463+
# Use the plugin when connecting
1464+
client = await Client.connect(
1465+
"my-server.com:7233",
1466+
plugins=[AuthenticationPlugin("my-api-key")]
1467+
)
1468+
```
1469+
1470+
#### Worker Plugins
1471+
1472+
Worker plugins can modify worker configuration and intercept worker execution. They are useful for adding monitoring,
1473+
custom lifecycle management, or modifying worker settings.
1474+
1475+
Here's an example of a worker plugin that adds custom monitoring:
1476+
1477+
```python
1478+
from temporalio.worker import Plugin, WorkerConfig, Worker
1479+
import logging
1480+
1481+
class MonitoringPlugin(Plugin):
1482+
def __init__(self):
1483+
self.logger = logging.getLogger(__name__)
1484+
1485+
def on_create_worker(self, config: WorkerConfig) -> WorkerConfig:
1486+
# Modify worker configuration
1487+
original_task_queue = config["task_queue"]
1488+
config["task_queue"] = f"monitored-{original_task_queue}"
1489+
self.logger.info(f"Worker created for task queue: {config['task_queue']}")
1490+
return super().on_create_worker(config)
1491+
1492+
async def run_worker(self, worker: Worker) -> None:
1493+
self.logger.info("Starting worker execution")
1494+
try:
1495+
await super().run_worker(worker)
1496+
finally:
1497+
self.logger.info("Worker execution completed")
1498+
1499+
# Use the plugin when creating a worker
1500+
worker = Worker(
1501+
client,
1502+
task_queue="my-task-queue",
1503+
workflows=[MyWorkflow],
1504+
activities=[my_activity],
1505+
plugins=[MonitoringPlugin()]
1506+
)
1507+
```
1508+
1509+
For plugins that need to work with both clients and workers, you can implement both interfaces in a single class:
1510+
1511+
```python
1512+
from temporalio.client import Plugin as ClientPlugin
1513+
from temporalio.worker import Plugin as WorkerPlugin
1514+
1515+
class UnifiedPlugin(ClientPlugin, WorkerPlugin):
1516+
def on_create_client(self, config: ClientConfig) -> ClientConfig:
1517+
# Client-side customization
1518+
config["namespace"] = "unified-namespace"
1519+
return super().on_create_client(config)
1520+
1521+
def on_create_worker(self, config: WorkerConfig) -> WorkerConfig:
1522+
# Worker-side customization
1523+
config["max_cached_workflows"] = 500
1524+
return super().on_create_worker(config)
1525+
1526+
async def run_worker(self, worker: Worker) -> None:
1527+
print("Starting unified worker")
1528+
await super().run_worker(worker)
1529+
1530+
# Create client with the unified plugin
1531+
client = await Client.connect(
1532+
"localhost:7233",
1533+
plugins=[UnifiedPlugin()]
1534+
)
1535+
1536+
# Worker will automatically inherit the plugin from the client
1537+
worker = Worker(
1538+
client,
1539+
task_queue="my-task-queue",
1540+
workflows=[MyWorkflow],
1541+
activities=[my_activity]
1542+
)
1543+
```
1544+
1545+
**Important Notes:**
1546+
1547+
- Plugins are executed in reverse order (last plugin wraps the first), forming a chain of responsibility
1548+
- Client plugins that also implement worker plugin interfaces are automatically propagated to workers
1549+
- Avoid providing the same plugin to both client and worker to prevent double execution
1550+
- Plugin methods should call `super()` to maintain the plugin chain
1551+
- Each plugin's `name()` method returns a unique identifier for debugging purposes
1552+
1553+
14191554
### Workflow Replay
14201555

14211556
Given a workflow's history, it can be replayed locally to check for things like non-determinism errors. For example,

0 commit comments

Comments
 (0)