EvaluationExposureDeduper

open class EvaluationExposureDeduper

Decides whether a hook should be told about an evaluation, so that repeated evaluations resolving to the same result do not invoke the hook again within a time window.

This class is not stable, and not subject to any backwards compatibility guarantees or semantic versioning. It is experimental. Subclassing it to change which evaluations are deduplicated is supported, but the shape it is subclassed through, and the components of EvaluationExposureKey a subclass reasons about, may change.

Deduplication is opt-in per hook: a hook is told about every evaluation until you wrap it in a DedupingHook, which is what consults a deduper.

config.hooks = [
    MetricsHook(),                                     // told about every evaluation
    DedupingHook(ObservabilityHook()),                  // default window
    DedupingHook(TelemetryHook(), window: 30),
    DedupingHook(ExperimentHook(), deduper: sharedDeduper)
]

This class is the SDK’s implementation: it remembers the result each flag last reported, and tells the hook about the flag again as soon as that result changes, or once the window elapses while it stays the same. Tracking one result per flag rather than every result seen keeps a flag that flips back and forth from hiding the flips, and holds one record per flag the application evaluates, so the window is the only thing there is to configure.

A deduper is consulted once per evaluation, before the series opens, so a suppressed evaluation invokes neither beforeEvaluation nor afterEvaluation. Implementations must be thread-safe, because evaluations may be made from any thread. Give each hook its own instance unless you intend hooks to share a window: the first hook to be told about an exposure starts the window that suppresses the rest.

  • The dedupe window used by a deduper built without a window of its own. (10 minutes)

    Declaration

    Swift

    public static let defaultWindow: TimeInterval
  • Reads the clock a window is measured against, in seconds. This is what shouldRecord(key:now:) reads when it is not given a time.

    CLOCK_MONOTONIC counts from an arbitrary point rather than from the epoch, so that correcting the device clock cannot stretch a window: were this Date(), a correction that moved the clock backwards would leave every recorded time in the future and suppress those flags until real time caught up. It is POSIX rather than one of Darwin’s own clocks, so the same reading is available on every platform Swift builds for.

    On Apple platforms it keeps advancing while the device sleeps, unlike mach_absolute_time and everything built on it, such as DispatchTime.now() and ProcessInfo.systemUptime, so a window is an interval of real time rather than of awake time. Where a platform’s monotonic clock instead stops while the host is suspended, a window outlasts the suspension, which holds a repeat back for longer rather than reporting one too often.

    Declaration

    Swift

    public static func monotonicNow() -> TimeInterval
  • Declaration

    Swift

    public init(window: TimeInterval = EvaluationExposureDeduper.defaultWindow)

    Parameters

    window

    The dedupe window, in seconds. Defaults to defaultWindow. A value of zero or less disables deduplication, so every evaluation reaches the hook.

  • Returns whether the hook should be told about the evaluation identified by the given key, and if so starts a new dedupe window for the flag.

    DedupingHook calls this once per evaluation. This implementation answers true when the flag is reporting a different result than it last did, and when the window has elapsed on the result it is repeating. See EvaluationExposureKey for what makes two evaluations the same result.

    The check and the update are performed together so that concurrent evaluations of the same flag cannot both be told to record.

    Declaration

    Swift

    open func shouldRecord(key: EvaluationExposureKey, now: TimeInterval = EvaluationExposureDeduper.monotonicNow()) -> Bool

    Parameters

    key

    The key identifying the evaluation result.

    now

    A reading of a clock that counts from an arbitrary point, in seconds. Defaults to monotonicNow(), which is not a time of day; see it for why a window is not measured against Date().

  • Clears all recorded exposures, so the next evaluation of each is reported again. DedupingHook calls this when the evaluation context changes.

    Declaration

    Swift

    open func reset()