Class FileStore
- All Implemented Interfaces:
Store
Store rooted at $JENKINS_HOME/batch-control/.
- Entities with state (requests, grants, incidents) are XStream XML files, rewritten
atomically (temp file, then
ATOMIC_MOVE). - Append-only records (runs, changes) are monthly JSONL files, written with append + flush; Instants are stored as epoch milliseconds.
- Writes are serialized per target file (a fixed set of lock stripes): an append to this month's bucket never waits for anything but another single write that happens to share its stripe, and no operation holds a lock across more than one file write or deletion. In particular retention, which deletes old months and closed entities file by file, cannot stall the queue gate or build completion.
- Page loads read JSONL buckets newest first and stop at a record cap; the month summary counters and the entity index are derived in-memory caches, never files.
- File names derived from identifiers are validated through
PathCodec; month and id names use ASCII digits whatever the default locale.
-
Field Summary
Fields inherited from interface io.jenkins.plugins.batchcontrol.store.Store
MAX_SCANNED_RECORDS -
Method Summary
Modifier and TypeMethodDescriptionvoidappendChangeRecord(ChangeRecord record) Appends one change record to the monthly JSONL bucket derived from its timestamp.voidappendRunRecord(RunRecord record) Appends one run record to the monthly JSONL bucket derived from its start time.voidcreateIncident(Incident incident) Persists a freshly opened incident: writesincidents/<id>.xmland appends the monthly index line (incidents/index/YYYY-MM.jsonl, bucket derived from the incident's creation time).booleandeleteActivationState(String jobFullName) Removes the activation state of a job;trueif one existed.deleteClosedEntitiesBefore(Instant cutoff) Retention of the XML entities: deletes closed run requests (not PENDING or APPROVED) and closed grant requests (not PENDING) whose last activity lies beforecutoff, and grants that stopped conferring anything (expiry or revocation) before it.voiddeleteConfigSnapshot(String jobFullName) Removes the config snapshot of a job (after deletion, rename or move).booleandeleteMonth(YearMonth month) Deletes one month bucket file by file: every incident XML, patch and bucket file is deleted under its own lock stripe, and the lock is released before the next one, so no writer waits behind more than a single deletion.voidDeletes the typed values of requestid(<id>.values.xml), when the approved run starts or the request ends (D-72b (5), D-74); nothing happens when there are none.static FileStoreget()The file store instance.grantRevokeRecordsSince(Instant since, Set<String> grantIds, Predicate<? super ChangeRecord> boundary) S-39-03: everyGRANT_REVOKEchange record of a grant ingrantIdswith a time at or aftersince, newest first, for re-ending windows at startup (D-74).booleanhasActivationRequestBy(String userId) Whether at least one stored activation or hold request, in any status, was filed byuserId(D-38b).booleanhasConfigSnapshot(String jobFullName) Whether anything is stored where the config snapshot of a job belongs: a snapshot, or something else in its place (whichStore.loadConfigSnapshot(java.lang.String)refuses).booleanhasRunRequestBy(String userId) Whether at least one stored run request, in any status, was filed byuserId(D-38b).booleanWhether the one-time upgrade seeding markeractivations/.schemaexists.Loads every stored activation request, sorted by id (creation order).Every stored activation state (unordered).listChangeRecords(YearMonth month) Reads every change record of the given month (empty list if the month file is absent).Loads every stored grant request, sorted by id (creation order).Loads every stored grant, sorted by id (creation order).listIncidents(YearMonth month) Loads every incident created in the given month via the monthly index, in index (creation) order.listOpenActivationRequests(Consumer<String> unreadable) AsStore.listOpenActivationRequests(), and hands the id of every open request left out because its file cannot be read tounreadable.Loads the PENDING grant requests only, sorted by id, via the entity index.listOpenRunRequests(Consumer<String> unreadable) AsStore.listOpenRunRequests(), and hands the id of every open request left out because its file cannot be read tounreadable, for a caller that must act on it once it can be read (an invalidation it would otherwise miss).listRunRecords(YearMonth month) Reads every run record of the given month (empty list if the month file is absent).Loads every stored run request, sorted by creation time.The listing fields of every stored run request, sorted by id, from memory.Every month for which any record bucket exists (runs, changes or incident index), sorted ascending.listStoredMonths(Period period) #33: the stored month buckets (asStore.listStoredMonths()) that may hold a record whose timestamp lies inperiod, whatever time zone the plugin clock had when the record was written: a bucket is named by the month of the record in the zone of its writer (SPEC 4), so after the controller's zone changed, a record near a month boundary sits in the neighbouring month's bucket.Loads an activation request by id, or returnsnullif it does not exist.loadActivationState(String jobFullName) The stored activation state of a job, ornullif none exists.loadConfigSnapshot(String jobFullName) The stored config snapshot text of a job, ornullif none exists.Loads a grant by id, or returnsnullif it does not exist.Loads a grant request by id, or returnsnullif it does not exist.loadIncident(String id) Loads an incident by id, or returnsnullif it does not exist.loadRunRequest(String id) Loads a request by id from<id>.xml, or returnsnullif it does not exist.The typed values of requestidfrom<id>.values.xml(D-74), ornullwhen there is no values file (the request has no parameters, or its values were deleted when its run started or it ended).voidWrites the upgrade seeding markeractivations/.schema(atomically).pageChangeRecords(Collection<YearMonth> months, Period period, Predicate<? super ChangeRecord> filter, int offset, int limit, int maxScanned) pageChangeRecords(Collection<YearMonth> months, Predicate<? super ChangeRecord> filter, int offset, int limit, int maxScanned) AsStore.pageRunRecords(java.util.Collection<java.time.YearMonth>, io.jenkins.plugins.batchcontrol.store.Period, java.util.function.Predicate<? super io.jenkins.plugins.batchcontrol.model.RunRecord>, int, int, int)for change records (time, then id, newest first), over all ofmonths.pageIncidents(Collection<YearMonth> months, Period period, Predicate<? super IncidentSummary> indexFilter, Predicate<? super Incident> filter, int offset, int limit, int maxScanned) AsStore.pageRunRecords(java.util.Collection<java.time.YearMonth>, io.jenkins.plugins.batchcontrol.store.Period, java.util.function.Predicate<? super io.jenkins.plugins.batchcontrol.model.RunRecord>, int, int, int)for incidents, walking the monthly index newest first (creation time, then id); every index line read counts againstmaxScanned.pageRunRecords(Collection<YearMonth> months, Period period, Predicate<? super RunRecord> filter, int offset, int limit, int maxScanned) One page of the run records ofmonthsthat matchfilter, newest first (start time, then run id).runMonthStats(YearMonth month) Run counters of one month, maintained incrementally from the bucket.voidsaveActivationRequest(ActivationRequest request) Writes (or rewrites, on a status transition) the activation request XML atomically.voidWrites the activation state of a job atomically toactivations/<PathCodec-encoded full name>.xml; the file name comes fromActivationState.getJobFullName().voidsaveConfigSnapshot(String jobFullName, String configXml) Stores the latest config.xml snapshot of a job (diff baseline; only the latest version is kept, ARCHITECTURE section 5).voidWrites (or rewrites, on revocation) the grant XML atomically.voidsaveGrantRequest(GrantRequest request) Writes (or rewrites, on a status transition) the grant request XML atomically.voidsaveIncident(Incident incident) Rewrites an existing incident's XML atomically (handling transitions, links).voidsaveNewRunRequest(RunRequest request, List<ParameterValue> values) Stores a new request (D-72, D-74):<id>.xml, then its typed values in<id>.values.xmlwhen there are any (a request without parameters has no values file).voidsaveRunRequest(RunRequest request) Writes (or rewrites, on a status transition) the request XML<id>.xmlatomically.voidwarmUp()Builds the entity index now (startup), so no later save pays for it.Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface io.jenkins.plugins.batchcontrol.store.Store
listOpenActivationRequests, listOpenRunRequests
-
Method Details
-
get
The file store instance. Callers go throughStore.get()(D-44); only that method and the tests of this class nameFileStoredirectly. -
saveRunRequest
Description copied from interface:StoreWrites (or rewrites, on a status transition) the request XML<id>.xmlatomically. Never touches the request's typed values (D-74).- Specified by:
saveRunRequestin interfaceStore
-
saveNewRunRequest
Stores a new request (D-72, D-74):<id>.xml, then its typed values in<id>.values.xmlwhen there are any (a request without parameters has no values file). Each file is written atomically; when either cannot be written, neither is left behind.The request file is written first: should the process stop between the two writes, what remains is a request whose values are missing, which cannot be approved (fail closed), and never a values file without its request.
- Specified by:
saveNewRunRequestin interfaceStore
-
loadRunRequest
Description copied from interface:StoreLoads a request by id from<id>.xml, or returnsnullif it does not exist. Its typed values are never read here (D-74): this is the read for screens, badges, listings, listeners and periodic work.- Specified by:
loadRunRequestin interfaceStore
-
loadRunRequestValues
Description copied from interface:StoreThe typed values of requestidfrom<id>.values.xml(D-74), ornullwhen there is no values file (the request has no parameters, or its values were deleted when its run started or it ended). Only for approving, submitting, recovering and disposing of a request. An element that could not be loaded may benull.- Specified by:
loadRunRequestValuesin interfaceStore
-
deleteRunRequestValues
Description copied from interface:StoreDeletes the typed values of requestid(<id>.values.xml), when the approved run starts or the request ends (D-72b (5), D-74); nothing happens when there are none.- Specified by:
deleteRunRequestValuesin interfaceStore
-
listRunRequests
Description copied from interface:StoreLoads every stored run request, sorted by creation time. A file that cannot be read (corrupt, or not readable at all) is left out with a warning, so one such file never breaks a listing.- Specified by:
listRunRequestsin interfaceStore
-
listOpenRunRequests
Description copied from interface:StoreAsStore.listOpenRunRequests(), and hands the id of every open request left out because its file cannot be read tounreadable, for a caller that must act on it once it can be read (an invalidation it would otherwise miss).- Specified by:
listOpenRunRequestsin interfaceStore
-
hasRunRequestBy
Description copied from interface:StoreWhether at least one stored run request, in any status, was filed byuserId(D-38b). Answered from the in-memory entity index; no request file and no item is read.- Specified by:
hasRunRequestByin interfaceStore
-
listRunRequestSummaries
Description copied from interface:StoreThe listing fields of every stored run request, sorted by id, from memory. A listing filters and pages on these and loads only the XML of the rows it renders.- Specified by:
listRunRequestSummariesin interfaceStore
-
saveGrantRequest
Description copied from interface:StoreWrites (or rewrites, on a status transition) the grant request XML atomically.- Specified by:
saveGrantRequestin interfaceStore
-
loadGrantRequest
Description copied from interface:StoreLoads a grant request by id, or returnsnullif it does not exist.- Specified by:
loadGrantRequestin interfaceStore
-
listGrantRequests
Description copied from interface:StoreLoads every stored grant request, sorted by id (creation order).- Specified by:
listGrantRequestsin interfaceStore
-
listOpenGrantRequests
Description copied from interface:StoreLoads the PENDING grant requests only, sorted by id, via the entity index. An open request whose file cannot be read is left out with a warning and its index entry kept, as forStore.listOpenRunRequests().- Specified by:
listOpenGrantRequestsin interfaceStore
-
saveGrant
Description copied from interface:StoreWrites (or rewrites, on revocation) the grant XML atomically. -
loadGrant
Description copied from interface:StoreLoads a grant by id, or returnsnullif it does not exist. -
listGrants
Description copied from interface:StoreLoads every stored grant, sorted by id (creation order). A file that cannot be read (corrupt, or not readable at all) is left out with a warning: that grant confers nothing, and the permission checks of everyone else keep working.- Specified by:
listGrantsin interfaceStore
-
saveActivationRequest
Description copied from interface:StoreWrites (or rewrites, on a status transition) the activation request XML atomically.- Specified by:
saveActivationRequestin interfaceStore
-
loadActivationRequest
Description copied from interface:StoreLoads an activation request by id, or returnsnullif it does not exist.- Specified by:
loadActivationRequestin interfaceStore
-
listActivationRequests
Description copied from interface:StoreLoads every stored activation request, sorted by id (creation order).- Specified by:
listActivationRequestsin interfaceStore
-
hasActivationRequestBy
Description copied from interface:StoreWhether at least one stored activation or hold request, in any status, was filed byuserId(D-38b). Answered from the in-memory entity index.- Specified by:
hasActivationRequestByin interfaceStore
-
listOpenActivationRequests
Description copied from interface:StoreAsStore.listOpenActivationRequests(), and hands the id of every open request left out because its file cannot be read tounreadable.- Specified by:
listOpenActivationRequestsin interfaceStore
-
saveActivationState
Description copied from interface:StoreWrites the activation state of a job atomically toactivations/<PathCodec-encoded full name>.xml; the file name comes fromActivationState.getJobFullName().- Specified by:
saveActivationStatein interfaceStore
-
loadActivationState
Description copied from interface:StoreThe stored activation state of a job, ornullif none exists.- Specified by:
loadActivationStatein interfaceStore
-
listActivationStates
Description copied from interface:StoreEvery stored activation state (unordered).- Specified by:
listActivationStatesin interfaceStore
-
deleteActivationState
Description copied from interface:StoreRemoves the activation state of a job;trueif one existed.- Specified by:
deleteActivationStatein interfaceStore
-
isActivationSchemaMarked
public boolean isActivationSchemaMarked()Description copied from interface:StoreWhether the one-time upgrade seeding markeractivations/.schemaexists.- Specified by:
isActivationSchemaMarkedin interfaceStore
-
markActivationSchema
public void markActivationSchema()Description copied from interface:StoreWrites the upgrade seeding markeractivations/.schema(atomically).- Specified by:
markActivationSchemain interfaceStore
-
saveConfigSnapshot
Description copied from interface:StoreStores the latest config.xml snapshot of a job (diff baseline; only the latest version is kept, ARCHITECTURE section 5).- Specified by:
saveConfigSnapshotin interfaceStore
-
loadConfigSnapshot
Description copied from interface:StoreThe stored config snapshot text of a job, ornullif none exists.- Specified by:
loadConfigSnapshotin interfaceStore
-
hasConfigSnapshot
Description copied from interface:StoreWhether anything is stored where the config snapshot of a job belongs: a snapshot, or something else in its place (whichStore.loadConfigSnapshot(java.lang.String)refuses). Nothing is read, so seeding the missing snapshots of every item costs one file system lookup per item.- Specified by:
hasConfigSnapshotin interfaceStore
-
deleteConfigSnapshot
Description copied from interface:StoreRemoves the config snapshot of a job (after deletion, rename or move).- Specified by:
deleteConfigSnapshotin interfaceStore
-
appendRunRecord
Description copied from interface:StoreAppends one run record to the monthly JSONL bucket derived from its start time. The line carriesappendedAt(D-81): the record's ownRunRecord.getAppendedAt()when the caller set it, else the plugin clock's instant of this append.- Specified by:
appendRunRecordin interfaceStore
-
listRunRecords
Description copied from interface:StoreReads every run record of the given month (empty list if the month file is absent).- Specified by:
listRunRecordsin interfaceStore
-
pageRunRecords
public RecordPage<RunRecord> pageRunRecords(Collection<YearMonth> months, Period period, Predicate<? super RunRecord> filter, int offset, int limit, int maxScanned) Description copied from interface:StoreOne page of the run records ofmonthsthat matchfilter, newest first (start time, then run id). Reads newest first and stops aftermaxScannedrecords, so the cost of a page load is bounded whatever a month holds; only records whose start time lies insideperiodcount towardmaxScanned(security-10 S-03).- Specified by:
pageRunRecordsin interfaceStore- Parameters:
offset- matches to skip (page index times page size)limit- rows to returnmaxScanned- records to read at most; seeStore.MAX_SCANNED_RECORDS
-
runMonthStats
Description copied from interface:StoreRun counters of one month, maintained incrementally from the bucket.- Specified by:
runMonthStatsin interfaceStore
-
appendChangeRecord
Description copied from interface:StoreAppends one change record to the monthly JSONL bucket derived from its timestamp.- Specified by:
appendChangeRecordin interfaceStore
-
listChangeRecords
Description copied from interface:StoreReads every change record of the given month (empty list if the month file is absent).- Specified by:
listChangeRecordsin interfaceStore
-
pageChangeRecords
public RecordPage<ChangeRecord> pageChangeRecords(Collection<YearMonth> months, Predicate<? super ChangeRecord> filter, int offset, int limit, int maxScanned) Description copied from interface:StoreAsStore.pageRunRecords(java.util.Collection<java.time.YearMonth>, io.jenkins.plugins.batchcontrol.store.Period, java.util.function.Predicate<? super io.jenkins.plugins.batchcontrol.model.RunRecord>, int, int, int)for change records (time, then id, newest first), over all ofmonths. Diff patches are read for the returned rows only.- Specified by:
pageChangeRecordsin interfaceStore
-
pageChangeRecords
public RecordPage<ChangeRecord> pageChangeRecords(Collection<YearMonth> months, Period period, Predicate<? super ChangeRecord> filter, int offset, int limit, int maxScanned) Description copied from interface:StoreAsStore.pageRunRecords(java.util.Collection<java.time.YearMonth>, io.jenkins.plugins.batchcontrol.store.Period, java.util.function.Predicate<? super io.jenkins.plugins.batchcontrol.model.RunRecord>, int, int, int)for change records, withinperiod.- Specified by:
pageChangeRecordsin interfaceStore
-
grantRevokeRecordsSince
public RecordPage<ChangeRecord> grantRevokeRecordsSince(Instant since, Set<String> grantIds, Predicate<? super ChangeRecord> boundary) S-39-03: everyGRANT_REVOKEchange record of a grant ingrantIdswith a time at or aftersince, newest first, for re-ending windows at startup (D-74). Bounded by time, not by a record count: the change log is read back only to the first record appended beforesince, and every record after it is read, however many there are. The page is truncated when that could not be done completely.Reading also stops at the first record before
sincethatboundaryaccepts (T-SEC-109): the caller's statement that no line appended before that record can be the end of any window ingrantIds, so the append-order slack is not read past it.The bounded page query with no record cap and no page limit: only the period bounds it, so it reads every line since
since(and the lines inside the append-order slack before it), keeping only the matching records, which are few. It is truncated only when the bytes of skipped out-of-period lines exceed the page query's byte budget. No diff is attached.D-75 (2): it is truncated too when a line it read could not be parsed (a torn or damaged line) or was too long to read: such a line may be a
GRANT_REVOKErecord of one of the windows, so their ends cannot be ruled out, and the caller ends them (fail-closed). Its position tells that it was appended within the part of the log read back. Logged once per read. A complete line of another record type, also one this version does not know, is not such a record and does not count.T-SEC-109: reading also stops at the first record before
sincethatboundaryaccepts, inside the append-order slack too, so damage behind such a record is not read again.- Specified by:
grantRevokeRecordsSincein interfaceStore
-
createIncident
Description copied from interface:StorePersists a freshly opened incident: writesincidents/<id>.xmland appends the monthly index line (incidents/index/YYYY-MM.jsonl, bucket derived from the incident's creation time).- Specified by:
createIncidentin interfaceStore
-
saveIncident
Description copied from interface:StoreRewrites an existing incident's XML atomically (handling transitions, links).- Specified by:
saveIncidentin interfaceStore
-
loadIncident
Description copied from interface:StoreLoads an incident by id, or returnsnullif it does not exist.- Specified by:
loadIncidentin interfaceStore
-
listIncidents
Description copied from interface:StoreLoads every incident created in the given month via the monthly index, in index (creation) order. Index lines whose XML has been deleted are skipped.- Specified by:
listIncidentsin interfaceStore
-
pageIncidents
public RecordPage<Incident> pageIncidents(Collection<YearMonth> months, Period period, Predicate<? super IncidentSummary> indexFilter, Predicate<? super Incident> filter, int offset, int limit, int maxScanned) Description copied from interface:StoreAsStore.pageRunRecords(java.util.Collection<java.time.YearMonth>, io.jenkins.plugins.batchcontrol.store.Period, java.util.function.Predicate<? super io.jenkins.plugins.batchcontrol.model.RunRecord>, int, int, int)for incidents, walking the monthly index newest first (creation time, then id); every index line read counts againstmaxScanned.indexFilteris tested on the index line and an incident's XML is loaded only when it passes (security-10 S-06);filterthen sees the loaded incident.- Specified by:
pageIncidentsin interfaceStore
-
listStoredMonths
Description copied from interface:StoreEvery month for which any record bucket exists (runs, changes or incident index), sorted ascending. Used by retention cleanup.- Specified by:
listStoredMonthsin interfaceStore
-
listStoredMonths
Description copied from interface:Store#33: the stored month buckets (asStore.listStoredMonths()) that may hold a record whose timestamp lies inperiod, whatever time zone the plugin clock had when the record was written: a bucket is named by the month of the record in the zone of its writer (SPEC 4), so after the controller's zone changed, a record near a month boundary sits in the neighbouring month's bucket. A date-range query reads these buckets and keeps the records insideperiod; existing files need no migration. A bucket outside the period's own months is included only when the period comes within the largest zone offset (18 hours) of it.- Specified by:
listStoredMonthsin interfaceStore
-
deleteMonth
Deletes one month bucket file by file: every incident XML, patch and bucket file is deleted under its own lock stripe, and the lock is released before the next one, so no writer waits behind more than a single deletion. Incident XMLs and patches go first and the JSONL files last, so an interrupted pass leaves the index in place and the next pass finishes it.- Specified by:
deleteMonthin interfaceStore- Returns:
trueif anything was deleted
-
deleteClosedEntitiesBefore
Description copied from interface:StoreRetention of the XML entities: deletes closed run requests (not PENDING or APPROVED) and closed grant requests (not PENDING) whose last activity lies beforecutoff, and grants that stopped conferring anything (expiry or revocation) before it. A grant request is kept while a grant issued from it is kept. Each file is deleted under its own lock, so no store lock is held for longer than one deletion.- Specified by:
deleteClosedEntitiesBeforein interfaceStore
-
warmUp
public void warmUp()Builds the entity index now (startup), so no later save pays for it.
-