Skip to content

Commit f785506

Browse files
committed
rename instance policy Fresh → Constructed across code and docs
1 parent 8b0726d commit f785506

7 files changed

Lines changed: 101 additions & 97 deletions

File tree

rust/otap-dataflow/crates/engine/src/capability/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ pub trait ExtensionCapability: private::Sealed + 'static {
7676
/// # Per-node freshness
7777
///
7878
/// Invoked **once per node** that binds the capability via the
79-
/// fallback path, with a fresh `Box<Self::Shared>` minted by the
80-
/// factory for that node. This matches the per-caller-fresh
79+
/// fallback path, with a new `Box<Self::Shared>` minted by the
80+
/// factory for that node. This matches the per-node instance
8181
/// semantics of [`Capabilities::require_shared`] — every binding
8282
/// gets its own instance.
8383
///

rust/otap-dataflow/crates/engine/src/capability/registry/resolve.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ pub(crate) fn resolve_bindings(
110110

111111
// Resolve local entry: prefer a native local registration; else,
112112
// if the extension registered a shared entry, invoke the
113-
// capability's `SharedAsLocal` adapter to build a fresh local
113+
// capability's `SharedAsLocal` adapter to build a new local
114114
// wrapper around this node's own clone of the shared instance.
115115
let native_local = local_entry;
116116

@@ -139,7 +139,7 @@ pub(crate) fn resolve_bindings(
139139
// (single, one-shot) `require_local` call. The closure
140140
// captures a refcounted handle to the shared produce
141141
// closure plus the capability's `adapt_as_local` fn
142-
// pointer, then on invocation produces a fresh shared
142+
// pointer, then on invocation produces a new shared
143143
// instance and routes it through the adapter to yield a
144144
// type-erased `Rc<dyn Any>` wrapping the local trait
145145
// object.

rust/otap-dataflow/crates/engine/src/capability/registry/tests.rs

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ impl TestCapLocal for LocalImpl {
129129

130130
// ── Helpers ──────────────────────────────────────────────────────────
131131

132-
// Build a SharedInstanceFactory that always produces a fresh
132+
// Build a SharedInstanceFactory that always produces a new
133133
// `Box<SharedImpl>` with the given value. Mimics the builder's
134134
// clone-per-consumer output.
135135
fn shared_instance_factory(val: &'static str) -> crate::capability::SharedInstanceFactory {
@@ -517,11 +517,11 @@ fn test_consumed_tracking_persists_across_nodes_shared() {
517517

518518
/// Regression for Option C: each node resolving a `SharedAsLocal`
519519
/// binding receives its own clone of the shared extension instance.
520-
/// This preserves per-caller-fresh semantics that a shared impl may
521-
/// rely on (e.g. per-caller mutable state) — the adapter is not
520+
/// This preserves the per-node instance semantics that a shared impl
521+
/// may rely on (e.g. per-caller mutable state) — the adapter is not
522522
/// pre-built once and shared across nodes.
523523
#[test]
524-
fn test_shared_as_local_builds_fresh_adapter_per_node() {
524+
fn test_shared_as_local_builds_new_adapter_per_node() {
525525
use std::sync::Arc;
526526
use std::sync::atomic::{AtomicUsize, Ordering};
527527

@@ -848,10 +848,10 @@ fn test_end_to_end_local_only_via_bundle() {
848848
}
849849

850850
#[test]
851-
fn test_end_to_end_shared_fresh_policy_mints_independent_instances() {
852-
// With the fresh (factory) policy, the instance factory invokes the
853-
// user's closure on each produce(); each consumer should observe
854-
// its own instance. Shared+Clone consumers observe
851+
fn test_end_to_end_shared_constructed_policy_mints_independent_instances() {
852+
// With the `.constructed()` (factory) policy, the instance factory
853+
// invokes the user's closure on each produce(); each consumer
854+
// should observe its own instance. Shared+Clone consumers observe
855855
// clone-of-prototype semantics — also independent here since
856856
// SharedImpl has no interior mutability.
857857
use crate::capability::ExtensionCapabilities;
@@ -868,11 +868,11 @@ fn test_end_to_end_shared_fresh_policy_mints_independent_instances() {
868868
));
869869
let runtime_config = ExtensionConfig::new("counter");
870870

871-
// FreshImpl: each `start()` increments a shared counter so we can
872-
// confirm the factory ran per-consumer.
871+
// ConstructedImpl: each `start()` increments a shared counter so
872+
// we can confirm the factory ran per-consumer.
873873
#[derive(Clone)]
874-
struct FreshImpl(Arc<AtomicUsize>, &'static str);
875-
impl TestCapShared for FreshImpl {
874+
struct ConstructedImpl(Arc<AtomicUsize>, &'static str);
875+
impl TestCapShared for ConstructedImpl {
876876
fn value(&self) -> &str {
877877
self.1
878878
}
@@ -883,10 +883,10 @@ fn test_end_to_end_shared_fresh_policy_mints_independent_instances() {
883883

884884
let bundle = ExtensionWrapper::builder(name.clone(), user_config, &runtime_config)
885885
.passive()
886-
.fresh()
887-
.shared::<FreshImpl, _>(move || {
886+
.constructed()
887+
.shared::<ConstructedImpl, _>(move || {
888888
let _ = counter_for_closure.fetch_add(1, Ordering::SeqCst);
889-
FreshImpl(Arc::clone(&counter_for_closure), "fresh")
889+
ConstructedImpl(Arc::clone(&counter_for_closure), "constructed")
890890
})
891891
.build()
892892
.expect("bundle builds");
@@ -897,7 +897,7 @@ fn test_end_to_end_shared_fresh_policy_mints_independent_instances() {
897897
register_shared: |ext_id, factory, registry| {
898898
registry.register_shared(
899899
TypeId::of::<TestCap>(),
900-
TestCap::shared_entry::<FreshImpl>(ext_id, factory),
900+
TestCap::shared_entry::<ConstructedImpl>(ext_id, factory),
901901
)
902902
},
903903
register_local: ExtensionCapabilities::none().register_local,
@@ -925,18 +925,19 @@ fn test_end_to_end_shared_fresh_policy_mints_independent_instances() {
925925
.expect("resolve");
926926

927927
// One claim per resolved Capabilities (one-shot contract). The
928-
// `fresh` policy invokes the user's factory on each `produce()`,
929-
// so two consumers should observe two factory invocations and no
930-
// resolve-time mint (the `SharedAsLocal` fallback is deferred to
931-
// the `require_local` call, which is never made here).
928+
// `.constructed()` policy invokes the user's factory on each
929+
// `produce()`, so two consumers should observe two factory
930+
// invocations and no resolve-time mint (the `SharedAsLocal`
931+
// fallback is deferred to the `require_local` call, which is never
932+
// made here).
932933
let s1 = resolved_a.require_shared::<TestCap>().unwrap();
933934
let s2 = resolved_b.require_shared::<TestCap>().unwrap();
934-
assert_eq!(s1.value(), "fresh");
935-
assert_eq!(s2.value(), "fresh");
935+
assert_eq!(s1.value(), "constructed");
936+
assert_eq!(s2.value(), "constructed");
936937
assert_eq!(
937938
counter.load(Ordering::SeqCst),
938939
2,
939-
"fresh factory should have been invoked exactly 2x (one per require_shared consumer)"
940+
"constructed factory should have been invoked exactly 2x (one per require_shared consumer)"
940941
);
941942
}
942943

rust/otap-dataflow/crates/engine/src/extension/builder.rs

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@
2222
//! .shared(MyShared::new(cfg))
2323
//! .build()?;
2424
//!
25-
//! // Passive extension, fresh-per-consumer policy.
25+
//! // Passive extension, constructed-per-consumer policy.
2626
//! builder
2727
//! .passive()
28-
//! .fresh()
28+
//! .constructed()
2929
//! .shared(|| MyShared::new(cfg.clone()))
3030
//! .local(|| Rc::new(MyLocal::new(cfg.clone())))
3131
//! .build()?;
@@ -36,25 +36,26 @@
3636
//! | Axis | Methods |
3737
//! |-----------------|----------------------------------------------------------------------------|
3838
//! | Lifecycle | `.active()` / `.passive()` |
39-
//! | Instance policy | *(implicit Cloned for Active)* `.cloned()` / `.fresh()` *(Passive only)* |
39+
//! | Instance policy | *(implicit Cloned for Active)* `.cloned()` / `.constructed()` *(Passive only)* |
4040
//! | Execution model | `.shared(...)` / `.local(...)` *(repeatable, register once each)* |
4141
//!
4242
//! **Why the lifecycle and policy are sealed per bundle.** "This
43-
//! extension has an event loop" or "this capability hands out fresh
44-
//! instances" is a property of the extension, not of which trait-object
45-
//! shape (shared vs local) the consumer happens to request. Letting the
46-
//! two execution models diverge would mean consumers observe different
47-
//! instance-sharing semantics depending on whether they call
48-
//! `require_local` vs `require_shared` on the same extension — a
49-
//! debugging hazard with no known legitimate use case. Forcing a single
50-
//! strategy across both execution models makes the extension's behavior
51-
//! predictable and eliminates a combinatorial footgun.
43+
//! extension has an event loop" or "this capability hands out a new
44+
//! instance per consumer" is a property of the extension, not of which
45+
//! trait-object shape (shared vs local) the consumer happens to
46+
//! request. Letting the two execution models diverge would mean
47+
//! consumers observe different instance-sharing semantics depending on
48+
//! whether they call `require_local` vs `require_shared` on the same
49+
//! extension — a debugging hazard with no known legitimate use case.
50+
//! Forcing a single strategy across both execution models makes the
51+
//! extension's behavior predictable and eliminates a combinatorial
52+
//! footgun.
5253
//!
53-
//! **Active + Fresh is unrepresentable.** Active extensions have a
54-
//! single engine-driven event loop; minting fresh instances per
54+
//! **Active + Constructed is unrepresentable.** Active extensions have
55+
//! a single engine-driven event loop; constructing a new instance per
5556
//! consumer doesn't compose with that. The `.active()` stage provides
56-
//! no `.fresh()` method — the invalid combination is a compile-time
57-
//! error.
57+
//! no `.constructed()` method — the invalid combination is a
58+
//! compile-time error.
5859
5960
use super::{ExtensionBundle, ExtensionLifecycle, ExtensionWrapper};
6061
use crate::capability::factory::{LocalInstanceFactory, SharedInstanceFactory};
@@ -181,11 +182,12 @@ impl PassiveStage {
181182
}
182183
}
183184

184-
/// Select the **fresh-per-consumer** instance policy: each consumer
185-
/// receives a freshly-constructed instance from the stored closure.
185+
/// Select the **constructed-per-consumer** instance policy: each
186+
/// consumer receives a newly-constructed instance from the stored
187+
/// closure.
186188
#[must_use]
187-
pub fn fresh(self) -> PassiveFreshStage {
188-
PassiveFreshStage {
189+
pub fn constructed(self) -> PassiveConstructedStage {
190+
PassiveConstructedStage {
189191
parent: self.parent,
190192
}
191193
}
@@ -250,15 +252,15 @@ impl PassiveClonedStage {
250252
}
251253
}
252254

253-
/// Passive + Fresh (fresh-per-consumer) stage.
255+
/// Passive + Constructed (constructed-per-consumer) stage.
254256
#[doc(hidden)]
255-
pub struct PassiveFreshStage {
257+
pub struct PassiveConstructedStage {
256258
parent: ExtensionBundleBuilder,
257259
}
258260

259-
impl PassiveFreshStage {
261+
impl PassiveConstructedStage {
260262
/// Register the shared (Send) variant via a factory closure.
261-
/// Each consumer receives a freshly-constructed instance.
263+
/// Each consumer receives a newly-constructed instance.
262264
///
263265
/// `F: Clone` is required so per-node factories can duplicate the
264266
/// closure; closures capturing `Clone` configuration (e.g.
@@ -348,8 +350,8 @@ impl ExtensionBundleBuilder {
348350
/// The engine will drive an event loop for whichever sides are
349351
/// registered.
350352
///
351-
/// Instance policy is implicitly clone-per-consumer — fresh-per-
352-
/// consumer (factory) is Passive-only.
353+
/// Instance policy is implicitly clone-per-consumer —
354+
/// constructed-per-consumer (factory closure) is Passive-only.
353355
#[must_use]
354356
pub fn active(self) -> ActiveStage {
355357
ActiveStage { parent: self }
@@ -359,7 +361,7 @@ impl ExtensionBundleBuilder {
359361
/// event loop is spawned; the extension exposes capabilities only.
360362
///
361363
/// Continue with [`PassiveStage::cloned`] (clone-per-consumer)
362-
/// or [`PassiveStage::fresh`] (fresh-per-consumer).
364+
/// or [`PassiveStage::constructed`] (constructed-per-consumer).
363365
#[must_use]
364366
pub fn passive(self) -> PassiveStage {
365367
PassiveStage { parent: self }

rust/otap-dataflow/crates/engine/src/extension/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
//! | Submodule | Contents |
1010
//! |-------------|--------------------------------------------------------------------------|
1111
//! | [`wrapper`] | [`ControlChannel`], [`EffectHandler`], [`ExtensionLifecycle`], [`ExtensionWrapper`], [`ExtensionBundle`] |
12-
//! | [`builder`] | Typestate chain: [`ExtensionBundleBuilder`], [`ActiveStage`], [`PassiveStage`], [`PassiveClonedStage`], [`PassiveFreshStage`], plus [`SharedDecomposed`] / [`LocalDecomposed`] |
12+
//! | [`builder`] | Typestate chain: [`ExtensionBundleBuilder`], [`ActiveStage`], [`PassiveStage`], [`PassiveClonedStage`], [`PassiveConstructedStage`], plus [`SharedDecomposed`] / [`LocalDecomposed`] |
1313
//!
1414
//! For the local (!Send) and shared (Send) Extension traits, see
1515
//! [`local::extension`](crate::local::extension) and
@@ -22,8 +22,8 @@ pub mod wrapper;
2222
mod tests;
2323

2424
pub use builder::{
25-
ActiveStage, ExtensionBundleBuilder, LocalDecomposed, PassiveClonedStage, PassiveFreshStage,
26-
PassiveStage, SharedDecomposed,
25+
ActiveStage, ExtensionBundleBuilder, LocalDecomposed, PassiveClonedStage,
26+
PassiveConstructedStage, PassiveStage, SharedDecomposed,
2727
};
2828
pub use wrapper::{
2929
ControlChannel, ControlReceiver, EffectHandler, ExtensionBundle, ExtensionLifecycle,

rust/otap-dataflow/crates/engine/src/extension/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ fn test_dual_passive_fresh() {
521521
let (n, u, c) = ext_config("dpf");
522522
let mut set = ExtensionWrapper::builder(n, u, &c)
523523
.passive()
524-
.fresh()
524+
.constructed()
525525
.local(|| std::rc::Rc::new(42u32))
526526
.shared(|| "data".to_string())
527527
.build()

0 commit comments

Comments
 (0)