Class OptionChecks
All of them are about a Duration a user hands a builder, and all of them exist so the
failure lands on the client rather than on a TaskManager, where a job has already started. Each
clears the base module's multiple-consumer bar on its own; ADR-0068 carries the dated call-site
survey behind that claim.
What they replace is not merely repetition. The ceiling constant stood in six files with its
message written out in eight, so a new budget knob was correct only if its author remembered to
copy four lines and word them the same way. Positivity had **three** message shapes for one check
— "x must be positive", "x must be positive: <value>" and "x must be
positive, but was <value>" — which is what a rule with no single implementation decays into. The
value-carrying form won: a builder chain setting several durations otherwise leaves the user
guessing which one the rejection meant. The millisecond floor was five private copies in two
message shapes and two mechanics, and what a copy of it silently dropped was not the wording but
the coverage: eleven setters converting a user value with toMillis() never had one.
Deliberately not a general-purpose precondition library: the next check needs an argument of its own, and "it is a precondition too" is not one.
-
Method Summary
Modifier and TypeMethodDescriptionstatic DurationcheckAtLeastOneMilli(Duration duration, String name) Returns the duration, having checked it is present and at least one millisecond.static DurationcheckAtLeastOneMilliOrZero(Duration duration, String name) Returns the duration, having checked it is present and either zero or at least one millisecond.static DurationcheckExpressibleInNanos(Duration duration, String name) Returns the budget, having checked it can be converted to nanoseconds.static DurationcheckPositive(Duration duration, String name) Returns the duration, having checked it is present and positive.
-
Method Details
-
checkPositive
Returns the duration, having checked it is present and positive.- Parameters:
duration- the duration to checkname- the option name, for the failure message- Returns:
- the duration
-
checkAtLeastOneMilli
Returns the duration, having checked it is present and at least one millisecond.Subsumes
checkPositive(java.time.Duration, java.lang.String): zero and negative durations fail this check too, with this message rather than that one. Deliberately — a knob whose floor is a millisecond rejectsDuration.ZEROfor the same reason it rejects 500 µs, and a rejection saying only "must be positive" costs the user a second round trip when their next attempt is 500 µs.Which knobs need it is a property of what consumes the value, never of the knob's name: it belongs wherever a user's
Durationis converted withDuration.toMillis()and a zero would be harmful — aRetrySchedule, a processing-time timer, a boundedawait, a millisecond field of a Google API request, or gax's retry algorithm, which truncates its own delays the same way. Two knobs of one name, on a sink and on a source, can therefore have different floors (ADR-0068).A duration longer than about 292 million years throws
ArithmeticExceptionout ofDuration.toMillis()instead of being rejected here. It still lands at the setter, which is where a failure belongs, so the conversion is left as it is and a test pins it.- Parameters:
duration- the duration to checkname- the option name, for the failure message- Returns:
- the duration
-
checkAtLeastOneMilliOrZero
Returns the duration, having checked it is present and either zero or at least one millisecond.For a knob this project **forwards to a vendor SDK that gives zero a meaning of its own** — gax reads a zero
totalTimeoutas "use the attempt count instead" and a zero RPC timeout as "let the call run indefinitely", and the BigQuery Storage writer reads a zeromaxRetryDurationas "retry without a time limit". A setting this project merely passes through stays settable as the SDK defines it, so zero is forwarded rather than refused.The floor and the exemption are the same argument, not a compromise between two: the vendor reads these values with
toMillis(), so a positive sub-millisecond value would arrive as zero and silently become the sentinel — which is how a retry ceiling set to give up almost at once turns into unlimited retry. Refusing it is what keeps zero meaning only what the user typed.Whether zero is legal is therefore a property of the SDK on the other side, never a loosening applied for convenience: a knob this project spends itself takes
checkAtLeastOneMilli(java.time.Duration, java.lang.String), and a forwarded knob the vendor does not truncate takes neither — a floor promising millisecond granularity where none applies would be promising something untrue (the Pub/Sub subscriber'smaxAckExtensionPeriodis that case).- Parameters:
duration- the duration to checkname- the option name, for the failure message- Returns:
- the duration
-
checkExpressibleInNanos
Returns the budget, having checked it can be converted to nanoseconds.A longer one throws
ArithmeticExceptionfromDuration.toNanos()instead — on a TaskManager, out of a teardown or a constructor, rather than here on the client, which is the whole point of the check (ADR-0068). Whether a given knob needs it is a property of what spends the budget, so the reason belongs at the call site.The message names the year count as well as the value, and that is load-bearing rather than decoration:
Duration.toString()renders the ceiling asPT2562047H47M16.854775807S, which was measured to be exactly what a SQL user is shown, and no reader turns an hour count with a fractional second on it into "292 years". Tests pin the year count, so removing it fails rather than quietly making every rejection unreadable.- Parameters:
duration- the budget to checkname- the option name, for the failure message- Returns:
- the budget
-