Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions sdk/docs/async.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ Same constructor options as `ConfigClient` — see [Configuration](configuration
from opendecree import AsyncConfigClient

async with AsyncConfigClient("localhost:9090", subject="myapp") as client:
async with client.watch("tenant-id") as watcher:
fee = watcher.field("payments.fee", float, default=0.01)
enabled = watcher.field("payments.enabled", bool, default=False)
watcher = client.watch("tenant-id")
fee = watcher.field("payments.fee", float, default=0.01)
enabled = watcher.field("payments.enabled", bool, default=False)

async with watcher:
# .value works the same
print(fee.value)

Expand All @@ -47,9 +48,10 @@ async with AsyncConfigClient("localhost:9090", subject="myapp") as client:
Use `async for` instead of `for`:

```python
async with client.watch("tenant-id") as watcher:
fee = watcher.field("payments.fee", float, default=0.01)
watcher = client.watch("tenant-id")
fee = watcher.field("payments.fee", float, default=0.01)

async with watcher:
async for change in fee.changes():
print(f"{change.old_value} -> {change.new_value}")
```
Expand Down
13 changes: 7 additions & 6 deletions sdk/docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,15 @@ with ConfigClient("localhost:9090", subject="myapp") as client:

```python
with ConfigClient("localhost:9090", subject="myapp") as client:
with client.watch("tenant-id") as watcher:
fee = watcher.field("payments.fee", float, default=0.01)
watcher = client.watch("tenant-id")
fee = watcher.field("payments.fee", float, default=0.01)

print(fee.value) # current value, always fresh
@fee.on_change
def on_fee_change(old: float, new: float):
print(f"Fee changed: {old} -> {new}")

@fee.on_change
def on_fee_change(old: float, new: float):
print(f"Fee changed: {old} -> {new}")
with watcher:
print(fee.value) # current value, always fresh
```

See [Watching](watching.md) for more patterns.
Expand Down
36 changes: 15 additions & 21 deletions sdk/docs/watching.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ Live config subscriptions via `ConfigWatcher` and `WatchedField[T]`.

## Basic usage

Create a watcher from a client. Register fields before entering the context manager:
Create a watcher from a client. Register fields before starting the watcher:

```python
from opendecree import ConfigClient

with ConfigClient("localhost:9090", subject="myapp") as client:
with client.watch("tenant-id") as watcher:
fee = watcher.field("payments.fee", float, default=0.01)
enabled = watcher.field("payments.enabled", bool, default=False)
watcher = client.watch("tenant-id")
fee = watcher.field("payments.fee", float, default=0.01)
enabled = watcher.field("payments.enabled", bool, default=False)

with watcher:
# Read current values
print(fee.value) # 0.025 (float, always fresh)
print(enabled.value) # True (bool)
Expand Down Expand Up @@ -79,29 +80,19 @@ The iterator blocks until a change arrives. It stops when the watcher exits.

## Lifecycle

Register fields **before** the `with` block. Fields cannot be added after the watcher starts:
Register fields **before** starting the watcher. Calling `field()` after `start()` raises `RuntimeError`:

```python
watcher = client.watch("tenant-id")

# Register fields first
fee = watcher.field("payments.fee", float, default=0.01)

# Then start
# Then start — loads snapshot and subscribes
with watcher:
print(fee.value)
```

Or equivalently:

```python
with client.watch("tenant-id") as watcher:
fee = watcher.field("payments.fee", float, default=0.01)
# ...
```

Wait — fields must be registered **before** `start()`. When using the two-line form, register between `watch()` and `with`. When using the one-line form, the watcher loads a snapshot on enter, so fields registered inside the `with` block will get their initial values from the snapshot.

## Auto-reconnect

If the gRPC stream drops (server restart, network issue), the watcher automatically reconnects with exponential backoff:
Expand All @@ -119,11 +110,14 @@ You can create multiple watchers for different tenants:

```python
with ConfigClient("localhost:9090", subject="myapp") as client:
with client.watch("tenant-a") as watcher_a:
with client.watch("tenant-b") as watcher_b:
fee_a = watcher_a.field("payments.fee", float, default=0.01)
fee_b = watcher_b.field("payments.fee", float, default=0.01)
# Both update independently
watcher_a = client.watch("tenant-a")
watcher_b = client.watch("tenant-b")
fee_a = watcher_a.field("payments.fee", float, default=0.01)
fee_b = watcher_b.field("payments.fee", float, default=0.01)

with watcher_a, watcher_b:
# Both update independently
print(fee_a.value, fee_b.value)
```

## Next steps
Expand Down