Skip to content

Commit 1ab2c83

Browse files
authored
Change timeout option from absolute to relative time (#236)
* Change timeout option from absolute to relative time * Fix failing tests * Use clock from dependencies
1 parent 108f3e2 commit 1ab2c83

30 files changed

+430
-330
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,4 @@ jobs:
6969
- name: run tests
7070
env:
7171
RESONATE_HOST: http://localhost
72-
run: uv run pytest tests
72+
run: uv run pytest -s

resonate/bridge.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def run(self, conv: Convention, func: Callable, args: tuple, kwargs: dict, opts:
9595
promise, task = self._store.promises.create_with_task(
9696
id=conv.id,
9797
ikey=conv.idempotency_key,
98-
timeout=conv.timeout,
98+
timeout=int((time.time() + conv.timeout) * 1000),
9999
headers=conv.headers,
100100
data=conv.data,
101101
tags=conv.tags,
@@ -113,7 +113,7 @@ def run(self, conv: Convention, func: Callable, args: tuple, kwargs: dict, opts:
113113
elif task is not None:
114114
self._promise_id_to_task[promise.id] = task
115115
self.start_heartbeat()
116-
self._cq.put_nowait((Invoke(conv.id, conv, func, args, kwargs, opts, promise), future))
116+
self._cq.put_nowait((Invoke(conv.id, conv, promise.abs_timeout, func, args, kwargs, opts, promise), future))
117117
else:
118118
self._cq.put_nowait((Listen(promise.id), future))
119119

@@ -123,7 +123,7 @@ def rpc(self, conv: Convention, future: Future) -> DurablePromise:
123123
promise = self._store.promises.create(
124124
id=conv.id,
125125
ikey=conv.idempotency_key,
126-
timeout=conv.timeout,
126+
timeout=int((time.time() + conv.timeout) * 1000),
127127
headers=conv.headers,
128128
data=conv.data,
129129
tags=conv.tags,
@@ -259,12 +259,13 @@ def _invoke(root: DurablePromise) -> Invoke:
259259
root.id,
260260
Base(
261261
root.id,
262-
root.timeout,
262+
root.rel_timeout,
263263
root.ikey_for_create,
264264
root.param.headers,
265265
root.param.data,
266266
root.tags,
267267
),
268+
root.abs_timeout,
268269
func,
269270
root.param.data.get("args", ()),
270271
root.param.data.get("kwargs", {}),

resonate/clocks/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from __future__ import annotations
22

33
from .step import StepClock
4-
from .wall import WallClock
54

6-
__all__ = ["StepClock", "WallClock"]
5+
__all__ = ["StepClock"]

resonate/clocks/step.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@
33

44
class StepClock:
55
def __init__(self) -> None:
6-
self._time = 0
6+
self._time = 0.0
77

8-
def time(self) -> int:
9-
return self._time
10-
11-
def set_time(self, time: int) -> None:
8+
def step(self, time: float) -> None:
129
assert time >= self._time, "The arrow of time only flows forward."
1310
self._time = time
11+
12+
def time(self) -> float:
13+
"""Return the current time in seconds."""
14+
return self._time
15+
16+
def strftime(self, format: str, /) -> str:
17+
raise NotImplementedError

resonate/clocks/wall.py

Lines changed: 0 additions & 8 deletions
This file was deleted.

resonate/conventions/base.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
@dataclass
1111
class Base:
1212
id: str
13-
timeout: int
13+
timeout: float
1414
idempotency_key: str | None = None
1515
headers: dict[str, str] | None = None
1616
data: Any = None
@@ -20,9 +20,9 @@ def options(
2020
self,
2121
id: str | None = None,
2222
idempotency_key: str | Callable[[str], str] | None = None,
23-
target: str | None = None,
2423
tags: dict[str, str] | None = None,
25-
timeout: int | None = None,
24+
target: str | None = None,
25+
timeout: float | None = None,
2626
version: int | None = None,
2727
) -> Base:
2828
self.id = id or self.id

resonate/conventions/local.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def data(self) -> Any:
2727
return None
2828

2929
@property
30-
def timeout(self) -> int:
30+
def timeout(self) -> float:
3131
return self.opts.timeout
3232

3333
@property
@@ -38,9 +38,9 @@ def options(
3838
self,
3939
id: str | None = None,
4040
idempotency_key: str | Callable[[str], str] | None = None,
41-
target: str | None = None,
4241
tags: dict[str, str] | None = None,
43-
timeout: int | None = None,
42+
target: str | None = None,
43+
timeout: float | None = None,
4444
version: int | None = None,
4545
) -> Local:
4646
self.id = id or self.id

resonate/conventions/remote.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def data(self) -> dict[str, Any]:
3030
return {"func": self.name, "args": self.args, "kwargs": self.kwargs, "version": self.opts.version}
3131

3232
@property
33-
def timeout(self) -> int:
33+
def timeout(self) -> float:
3434
return self.opts.timeout
3535

3636
@property
@@ -41,8 +41,8 @@ def options(
4141
self,
4242
id: str | None = None,
4343
idempotency_key: str | Callable[[str], str] | None = None,
44-
target: str | None = None,
4544
tags: dict[str, str] | None = None,
45+
target: str | None = None,
4646
timeout: int | None = None,
4747
version: int | None = None,
4848
) -> Remote:

resonate/conventions/sleep.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
@dataclass
1111
class Sleep:
1212
id: str
13-
timeout: int
13+
secs: float
1414

1515
@property
1616
def idempotency_key(self) -> str:
@@ -24,6 +24,10 @@ def headers(self) -> dict[str, str] | None:
2424
def data(self) -> Any:
2525
return None
2626

27+
@property
28+
def timeout(self) -> float:
29+
return self.secs
30+
2731
@property
2832
def tags(self) -> dict[str, str]:
2933
return {"resonate:timeout": "true"}
@@ -32,8 +36,8 @@ def options(
3236
self,
3337
id: str | None = None,
3438
idempotency_key: str | Callable[[str], str] | None = None,
35-
target: str | None = None,
3639
tags: dict[str, str] | None = None,
40+
target: str | None = None,
3741
timeout: int | None = None,
3842
version: int | None = None,
3943
) -> Sleep:

resonate/coroutine.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class LFC[T](LFX[T]):
5656
@dataclass
5757
class RFX[T]:
5858
conv: Convention
59+
opts: Options = field(default_factory=Options) # unused for the time being
5960

6061
@property
6162
def id(self) -> str:

resonate/models/clock.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
from __future__ import annotations
22

3-
from typing import Protocol
3+
from typing import Protocol, runtime_checkable
44

55

6+
@runtime_checkable
67
class Clock(Protocol):
7-
def time(self) -> int: ...
8+
def time(self) -> float: ...
9+
def strftime(self, format: str, /) -> str: ...

resonate/models/commands.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
class Invoke:
2525
id: str
2626
conv: Convention
27+
timeout: float # absolute time in seconds
2728
func: Callable[..., Any] = field(repr=False)
2829
args: tuple[Any, ...] = field(default_factory=tuple)
2930
kwargs: dict[str, Any] = field(default_factory=dict)

resonate/models/context.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22

3-
from typing import Protocol
3+
from typing import Any, Protocol
44

55

66
class Context(Protocol):
@@ -9,6 +9,8 @@ def id(self) -> str: ...
99
@property
1010
def info(self) -> Info: ...
1111

12+
def get_dependency(self, key: str, default: Any = None) -> Any: ...
13+
1214

1315
class Info(Protocol):
1416
@property
@@ -18,6 +20,6 @@ def idempotency_key(self) -> str | None: ...
1820
@property
1921
def tags(self) -> dict[str, str] | None: ...
2022
@property
21-
def timeout(self) -> int: ...
23+
def timeout(self) -> float: ...
2224
@property
2325
def version(self) -> int: ...

resonate/models/convention.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@ def headers(self) -> dict[str, str] | None: ...
1616
@property
1717
def data(self) -> Any: ...
1818
@property
19-
def timeout(self) -> int: ...
19+
def timeout(self) -> float: ... # relative time in seconds
2020
@property
2121
def tags(self) -> dict[str, str] | None: ...
2222

2323
def options(
2424
self,
2525
id: str | None = None,
2626
idempotency_key: str | Callable[[str], str] | None = None,
27-
target: str | None = None,
2827
tags: dict[str, str] | None = None,
29-
timeout: int | None = None,
28+
target: str | None = None,
29+
timeout: float | None = None,
3030
version: int | None = None,
3131
) -> Convention: ...

resonate/models/durable_promise.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,14 @@ def canceled(self) -> bool:
6161
def timedout(self) -> bool:
6262
return self.state == "REJECTED_TIMEDOUT"
6363

64+
@property
65+
def abs_timeout(self) -> float:
66+
return self.timeout / 1000
67+
68+
@property
69+
def rel_timeout(self) -> float:
70+
return (self.timeout - self.created_on) / 1000
71+
6472
def resolve(self, data: str | None, *, ikey: str | None = None, strict: bool = False, headers: dict[str, str] | None = None) -> None:
6573
promise = self.store.promises.resolve(
6674
id=self.id,

resonate/options.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from __future__ import annotations
22

3-
import sys
43
from dataclasses import dataclass, field
54
from inspect import isgeneratorfunction
65
from typing import TYPE_CHECKING
@@ -23,7 +22,7 @@ class Options:
2322
retry_policy: RetryPolicy | Callable[[Callable], RetryPolicy] = lambda f: Never() if isgeneratorfunction(f) else Exponential()
2423
target: str = "poll://default"
2524
tags: dict[str, str] = field(default_factory=dict)
26-
timeout: int = sys.maxsize
25+
timeout: float = 31536000 # relative time in seconds, default 1 year
2726
version: int = 0
2827

2928
def __post_init__(self) -> None:
@@ -44,7 +43,7 @@ def merge(
4443
retry_policy: RetryPolicy | Callable[[Callable], RetryPolicy] | None = None,
4544
target: str | None = None,
4645
tags: dict[str, str] | None = None,
47-
timeout: int | None = None,
46+
timeout: float | None = None,
4847
version: int | None = None,
4948
) -> Options:
5049
if version and not (version >= 0):

0 commit comments

Comments
 (0)