New to Kestra?
Use blueprints to kickstart your first workflows.
Detect Oracle NOCYCLE sequences low on headroom before ORA-08004 stops inserts, raise MAXVALUE behind an approval pause, then re-read dba_sequences.
A NOCYCLE sequence approaching its MAXVALUE is a time bomb. While there is headroom
it looks perfectly healthy. The moment nextval would pass the ceiling, every INSERT
that draws a key from it fails with ORA-08004, "sequence exceeds MAXVALUE and cannot be
instantiated". The table is write-down from that instant, with no warning beforehand
and no gradual slowdown, because a sequence has no concept of being nearly full.
The trap is that the healthy state and the dead state look almost identical in the data
dictionary. dba_sequences shows a last_number climbing toward max_value run after
run, which nobody watches, right up to the call that tips over. A sequence created years
ago with a small explicit MAXVALUE reaches that point long before anyone expects a 28
digit counter to. So does one defined on a narrow NUMBER.
This blueprint reads dba_sequences, measures how many more nextval calls each
ascending NOCYCLE sequence can serve before its ceiling, raises the MAXVALUE on the ones
running short with ALTER SEQUENCE behind a dry run plus an approval pause, then reads
dba_sequences again and fails the run if a target is still short.
This blueprint was created by zkasuran.
Raising a MAXVALUE is additive and non-destructive. It gives the sequence more room and changes nothing about the values already handed out, so it is one of the safer schema changes there is. It is still a schema change, so it is gated like one.
auto_remediate is true, dry_run is false plus a human
resumes the approval_gate pause with approved set to true. Three independent
conditions, all off by default.9999999999999999999999999999, the largest value a 28
digit Oracle NUMBER holds. A sequence already at that ceiling is reported, never
raised, because there is no higher ceiling to move it to.increment_by > 0) are considered. A descending sequence
approaches its MINVALUE, which is a different check.totals_before (io.kestra.plugin.jdbc.oracle.Query, read-only) counts the sequences
in the checked schemas, split into NOCYCLE plus CYCLE, with how many of each are
already low on headroom.audit (read-only) lists the ascending NOCYCLE sequences whose remaining draw count,
measured in SQL from last_number, max_value plus increment_by, is below
min_headroom_values. For each it returns the current ceiling, the remaining count,
the computed new ceiling plus the exact ALTER SEQUENCE statement. It tags each one
RAISE or REPORT_ONLY when the ceiling is already at the NUMBER limit.excluded (read-only) lists ascending CYCLE sequences that are also low on headroom,
for reporting only.targets (io.kestra.plugin.core.output.OutputValues) collects the raise statements,
the report-only detail plus the comma-joined names of the raise targets. summary
logs every group.route (io.kestra.plugin.core.flow.If):alert posts the groups to Slack. ceiling_route explains any
report-only sequences at the NUMBER limit.remediate_gate runs only when auto_remediate is true, dry_run is false plus
there is at least one raise target.approval_gate (io.kestra.plugin.core.flow.Pause) holds the run. apply checks
the resume answer, so an unanswered pause never reads as approval.remediate (io.kestra.plugin.core.flow.Loop, concurrencyLimit: 1) runs one
ALTER SEQUENCE ... MAXVALUE per target, in turn.reverify re-reads dba_sequences for the raised sequences. totals_after
re-reads the counts. verify_result fails the run through
io.kestra.plugin.core.execution.Fail if any target is still short, otherwise
remediated logs the before plus after headroom.errors posts to Slack if the flow fails for any reason.nextval calls left, rather than a raw last_number nobody can judge.dba_sequences
shows every raised sequence back above the floor, with the counts before plus after in
the log.The usual answer is a one-off ALTER SEQUENCE typed by a DBA after the outage has already
started. Kestra turns it into a policy that runs before the outage: read dba_sequences
on a schedule, measure the real headroom, post what is short to Slack, hold the schema
change behind a human approval, then prove the raise landed by reading the dictionary
again plus failing the run if it did not. Every run keeps its logs, so you can show which
sequence was short, how many draws it had left plus what its ceiling became.
On Kestra 2.0.5 OSS against Oracle Database Free 23ai (gvenzl/oracle-free, service
FREEPDB1), with a local HTTP sink standing in for Slack. The schema APP held four
sequences. SEQ_WARM was MAXVALUE 1000 NOCYCLE advanced with nextval to 998, so it
had 2 draws left while still working. SEQ_HOT was MAXVALUE 1000 NOCYCLE advanced past
its ceiling until nextval raised ORA-08004. SEQ_CYCLE was MAXVALUE 1000 CYCLE
advanced to 998. SEQ_HEALTHY was a default sequence with the full 28 digit ceiling,
untouched.
The real bite was measured, not assumed. With SEQ_HOT at its ceiling,
SELECT app.seq_hot.nextval FROM dual raised ORA-08004: sequence SEQ_HOT.NEXTVAL exceeds MAXVALUE and cannot be instantiated. After the flow raised its ceiling, the same
statement returned 1001. Every max_value plus last_number was read back from
dba_sequences with sqlplus, not only from the Kestra log.
| Run | Setup and inputs | Result |
|---|---|---|
| 1 | only SEQ_HEALTHY, defaults |
0 findings, clean branch, SUCCESS |
| 2 | warm, hot, cycle, healthy present, defaults | 2 NOCYCLE findings (SEQ_WARM, SEQ_HOT), SEQ_CYCLE reported as excluded, SEQ_HEALTHY untouched, posted to Slack, no change, SUCCESS |
| 3 | same, auto_remediate: true, dry_run: true |
Same report, Slack said no change applied, dba_sequences unchanged. The dry run gate held |
| 4 | same, armed, pause resumed approved: false |
Declined, nothing raised, SUCCESS |
| 5 | same, armed, pause resumed approved: true |
SEQ_WARM max_value 1000 to 100000998, SEQ_HOT max_value 1000 to 100001001, reverify found 0 short, SUCCESS. SEQ_HOT.nextval then returned 1001 |
| 6 | low-privilege user without ALTER ANY SEQUENCE, armed, approved | ALTER refused with ORA-01031, reverify still found the targets short, the run FAILED plus the errors block alerted Slack |
| 7 | SEQ_AT_CEILING created at the 28 digit MAXVALUE near its top, defaults |
1 finding tagged REPORT_ONLY, 0 raise targets, reported not raised, SUCCESS |
Runs 1 and 2 are the proof the check can fail: the same query returned 0 findings with only the healthy sequence present and exactly 2 NOCYCLE findings once the short ones were added, with the CYCLE sequence held out of the targets. Run 7 proves the NUMBER ceiling is respected. Run 6 proves the re-read gate: a raise that did not land left the run FAILED rather than reporting success.
jdbc:oracle:thin:@//host:1521/service).dba_sequences (SELECT_CATALOG_ROLE or SELECT ANY DICTIONARY). Raising another schema's sequence needs ALTER ANY SEQUENCE. A user
raising only its own sequences needs no extra privilege.ORACLE_PASSWORD: the database password. It is the default for the password input.SLACK_WEBHOOK_URL: used by alert and the errors block.SECRET_.| Input | Default | Purpose |
|---|---|---|
jdbc_url |
jdbc:oracle:thin:@//localhost:1521/FREEPDB1 |
The Oracle instance to check |
username |
SYSTEM |
The database user |
password |
{{ secret('ORACLE_PASSWORD') }} |
The database password |
sequence_owners |
[APP] |
Schema owners whose sequences are checked |
min_headroom_values |
1000000 |
A NOCYCLE sequence with fewer draws left than this is a finding |
restore_headroom_values |
100000000 |
How many draws of headroom a raise restores |
auto_remediate |
false |
Allow ALTER SEQUENCE to raise a ceiling |
dry_run |
true |
Must be false for a raise to run |
ORACLE_PASSWORD and SLACK_WEBHOOK_URL secrets.jdbc_url plus username, then list the schemas in sequence_owners.min_headroom_values to the warning you want, based on how fast the sequences grow.auto_remediate: true plus dry_run: false,
then resume the pause with approved: true.daily trigger, which always runs in safe mode.increment_by.min_headroom_values from the recent growth rate of each sequence rather than
one fixed number, so a fast sequence is warned earlier than a slow one.