ohra 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.
- ohra/__init__.py +215 -0
- ohra/behavior.py +215 -0
- ohra/cell.py +667 -0
- ohra/cluster/__init__.py +72 -0
- ohra/cluster/core.py +1656 -0
- ohra/cluster/failure_detector.py +304 -0
- ohra/cluster/gossip.py +412 -0
- ohra/cluster/reachability.py +183 -0
- ohra/cluster/sbr.py +802 -0
- ohra/cluster/sharding/__init__.py +130 -0
- ohra/cluster/sharding/coordinator.py +817 -0
- ohra/cluster/sharding/region.py +392 -0
- ohra/cluster/sharding/shard.py +511 -0
- ohra/cluster/singleton.py +1007 -0
- ohra/cluster/vector_clock.py +46 -0
- ohra/context.py +163 -0
- ohra/coordinated_shutdown.py +224 -0
- ohra/discovery.py +59 -0
- ohra/ext/__init__.py +2 -0
- ohra/ext/fastapi.py +59 -0
- ohra/ext/postgres.py +140 -0
- ohra/ext/shm.py +80 -0
- ohra/mailbox.py +31 -0
- ohra/management/__init__.py +195 -0
- ohra/management/bootstrap.py +690 -0
- ohra/management/cluster_http.py +109 -0
- ohra/management/health.py +76 -0
- ohra/management/http.py +228 -0
- ohra/management/kubernetes.py +181 -0
- ohra/patterns.py +138 -0
- ohra/persistence.py +276 -0
- ohra/py.typed +0 -0
- ohra/receptionist.py +372 -0
- ohra/ref.py +95 -0
- ohra/remote/__init__.py +18 -0
- ohra/remote/remoting.py +442 -0
- ohra/remote/serialization.py +164 -0
- ohra/result.py +19 -0
- ohra/routers.py +73 -0
- ohra/signals.py +49 -0
- ohra/supervision.py +81 -0
- ohra/system.py +236 -0
- ohra-0.1.0.dist-info/METADATA +175 -0
- ohra-0.1.0.dist-info/RECORD +48 -0
- ohra-0.1.0.dist-info/WHEEL +5 -0
- ohra-0.1.0.dist-info/licenses/LICENSE +201 -0
- ohra-0.1.0.dist-info/licenses/NOTICE +39 -0
- ohra-0.1.0.dist-info/top_level.txt +1 -0
ohra/__init__.py
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""Apache Pekko Typed semantics on asyncio."""
|
|
2
|
+
|
|
3
|
+
from .behavior import Behavior, Behaviors, Receive, StashBuffer, StashOverflowError
|
|
4
|
+
from .cell import TimerScheduler
|
|
5
|
+
from .cluster.core import (
|
|
6
|
+
Cluster,
|
|
7
|
+
ClusterDomainEvent,
|
|
8
|
+
ClusterSettings,
|
|
9
|
+
CurrentClusterState,
|
|
10
|
+
Down,
|
|
11
|
+
GetCurrentState,
|
|
12
|
+
Join,
|
|
13
|
+
JoinSeedNodes,
|
|
14
|
+
LeaderChanged,
|
|
15
|
+
Leave,
|
|
16
|
+
MemberDowned,
|
|
17
|
+
MemberEvent,
|
|
18
|
+
MemberExited,
|
|
19
|
+
MemberJoined,
|
|
20
|
+
MemberLeft,
|
|
21
|
+
MemberRemoved,
|
|
22
|
+
MemberUp,
|
|
23
|
+
MemberWeaklyUp,
|
|
24
|
+
ReachabilityEvent,
|
|
25
|
+
ReachableMember,
|
|
26
|
+
SelfRemoved,
|
|
27
|
+
SelfUp,
|
|
28
|
+
Subscribe,
|
|
29
|
+
UnreachableMember,
|
|
30
|
+
Unsubscribe,
|
|
31
|
+
)
|
|
32
|
+
from .cluster.gossip import Member
|
|
33
|
+
from .cluster.sharding import (
|
|
34
|
+
ClusterSharding,
|
|
35
|
+
ClusterShardingSettings,
|
|
36
|
+
CurrentShardRegionState,
|
|
37
|
+
Entity,
|
|
38
|
+
EntityContext,
|
|
39
|
+
EntityRef,
|
|
40
|
+
EntityTypeKey,
|
|
41
|
+
GetShardRegionState,
|
|
42
|
+
Passivate,
|
|
43
|
+
ShardCommand,
|
|
44
|
+
Sharding,
|
|
45
|
+
ShardingEnvelope,
|
|
46
|
+
)
|
|
47
|
+
from .cluster.sbr import (
|
|
48
|
+
DownAll,
|
|
49
|
+
KeepMajority,
|
|
50
|
+
KeepOldest,
|
|
51
|
+
SplitBrainResolverSettings,
|
|
52
|
+
StaticQuorum,
|
|
53
|
+
)
|
|
54
|
+
from .cluster.singleton import ClusterSingleton, ClusterSingletonSettings, SingletonActor
|
|
55
|
+
from .context import ActorContext
|
|
56
|
+
from .discovery import ConfigServiceDiscovery, Lookup, Resolved, ResolvedTarget, ServiceDiscovery
|
|
57
|
+
from .coordinated_shutdown import (
|
|
58
|
+
ActorSystemTerminateReason,
|
|
59
|
+
ClusterDowningReason,
|
|
60
|
+
ClusterLeavingReason,
|
|
61
|
+
CoordinatedShutdown,
|
|
62
|
+
CoordinatedShutdownSettings,
|
|
63
|
+
JvmExitReason,
|
|
64
|
+
Reason,
|
|
65
|
+
)
|
|
66
|
+
from .mailbox import MailboxOverflowError, MailboxSelector
|
|
67
|
+
from .management import Management, ManagementSettings
|
|
68
|
+
from .management.bootstrap import ClusterBootstrap, ClusterBootstrapSettings
|
|
69
|
+
from .management.kubernetes import KubernetesApiServiceDiscovery, KubernetesApiSettings
|
|
70
|
+
from .management.health import HealthCheckSettings
|
|
71
|
+
from .patterns import AskTimeoutError, Replyable, SpawnProtocol, ask, batching
|
|
72
|
+
from .receptionist import (
|
|
73
|
+
Deregister,
|
|
74
|
+
Deregistered,
|
|
75
|
+
Find,
|
|
76
|
+
Listing,
|
|
77
|
+
Receptionist,
|
|
78
|
+
Register,
|
|
79
|
+
Registered,
|
|
80
|
+
ServiceKey,
|
|
81
|
+
)
|
|
82
|
+
from .persistence import (
|
|
83
|
+
InMemoryStore,
|
|
84
|
+
Persist,
|
|
85
|
+
Persisted,
|
|
86
|
+
PersistenceError,
|
|
87
|
+
RevisionConflictError,
|
|
88
|
+
SqliteStore,
|
|
89
|
+
Store,
|
|
90
|
+
)
|
|
91
|
+
from .ref import ActorRef
|
|
92
|
+
from .remote import Address, RemoteRef, UniqueAddress
|
|
93
|
+
from .remote.serialization import Serialization, SerializationError, remotable
|
|
94
|
+
from .result import Failure, Success, Try
|
|
95
|
+
from .routers import PoolRouter, Routers
|
|
96
|
+
from .signals import ChildFailed, DeathPactError, PostStop, PreRestart, Signal, Terminated
|
|
97
|
+
from .supervision import SupervisorStrategy
|
|
98
|
+
from .system import ActorSystem
|
|
99
|
+
|
|
100
|
+
__all__ = [
|
|
101
|
+
"ActorContext",
|
|
102
|
+
"ActorRef",
|
|
103
|
+
"ActorSystem",
|
|
104
|
+
"ActorSystemTerminateReason",
|
|
105
|
+
"Address",
|
|
106
|
+
"AskTimeoutError",
|
|
107
|
+
"Behavior",
|
|
108
|
+
"Behaviors",
|
|
109
|
+
"ChildFailed",
|
|
110
|
+
"Cluster",
|
|
111
|
+
"ClusterBootstrap",
|
|
112
|
+
"ClusterBootstrapSettings",
|
|
113
|
+
"ClusterDomainEvent",
|
|
114
|
+
"ClusterDowningReason",
|
|
115
|
+
"ClusterLeavingReason",
|
|
116
|
+
"ClusterSettings",
|
|
117
|
+
"ClusterSharding",
|
|
118
|
+
"ClusterShardingSettings",
|
|
119
|
+
"ClusterSingleton",
|
|
120
|
+
"ClusterSingletonSettings",
|
|
121
|
+
"ConfigServiceDiscovery",
|
|
122
|
+
"CoordinatedShutdown",
|
|
123
|
+
"CoordinatedShutdownSettings",
|
|
124
|
+
"CurrentClusterState",
|
|
125
|
+
"CurrentShardRegionState",
|
|
126
|
+
"DeathPactError",
|
|
127
|
+
"Deregister",
|
|
128
|
+
"Deregistered",
|
|
129
|
+
"Down",
|
|
130
|
+
"DownAll",
|
|
131
|
+
"Entity",
|
|
132
|
+
"EntityContext",
|
|
133
|
+
"EntityRef",
|
|
134
|
+
"EntityTypeKey",
|
|
135
|
+
"Failure",
|
|
136
|
+
"Find",
|
|
137
|
+
"GetCurrentState",
|
|
138
|
+
"GetShardRegionState",
|
|
139
|
+
"HealthCheckSettings",
|
|
140
|
+
"InMemoryStore",
|
|
141
|
+
"Join",
|
|
142
|
+
"JoinSeedNodes",
|
|
143
|
+
"JvmExitReason",
|
|
144
|
+
"KubernetesApiServiceDiscovery",
|
|
145
|
+
"KubernetesApiSettings",
|
|
146
|
+
"KeepMajority",
|
|
147
|
+
"KeepOldest",
|
|
148
|
+
"LeaderChanged",
|
|
149
|
+
"Leave",
|
|
150
|
+
"Listing",
|
|
151
|
+
"Lookup",
|
|
152
|
+
"MailboxOverflowError",
|
|
153
|
+
"MailboxSelector",
|
|
154
|
+
"Member",
|
|
155
|
+
"MemberDowned",
|
|
156
|
+
"MemberEvent",
|
|
157
|
+
"MemberExited",
|
|
158
|
+
"MemberJoined",
|
|
159
|
+
"MemberLeft",
|
|
160
|
+
"MemberRemoved",
|
|
161
|
+
"MemberUp",
|
|
162
|
+
"MemberWeaklyUp",
|
|
163
|
+
"Passivate",
|
|
164
|
+
"Management",
|
|
165
|
+
"ManagementSettings",
|
|
166
|
+
"Persist",
|
|
167
|
+
"Persisted",
|
|
168
|
+
"PersistenceError",
|
|
169
|
+
"PoolRouter",
|
|
170
|
+
"PostStop",
|
|
171
|
+
"PreRestart",
|
|
172
|
+
"ReachabilityEvent",
|
|
173
|
+
"ReachableMember",
|
|
174
|
+
"Reason",
|
|
175
|
+
"Receive",
|
|
176
|
+
"Receptionist",
|
|
177
|
+
"Register",
|
|
178
|
+
"Registered",
|
|
179
|
+
"RemoteRef",
|
|
180
|
+
"Replyable",
|
|
181
|
+
"Resolved",
|
|
182
|
+
"ResolvedTarget",
|
|
183
|
+
"RevisionConflictError",
|
|
184
|
+
"Routers",
|
|
185
|
+
"SelfRemoved",
|
|
186
|
+
"SelfUp",
|
|
187
|
+
"Serialization",
|
|
188
|
+
"SerializationError",
|
|
189
|
+
"ServiceDiscovery",
|
|
190
|
+
"ServiceKey",
|
|
191
|
+
"ShardCommand",
|
|
192
|
+
"Sharding",
|
|
193
|
+
"ShardingEnvelope",
|
|
194
|
+
"Signal",
|
|
195
|
+
"SingletonActor",
|
|
196
|
+
"SplitBrainResolverSettings",
|
|
197
|
+
"StaticQuorum",
|
|
198
|
+
"SpawnProtocol",
|
|
199
|
+
"SqliteStore",
|
|
200
|
+
"StashBuffer",
|
|
201
|
+
"StashOverflowError",
|
|
202
|
+
"Store",
|
|
203
|
+
"Subscribe",
|
|
204
|
+
"Success",
|
|
205
|
+
"SupervisorStrategy",
|
|
206
|
+
"Terminated",
|
|
207
|
+
"TimerScheduler",
|
|
208
|
+
"Try",
|
|
209
|
+
"UniqueAddress",
|
|
210
|
+
"UnreachableMember",
|
|
211
|
+
"Unsubscribe",
|
|
212
|
+
"ask",
|
|
213
|
+
"batching",
|
|
214
|
+
"remotable",
|
|
215
|
+
]
|
ohra/behavior.py
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""Behaviors: values describing how an actor reacts to the next message.
|
|
2
|
+
|
|
3
|
+
Mirrors org.apache.pekko.actor.typed.scaladsl.Behaviors. A handler returns the
|
|
4
|
+
next behavior; returning ``Behaviors.same`` keeps the current one.
|
|
5
|
+
Handlers may be plain functions or coroutines.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from collections import deque
|
|
11
|
+
from collections.abc import Awaitable, Callable
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from typing import TYPE_CHECKING, Any
|
|
14
|
+
|
|
15
|
+
from .signals import Signal
|
|
16
|
+
from .supervision import SupervisorStrategy
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from .persistence import Store, _Persister # pyright: ignore[reportPrivateUsage]
|
|
20
|
+
from .cell import TimerScheduler
|
|
21
|
+
from .context import ActorContext
|
|
22
|
+
|
|
23
|
+
type MaybeAwaitable[X] = X | Awaitable[X]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Behavior[T]:
|
|
27
|
+
"""Base of all behaviors. T is the message type the actor accepts."""
|
|
28
|
+
|
|
29
|
+
__slots__ = ()
|
|
30
|
+
|
|
31
|
+
def _accepts(self, msg: T) -> None: # pins T as contravariant, never called
|
|
32
|
+
raise NotImplementedError
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class _Same(Behavior[Any]):
|
|
36
|
+
__slots__ = ()
|
|
37
|
+
|
|
38
|
+
def __repr__(self) -> str:
|
|
39
|
+
return "Behaviors.same"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class _Unhandled(Behavior[Any]):
|
|
43
|
+
__slots__ = ()
|
|
44
|
+
|
|
45
|
+
def __repr__(self) -> str:
|
|
46
|
+
return "Behaviors.unhandled"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass(frozen=True, eq=False, slots=True)
|
|
50
|
+
class Stopped(Behavior[Any]):
|
|
51
|
+
post_stop: Callable[[], None] | None = None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass(frozen=True, eq=False, slots=True)
|
|
55
|
+
class Setup[T](Behavior[T]):
|
|
56
|
+
factory: Callable[[ActorContext[T]], MaybeAwaitable[Behavior[T]]]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass(frozen=True, eq=False, slots=True)
|
|
60
|
+
class Receive[T](Behavior[T]):
|
|
61
|
+
on_message: Callable[[ActorContext[T], T], MaybeAwaitable[Behavior[T]]]
|
|
62
|
+
on_signal: Callable[[ActorContext[T], Signal], MaybeAwaitable[Behavior[T]]] | None = None
|
|
63
|
+
|
|
64
|
+
def receive_signal(
|
|
65
|
+
self, handler: Callable[[ActorContext[T], Signal], MaybeAwaitable[Behavior[T]]]
|
|
66
|
+
) -> Receive[T]:
|
|
67
|
+
return Receive(self.on_message, handler)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@dataclass(frozen=True, eq=False, slots=True)
|
|
71
|
+
class WithTimers[T](Behavior[T]):
|
|
72
|
+
factory: Callable[[TimerScheduler[T]], MaybeAwaitable[Behavior[T]]]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@dataclass(frozen=True, eq=False, slots=True)
|
|
76
|
+
class Unstash[T](Behavior[T]):
|
|
77
|
+
"""Returned by StashBuffer.unstash_all: the runtime replays ``messages`` into ``behavior``."""
|
|
78
|
+
|
|
79
|
+
messages: deque[T]
|
|
80
|
+
behavior: Behavior[T]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class StashOverflowError(Exception):
|
|
84
|
+
"""Raised by StashBuffer.stash when the buffer is full."""
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class StashBuffer[T]:
|
|
88
|
+
"""Pekko's StashBuffer. Create one with ``Behaviors.with_stash``."""
|
|
89
|
+
|
|
90
|
+
__slots__ = ("_capacity", "_messages")
|
|
91
|
+
|
|
92
|
+
def __init__(self, capacity: int) -> None:
|
|
93
|
+
self._capacity = capacity
|
|
94
|
+
self._messages: deque[T] = deque()
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def size(self) -> int:
|
|
98
|
+
return len(self._messages)
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def is_empty(self) -> bool:
|
|
102
|
+
return not self._messages
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def is_full(self) -> bool:
|
|
106
|
+
return len(self._messages) >= self._capacity
|
|
107
|
+
|
|
108
|
+
def stash(self, msg: T) -> None:
|
|
109
|
+
if self.is_full:
|
|
110
|
+
raise StashOverflowError(f"stash buffer is full ({self._capacity})")
|
|
111
|
+
self._messages.append(msg)
|
|
112
|
+
|
|
113
|
+
def unstash_all(self, behavior: Behavior[T]) -> Behavior[T]:
|
|
114
|
+
"""Process the stashed messages, in order, with ``behavior`` before any new message.
|
|
115
|
+
|
|
116
|
+
Messages stashed during the replay stay in the buffer. Behaves like
|
|
117
|
+
``Behaviors.same`` once the buffer is empty."""
|
|
118
|
+
return Unstash(self._messages, behavior)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@dataclass(frozen=True, eq=False, slots=True)
|
|
122
|
+
class Supervised[T](Behavior[T]):
|
|
123
|
+
"""Template: the runtime wraps it in a supervisor when the actor starts."""
|
|
124
|
+
|
|
125
|
+
initial: Behavior[T]
|
|
126
|
+
strategy: SupervisorStrategy
|
|
127
|
+
exc_type: type[Exception] = Exception
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class _SuperviseBuilder[T]:
|
|
131
|
+
__slots__ = ("_behavior",)
|
|
132
|
+
|
|
133
|
+
def __init__(self, behavior: Behavior[T]) -> None:
|
|
134
|
+
self._behavior = behavior
|
|
135
|
+
|
|
136
|
+
def on_failure(
|
|
137
|
+
self, strategy: SupervisorStrategy, exc_type: type[Exception] = Exception
|
|
138
|
+
) -> Behavior[T]:
|
|
139
|
+
return Supervised(self._behavior, strategy, exc_type)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
SAME: Behavior[Any] = _Same()
|
|
143
|
+
UNHANDLED: Behavior[Any] = _Unhandled()
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _unhandled_message(_ctx: Any, _msg: Any) -> Behavior[Any]:
|
|
147
|
+
return UNHANDLED
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _ignore_message(_ctx: Any, _msg: Any) -> Behavior[Any]:
|
|
151
|
+
return SAME
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class Behaviors:
|
|
155
|
+
"""Factory namespace, named after the Scala DSL."""
|
|
156
|
+
|
|
157
|
+
same: Behavior[Any] = SAME
|
|
158
|
+
unhandled: Behavior[Any] = UNHANDLED
|
|
159
|
+
empty: Behavior[Any] = Receive(_unhandled_message)
|
|
160
|
+
ignore: Behavior[Any] = Receive(_ignore_message)
|
|
161
|
+
|
|
162
|
+
@staticmethod
|
|
163
|
+
def stopped(post_stop: Callable[[], None] | None = None) -> Behavior[Any]:
|
|
164
|
+
return Stopped(post_stop)
|
|
165
|
+
|
|
166
|
+
@staticmethod
|
|
167
|
+
def setup[T](factory: Callable[[ActorContext[T]], MaybeAwaitable[Behavior[T]]]) -> Behavior[T]:
|
|
168
|
+
return Setup(factory)
|
|
169
|
+
|
|
170
|
+
@staticmethod
|
|
171
|
+
def receive[T](
|
|
172
|
+
handler: Callable[[ActorContext[T], T], MaybeAwaitable[Behavior[T]]],
|
|
173
|
+
) -> Receive[T]:
|
|
174
|
+
return Receive(handler)
|
|
175
|
+
|
|
176
|
+
@staticmethod
|
|
177
|
+
def receive_signal[T](
|
|
178
|
+
handler: Callable[[ActorContext[T], Signal], MaybeAwaitable[Behavior[T]]],
|
|
179
|
+
) -> Receive[T]:
|
|
180
|
+
"""Handle signals only; messages are unhandled."""
|
|
181
|
+
return Receive(_unhandled_message, handler)
|
|
182
|
+
|
|
183
|
+
@staticmethod
|
|
184
|
+
def receive_message[T](handler: Callable[[T], MaybeAwaitable[Behavior[T]]]) -> Receive[T]:
|
|
185
|
+
def on_message(_ctx: ActorContext[T], msg: T) -> MaybeAwaitable[Behavior[T]]:
|
|
186
|
+
return handler(msg)
|
|
187
|
+
|
|
188
|
+
return Receive(on_message)
|
|
189
|
+
|
|
190
|
+
@staticmethod
|
|
191
|
+
def supervise[T](behavior: Behavior[T]) -> _SuperviseBuilder[T]:
|
|
192
|
+
return _SuperviseBuilder(behavior)
|
|
193
|
+
|
|
194
|
+
@staticmethod
|
|
195
|
+
def persisted[S](store: Store[S]) -> _Persister[S]:
|
|
196
|
+
"""Make a state-to-behavior factory persisted: ``@Behaviors.persisted(store)`` on ``def
|
|
197
|
+
cart(items)``. Calling the factory writes the state before becoming; ``factory.recover(id,
|
|
198
|
+
empty)`` starts it."""
|
|
199
|
+
from .persistence import persisted
|
|
200
|
+
|
|
201
|
+
return persisted(store)
|
|
202
|
+
|
|
203
|
+
@staticmethod
|
|
204
|
+
def with_timers[T](
|
|
205
|
+
factory: Callable[[TimerScheduler[T]], MaybeAwaitable[Behavior[T]]],
|
|
206
|
+
) -> Behavior[T]:
|
|
207
|
+
"""Give the behavior access to the actor's timers. All timers are cancelled on restart."""
|
|
208
|
+
return WithTimers(factory)
|
|
209
|
+
|
|
210
|
+
@staticmethod
|
|
211
|
+
def with_stash[T](
|
|
212
|
+
capacity: int, factory: Callable[[StashBuffer[T]], MaybeAwaitable[Behavior[T]]]
|
|
213
|
+
) -> Behavior[T]:
|
|
214
|
+
"""Give the behavior a fresh StashBuffer per incarnation."""
|
|
215
|
+
return Setup(lambda _ctx: factory(StashBuffer(capacity)))
|