dandi_compute_code.queue#
- dandi_compute_code.queue.aggregate_queue_statistics(
- *,
- queue_directory,
- dandiset_directory,
- output_file_name='queue_stats.json',
Write aggregate queue statistics JSON and return the written payload.
- dandi_compute_code.queue.clean_unsubmitted_capsules(*, dandiset_directory, queue_directory)[source]#
Remove all queued (unsubmitted) capsule directories from the dandiset tree.
A capsule is considered queued (prepared but not yet submitted) when its attempt directory has a
code/subdirectory but neither a non-emptylogs/subdirectory nor aderivatives/subdirectory, and the attempt directory does not contain a submitted-marker file (code/submittedorcode/submitted_date-*).The function reads the queue state, then deletes each matching attempt directory tree from the DANDI archive (via
dandi delete) and from the local filesystem. This expects the local Dandiset copy to be up-to-date.- Parameters:
- Returns:
List of attempt directory paths that were deleted.
- Return type:
- Raises:
NotADirectoryError – If queue_directory does not exist or is not a directory.
RuntimeError – If the
DANDI_API_KEYenvironment variable is not set or is blank.
- dandi_compute_code.queue.dump_issues(*, dandiset_directory, queue_directory, output_file_name='issues_dump.json')[source]#
Scan nextflow/slurm logs and write per-capsule error lines under queue_directory.
- dandi_compute_code.queue.has_pending_jobs()[source]#
Report whether any queued jobs are awaiting submission.
This is a lightweight check intended to gate queue dispatch. It inspects the DANDI assets metadata for attempt directories that contain a
code/submit.shasset without an adjacent submitted marker. It does not submit anything and does not require SLURM access.- Returns:
Truewhen at least one job is awaiting submission,Falseotherwise.- Return type:
- class dandi_compute_code.queue.JobEntry(
- job,
- content_id,
- asset_size_bytes,
- has_code=False,
- has_been_submitted=False,
- has_output=False,
- has_logs=False,
- created_at=None,
- job_completion_time=None,
- dataset_description_path=<factory>,
- output_paths=<factory>,
- log_paths=<factory>,
Bases:
objectA
JobInfo(identity) plus the status fields written bywrite_queue_stateand consumed across the queue module.- Parameters:
- job: JobInfo#
- property identity: tuple#
Stable key for matching queue/state/last-submitted entries.
Excludes
codebasedeliberately: an attempt is the same logical job regardless of which codebase version produced it.
- attempt_dir_candidates(base_dir)[source]#
Return
(flat_layout_path, legacy_nested_layout_path)for this attempt.- Parameters:
base_dir (
Path) – Root of the local Dandiset tree to resolve paths under.- Raises:
ValueError – If this entry’s
dandi_pathis an empty string.- Return type:
- resolve_attempt_dir(base_dir)[source]#
Resolve the best on-disk attempt-directory path for this entry.
- resolve_unsubmitted_attempt_dir(base_dir)[source]#
Resolve the attempt directory only if this entry is queued but unsubmitted.
Returns
Nonewhen the entry is not pending (seeis_pending) or when a submitted marker (code/submittedorcode/submitted_date-*) is present on disk.
- class dandi_compute_code.queue.JobInfo(dandiset_id, dandi_path, pipeline, version, params, config, attempt, codebase)[source]#
Bases:
object- Parameters:
- dandi_compute_code.queue.prepare_queue(
- *,
- queue_directory,
- pipeline_directory=None,
- config_key='default',
- content_ids=None,
- limit=None,
En-masse preparation of qualifying assets based on the current queue config.
For every pipeline/version/params combination declared in
queue_config.jsonthis function determines which content IDs to prepare and callsprepare_aind_ephys_job()for each asset — generating thecode/directory and its parent directories without submitting a job.The per-pipeline failure cap (
max_fail_per_dandisetinqueue_config.json) is enforced by reading the existingstate.jsonlfile inside queue_directory. Entries withhas_code=True,has_logs=True, andhas_output=Falseare counted as failures for the relevant pipeline, version, and source Dandiset. Runwrite_queue_state()beforehand to ensurestate.jsonlis up to date.- Parameters:
queue_directory (
Path) – Path to the queue root directory.pipeline_directory (
Path|None) – Local path to the AIND pipeline repository. Passed directly toprepare_aind_ephys_job().config_key (
str) – Key for a registered job configuration. Passed directly toprepare_aind_ephys_job().content_ids (
list[str] |None) – Explicit list of content IDs to prepare. When provided, the qualifying content IDs list is not fetched from the network and these IDs are used directly instead. Useful for targeted runs such as testing with one or more known content IDs.limit (
int|None) – If provided, stop after preparing limit assets in total (across all pipeline/version/params combinations). When qualifying IDs are fetched automatically, they are randomized in round-robin order across source Dandisets before this limit is applied. Useful for testing.
- Return type:
- dandi_compute_code.queue.process_queue(
- *,
- queue_directory,
- processing_directory,
- max_concurrent_aind_jobs=2,
- jitter_seconds=30.0,
- test=False,
Submit jobs from
state.jsonlup tomax_concurrent_aind_jobstotal runningAIND-Ephys-PipelineSLURM jobs.If
state.jsonlis absent, aFileNotFoundErroris raised. Ifstate.jsonlexists but is empty, a warning is emitted and the invocation returns without submitting jobs. Otherwisesqueue --meis checked for currently runningAIND-Ephys-Pipelinejobs, and up to the difference frommax_concurrent_aind_jobsjobs are submitted.A random delay of up to jitter_seconds is applied before any work is done to spread out concurrent invocations and avoid thundering-herd submission bursts.
- Parameters:
queue_directory (
Path) – Path to the queue root directory.processing_directory (
Path) – Path to the directory used for temporary working trees during job submission.max_concurrent_aind_jobs (
int) – Maximum number ofAIND-Ephys-Pipelinejobs allowed to be running concurrently before new submissions are skipped.jitter_seconds (
float) – Maximum number of seconds to sleep before proceeding. A uniformly random duration between0and jitter_seconds is chosen each invocation. Set to0to disable jitter entirely.test (
bool) – IfTrue, preserve temporary processing directories on success.
- Returns:
"submitted"when one or more jobs were submitted,"no-pending"when no pending jobs were available to submit, and"slots-unavailable"when submission was skipped because no queue slots were available.- Return type:
Literal['submitted','no-pending','slots-unavailable']- Raises:
FileNotFoundError – If
state.jsonlis not found in queue_directory.ValueError – If jitter_seconds is negative.
- class dandi_compute_code.queue.QueueState(entries)[source]#
Bases:
objectContainer for all entries in
state.jsonl.Replaces the scattered
list[dict]reads in_prepare_queue.py,_aggregate_queue_statistics.py,_process_queue.py, and_clean_unsubmitted_capsules.py.- property successful_asset_bytes_total: int#
Total source-asset bytes across successful entries with a known size.
- content_id_to_dandiset_ids()[source]#
Map each
content_idto the set of source Dandiset IDs it appears under.A content ID is expected to map to a single source Dandiset in normal operation; ambiguous mappings (more than one) are surfaced so callers can handle them conservatively.
- entry_for(*, dandi_path, attempt=1)[source]#
Return the entry with the given
dandi_path(andattempt).
- static pending_code_dirs()[source]#
Identify attempt
codedirectories awaiting submission from DANDI assets metadata.Loads the DANDI
assets.jsonldmetadata and collects every attempt directory that contains acode/submit.shasset but no adjacent submitted-marker asset. An entry is considered submitted when a siblingsubmittedasset exists, or when a sibling asset whose name starts withsubmitted_date-exists.
- classmethod has_pending_jobs()[source]#
Report whether any queued jobs are awaiting submission.
Lightweight check intended to gate queue dispatch: it inspects the DANDI assets metadata for attempt directories that contain a
code/submit.shasset without an adjacent submitted marker. It does not submit anything and does not require SLURM access.- Return type:
- classmethod submit_next(*, processing_directory, max_submissions=2, test=False)[source]#
Submit the next eligible pending entries from the DANDI assets metadata.
Identifies all attempt directories that contain a
code/submit.shasset but no adjacent submitted-marker asset (seepending_code_dirs()). For each candidate (up to max_submissions), a temporary working directory is created inside processing_directory, thecode/tree is downloaded viadandi download --preserve-tree, the submission script is executed viasbatch, a submitted marker is written adjacent tosubmit.sh, the marker is pushed back to the archive viadandi upload --allow-any-path, and the temporary directory is removed on success.- Parameters:
- Returns:
Trueif at least one job was submitted,Falseotherwise.- Return type:
- Raises:
RuntimeError – If
dandi download,sbatch, ordandi uploadreturns a non-zero exit code for any candidate.
- static count_running_aind_ephys_pipeline_jobs()[source]#
Count currently running AIND Ephys pipeline jobs via the SLURM scheduler.
Calls
squeue --me --format=%jand counts jobs whose name is exactlyAIND-Ephys-Pipeline.- Raises:
RuntimeError – If the
squeueinvocation exits non-zero and writes to standard error.- Return type:
- static load_queue_config(*, queue_directory)[source]#
Read and validate
queue_config.jsonunder queue_directory.- Raises:
FileNotFoundError – If
queue_config.jsonis not found.ValueError – If the queue configuration fails LinkML validation.
- Return type:
- Parameters:
queue_directory (Path)
- static resolve_params_key_to_id(pipeline, params_key)[source]#
Resolve a human-readable parameters key to its 7-character hash ID.
For the
aind+ephyspipeline the lookup is performed against the registered params registry. For any other pipeline, or if the key is not found, params_key is returned unchanged so callers that already store raw hash IDs continue to work.
- classmethod from_metadata(metadata, /)[source]#
Build a queue state from indexed DANDI assets metadata.
Each entry represents one attempt capsule inferred from the
derivatives/dandiset-*/.../pipeline-*/..._attempt-*path structure, withcontent_id/asset_size_bytesresolved from the upstream source Dandiset’sassets.jsonld.- Parameters:
metadata (
AssetsJsonldMetadata) – Indexed assets metadata, as produced byfrom_jsonld()orfrom_dandi().- Return type:
- classmethod from_jsonld(*, file_path)[source]#
Build a queue state from a local DANDI
assets.jsonldfile.The file should be a JSON file whose content is a list of asset dicts with
path,contentSize,dateModified, andcontentUrlfields (matching theassets.jsonldlayout from DANDI). The.jsonldfile is preferred over itsassets.yamlcounterpart at the same S3 location because JSON parsing is many times faster than YAML for identical content.- Parameters:
file_path (
Path) – Path to a local assets JSON-LD file.- Raises:
ValueError – If the file content is not a JSON array.
- Return type:
- classmethod from_dandi(*, dandiset_id='001697')[source]#
Build a queue state from a Dandiset’s remote
assets.jsonldmetadata.Fetches
assets.jsonldfor dandiset_id from the DANDI S3 bucket over the network.- Parameters:
dandiset_id (
str) – The Dandiset whoseassets.jsonldis read. Defaults to the job capsules Dandiset (001697).- Return type:
- classmethod write_state(
- *,
- queue_directory,
- dandiset_id='001697',
- state_file_name='state.jsonl',
Write a queue state file from DANDI
assets.jsonldmetadata.Validates
queue_config.jsonunder queue_directory, builds the state viafrom_dandi(), and writes it toqueue_directory/state_file_name.- Parameters:
- Raises:
FileNotFoundError – If
queue_config.jsonis not found.ValueError – If the queue configuration fails LinkML validation.
- Return type:
- classmethod write_archive_state(*, queue_directory)[source]#
Write
archive_state.jsonlfrom the failed runs archiveassets.jsonld.The archive counterpart to
write_state(); produces an identically structured state file adjacent tostate.jsonlportraying the failed runs archive Dandiset (001873) rather than the job capsules Dandiset.
- aggregate_statistics(
- *,
- queue_directory,
- dandiset_directory,
- output_file_name='queue_stats.json',
Write aggregate queue statistics JSON and return the written payload.
- clean_unsubmitted_capsules(*, dandiset_directory)[source]#
Remove all queued (unsubmitted) capsule directories from the dandiset tree.
A capsule is queued when its attempt directory has a
code/subdirectory but nologs/orderivatives/content and no submitted marker. Each matching attempt directory is deleted from the DANDI archive (viadandi delete) and the local filesystem.- Parameters:
dandiset_directory (
Path) – Local clone of the dandiset used to resolve and delete matching attempt directories.- Returns:
Attempt directory paths that were deleted.
- Return type:
- Raises:
RuntimeError – If
DANDI_API_KEYis not set or is blank.
- classmethod process_queue(
- *,
- queue_directory,
- processing_directory,
- max_concurrent_aind_jobs=2,
- jitter_seconds=30.0,
- test=False,
Submit jobs from
state.jsonlup tomax_concurrent_aind_jobstotal runningAIND-Ephys-PipelineSLURM jobs.- Parameters:
queue_directory (
Path) – Path to the queue root directory.processing_directory (
Path) – Directory for temporary working trees during submission.max_concurrent_aind_jobs (
int) – Maximum concurrentAIND-Ephys-Pipelinejobs.jitter_seconds (
float) – Maximum random delay (seconds) before processing;0disables.test (
bool) – IfTrue, preserve temporary processing directories on success.
- Raises:
FileNotFoundError – If
state.jsonlis not found in queue_directory.ValueError – If jitter_seconds is negative or max_concurrent_aind_jobs < 1.
- Return type:
Literal['submitted','no-pending','slots-unavailable']
- classmethod prepare(
- *,
- queue_directory,
- pipeline_directory=None,
- config_key='default',
- content_ids=None,
- limit=None,
En-masse preparation of qualifying assets based on the current queue config.
For every pipeline/version/params combination declared in
queue_config.jsonthis determines which content IDs to prepare and callsprepare_aind_ephys_job()for each asset. The per-pipeline failure cap (max_fail_per_dandiset) is enforced by reading the existingstate.jsonlunder queue_directory.- Parameters:
queue_directory (
Path) – Path to the queue root directory.pipeline_directory (
Path|None) – Local path to the AIND pipeline repository.config_key (
str) – Key for a registered job configuration.content_ids (
list[str] |None) – Explicit content IDs to prepare; when provided, the qualifying list is not fetched from the network.limit (
int|None) – If provided, stop after preparing limit assets in total.
- Return type:
- static dump_issues(
- *,
- dandiset_directory,
- queue_directory,
- output_file_name='issues_dump.json',
Scan nextflow/slurm logs and write per-capsule error lines under queue_directory.
- static summarize_issues(
- *,
- dandiset_directory,
- queue_directory,
- dump_output_file_name='issues_dump.json',
- output_file_name='issues_summary.json',
Write descending error-frequency summary where keys are counts and values are error strings.
- classmethod from_jsonl(file_path, /)[source]#
Load from an existing
state.jsonlfile.- Parameters:
file_path (
Path) – Path to thestate.jsonlfile to read.- Raises:
FileNotFoundError – If file_path does not exist.
- Return type:
- dandi_compute_code.queue.summarize_issues(
- *,
- dandiset_directory,
- queue_directory,
- dump_output_file_name='issues_dump.json',
- output_file_name='issues_summary.json',
Write descending error-frequency summary where keys are counts and values are error strings.
- dandi_compute_code.queue.write_archive_state(*, queue_directory)[source]#
Write
archive_state.jsonlfrom the failed runs archiveassets.jsonld.This is the archive counterpart to
write_queue_state(). It produces an identically structured state file that lives adjacent tostate.jsonlin queue_directory, but portrays the state of the failed runs archive Dandiset (001873) — wheredandicompute archive jobmoves capsules — rather than the job capsules Dandiset (001697) where jobs run.
- dandi_compute_code.queue.write_queue_state(
- *,
- queue_directory,
- dandiset_id='001697',
- state_file_name='state.jsonl',
Write a queue state file from DANDI
assets.jsonldmetadata.Each state entry represents one attempt capsule inferred from the
derivatives/dandiset-*/.../pipeline-*/..._attempt-*path structure inassets.jsonld.dandi_pathis derived from the path segment betweendandiset-*andpipeline-*with.nwbappended.has_code/has_been_submitted/has_output/has_logsare inferred from the assets present under each attempt directory.dataset_description_pathmaps the root-leveldataset_description.jsonasset path to its blob ID when present.output_pathsmaps each output asset path to its blob ID whenhas_outputisTrue; otherwise it is an empty dict.log_pathsmaps each log asset path to its blob ID whenhas_logsisTrue; otherwise it is an empty dict.For meta-analysis dandisets, the
content_idandasset_size_bytesof each attempt’s source NWB are resolved by fetching the upstream dandiset’sassets.jsonld.created_atcomes from the localcode/submit.shmodification time;job_completion_timeis the latest modification time among log files.- Parameters:
- Return type: