Skip to content

Add isReady() and document lazy model loading in the Java library (#17) - #22

Open
mpc00620 wants to merge 1 commit into
IABTechLab:mainfrom
mpc00620:fix-17-init-loads-models
Open

mpc00620 wants to merge 1 commit into
IABTechLab:mainfrom
mpc00620:fix-17-init-loads-models

Conversation

@mpc00620

@mpc00620 mpc00620 commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #17.

TaskInitializer.init() loads the model and experiment configurations and schedules the feature registry setup and the periodic model-result and bloom-filter loading. Before this change, callers had no way to tell when the model data was in place, and the README told them to Thread.sleep(10000), which does not cover the first load: that load runs after a random delay of up to one refresh period (300 s by default).

This PR keeps that first load lazy, exactly as on main, and adds a readiness signal instead.

Changes:

  • BidRequestEvaluatorFactory.isReady(). It is true once init() has completed, the extractor and transformer registries hold every ServiceLoader provider, and every model-result and bloom-filter loading task of that TaskInitializer has finished its first periodic run. The outcome of that run does not matter: a missing file, an S3 error or a loader exception still counts, so model data can never block readiness forever. Once true it stays true. The base class method throws UnsupportedOperationException, so existing subclasses keep compiling. DefaultBidRequestEvaluatorFactory overrides it.
  • PeriodicTaskWithRandomizedStart.hasFinishedFirstRun(). This is a volatile flag set in a finally block of the scheduled run, so an exception or an Error still marks the run as finished.
  • A TaskInitializer constructor that takes a callback. init() runs the callback once both stages have completed. init() now also throws when a stage ends with tasks still pending, instead of returning normally. The three-argument constructor is unchanged.
  • getTask() on InitializerTaskOnPeriodicTask and ModelResultPeriodicLoadingInitializerTask.
  • Javadoc for init(), isReady() and the loading tasks now says what actually happens. The README replaces the sleep with isReady() and says it can take up to one refresh period plus the load time.
  • The docs also say what an unloaded model returns. It returns its no-match value: 1.0 (forward) for a LowValue model and 0.0 (low value) for a HighValue model. So evaluations fail open only for LowValue models. This is unchanged from main and is only documented here.

Tests:

  • DefaultBidRequestEvaluatorFactoryReadinessTest uses a fake S3 and an executor that captures periodic tasks so each test decides when they run. It covers:
    • isReady() is false after init() and true only after both model tasks have run.
    • init() itself does not load model data.
    • A missing model file, or a loader failure, still makes isReady() true.
    • A configuration with no bloom-filter model is not blocked by the bloom-filter task.
    • isReady() stays true after a later refresh fails.
    • isReady() waits for the registry setup.
    • isReady() stays false when init() fails.
  • PeriodicTaskWithRandomizedStartTest: the flag before and after the first run, after an Exception and after an Error, and the first delay staying within one period.
  • TaskInitializerTest: the callback runs after both stages, is not run on failure, and a null callback is rejected.

LC_ALL=C.UTF-8 ./gradlew clean build passes: 556 tests, checkstyle and spotbugs green.

…BTechLab#17)

TaskInitializer.init() loads the model and experiment configurations and
schedules the feature registry setup and the periodic model-result and
bloom-filter loading. The first model load stays on the periodic schedule,
after a random delay of up to one refresh period (300 s by default), as
before: blocking init() on it would add up to a minute of model load time
to every deployment batch on large fleets, stop bidding for that long on an
all-at-once restart, and send the fleet's first S3 reads in one burst.

Callers had no way to tell when the model data was in place, and the README
suggested sleeping for 10 seconds. Add BidRequestEvaluatorFactory.isReady():

- true once init() has completed, the extractor and transformer registries
  hold every ServiceLoader provider, and every model-result and
  bloom-filter loading task of that TaskInitializer has finished its first
  periodic run, whatever the outcome (a missing file, an S3 error or a
  loader exception still counts, so model data never blocks readiness
  forever); once true it stays true
- the base class method throws UnsupportedOperationException, so existing
  subclasses keep compiling; DefaultBidRequestEvaluatorFactory overrides it

Supporting changes, all additive:

- PeriodicTaskWithRandomizedStart.hasFinishedFirstRun(), a volatile flag
  set in a finally block of the scheduled run
- a TaskInitializer constructor taking a callback that init() runs once
  both stages have completed; init() now also throws when a stage ends
  with tasks still pending, instead of returning normally
- getTask() on InitializerTaskOnPeriodicTask and
  ModelResultPeriodicLoadingInitializerTask

Fix the init() and loading task javadoc to say what happens, and replace
the README's Thread.sleep(10000) advice with isReady(), stating that this
can take up to one refresh period plus the load time and that until a
model loads it returns its no-match value: LowValue models forward (fail
open), HighValue models score the request low value.

Fixes IABTechLab#17
@mpc00620 mpc00620 changed the title Load first model results in TaskInitializer.init() and add isReady() Add isReady() and document lazy model loading in the Java library (#17) Oct 20, 2026
@mpc00620
mpc00620 force-pushed the fix-17-init-loads-models branch from 2bbe4e3 to 562f751 Compare October 20, 2026 22:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Java] TaskInitializer.init() returns before model results are loaded; no readiness signal

1 participant