Skip to content

Add --runDir option; make --workDir/--coordinationDir create-if-missing (#5516) - #5560

Open
annagiroti wants to merge 12 commits into
masterfrom
issues/5516-runtime-storage-log-paths
Open

Add --runDir option; make --workDir/--coordinationDir create-if-missing (#5516)#5560
annagiroti wants to merge 12 commits into
masterfrom
issues/5516-runtime-storage-log-paths

Conversation

@annagiroti

Copy link
Copy Markdown
Collaborator

Changelog Entry

Added --runDir/TOIL_RUN_DIR to derive the job store, work dir, coordination dir, and CWL/WDL image cache locations under one directory. --workDir/--coordinationDir are now created automatically if missing instead of raising, and Toil logs the resolved run paths at startup.

Known follow-up: the job store path derived from --runDir is fixed, so concurrent workflows sharing one --runDir will collide. Flagged with TODOs pending a fix.

Addresses #5516

To be copied to the draft changelog by merger:

  • PR submitter writes their recommendation for a changelog entry here

Reviewer Checklist

  • Make sure it is coming from issues/XXXX-fix-the-thing in the Toil repo, or from an external repo.
    • If it is coming from an external repo, make sure to pull it in for CI with:
      contrib/admin/test-pr otheruser theirbranchname issues/XXXX-fix-the-thing
      
    • If there is no associated issue, create one.
  • Read through the code changes. Make sure that it doesn't have:
    • Addition of trailing whitespace.
    • New variable or member names in camelCase that want to be in snake_case.
    • New functions without type hints.
    • New functions or classes without informative docstrings.
    • Changes to semantics not reflected in the relevant docstrings.
    • New or changed command line options for Toil workflows that are not reflected in docs/running/{cliOptions,cwl,wdl}.rst
    • New features without tests.
  • Comment on the lines of code where problems exist with a review comment. You can shift-click the line numbers in the diff to select multiple lines.
  • Finish the review with an overall description of your opinion.

Merger Checklist

  • Make sure the PR passed tests, including the Gitlab tests, for the most recent commit in its branch.
  • Make sure the PR has been reviewed. If not, review it. If it has been reviewed and any requested changes seem to have been addressed, proceed.
  • Merge with the Github "Squash and merge" feature.
    • If there are multiple authors' commits, add Co-authored-by to give credit to all contributing authors.
  • Copy its recommended changelog entry to the Draft Changelog.
  • Append the issue number in parentheses to the changelog entry.

…og warning

--workDir and --coordinationDir get created automatically instead
of raising when missing, matching --batchLogsDir's existing behavior.
ensure_dir_exists moves to toil.lib.io and backstops this in
Leader.run(). The "no log file" warning on job failure now checks the
batch system's own logs first, so it isn't shown next to log content
Toil already found. Adds tests for dir auto-creation and --runDir
derivation/override precedence.
Comment thread src/toil/leader.py Outdated
Comment thread src/toil/cwl/cwltoil.py
Comment thread src/toil/wdl/wdltoil.py Outdated
Comment thread src/toil/test/options/options.py
Comment thread src/toil/common.py Outdated
@annagiroti
annagiroti marked this pull request as ready for review August 6, 2026 20:07
Comment thread src/toil/test/cwl/cwlTest.py Outdated
Comment on lines +255 to +258
Test that --runDir derives the CWL image cache location. The job
store/work dir derivation is covered by
commonTests.TestDeriveRunDirDefaults; cachedir is CWL-specific and
isn't.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually you want a docstring to have a one-line summary, then a blank line, and then any further description.

The further description right now is mostly about what isn't here and why it shouldn't be here. I don't think that the reader is at risk of thinking that those things should have been here, so trying to disabuse them of that notion seems more likely to confuse them than to help, because they're going to read this and then be confused as to why anyone would think they would have thought that. I would just cut everything that isn't about this test itself.

Comment thread src/toil/test/cwl/cwlTest.py Outdated
Comment thread src/toil/leader.py Outdated
Comment on lines +261 to +275
# exists. These calls are a defensive backstop for any code path that
# reaches Leader.run() without having gone through Toil.__enter__ first.
ensure_dir_exists(self.config.workDir, "--workDir")
ensure_dir_exists(self.config.coordination_dir, "--coordinationDir")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But ensure_dir_exists() is allowed to just exit the whole process if it e.g. doesn't have permission, right? Is someone's Python-based workflow going to be expecting that behavior from inside the Toil object, rather than an exception?

I might let ensure_dir_exists() propagate its various permission errors upward.

On the other hand, having it handle the exit makes the user-facing behavior in what's now the "normal" case of running a workflow nicer (because there's no stack trace), without us needing to put catches everywhere.

Comment thread src/toil/leader.py Outdated
Comment on lines +272 to +273
# exists. These calls are a defensive backstop for any code path that
# reaches Leader.run() without having gone through Toil.__enter__ first.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we think those codepaths are possible or legal? Outside Toil's own testing where we might independently instantiate a Leader?

I think we're not actually allowed to run a Leader for a workflow when not inside the Toil object context manager, so the thing we're trying to handle isn't possible and we shouldn't handle it (unless it's to complain that it has happened and something has gone terribly wrong).

Comment thread src/toil/leader.py
Comment thread src/toil/common.py
Remove dead ensure_dir_exists calls in Leader.run() (unreachable
codepath), compose the duplicate missing-log warning messages from
shared/variable strings, and simplify test_cwl_run_dir.

@adamnovak adamnovak left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the missing-log-file messages still aren't quite right.

The behavior where derive_run_dir_defaults() sometimes promises that the directories it returns exist, and sometimes doesn't, seems weird and under-justified; is there a good reason for this I am missing? Is it just to make sure that a failed directory creation can't print a wrong error message?

I found a few other things I don't like about the comments, that I probably should have found the first time.

Comment thread src/toil/common.py
Comment on lines +137 to +138
Creates the derived work dir and coordination dir; the job store
creates itself.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't clearly communicate that work_dir and coordination_dir will be created, if they didn't exist, only when they are defaulted from run_dir, and not when they are passed in. I think it does mean that (it talks about "derived" directories), but that's weird so if it does that it should mean that harder and more obviously.

Right now the tests test that behavior, but it's not clear to me why that's the right design. The caller is going to need to make sure the directories exist before using them whether they are determined by this function or passed through it, so this design means there need to be at least two places where we worry about directory creation.

If we made this function either always or never create the directories, we'd need to worry about it in fewer places.

Comment thread src/toil/leader.py
Comment on lines +269 to +276
if isinstance(self.batchSystem, AbstractGridEngineBatchSystem):
# The batch system isn't available yet when Toil logs the other
# resolved run paths (see Toil._log_resolved_paths), so log this
# one here instead, now that it exists. Only grid batch systems
# actually write their own logs to this directory.
logger.info(
"Resolved batch logs dir: %s", self.batchSystem.get_batch_logs_dir()
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could leave it like this, but it would be nicer if, if only some batch systems use a batch logs directory, we exposed that in the batch system API itself (maybe by making get_batch_logs_dir() return None when not used?), instead of needing to know here what implementations actually use it.

Comment thread src/toil/leader.py
Comment on lines 1617 to 1620
# returning logs to the machine that submitted jobs, or if
# --workDir / TOIL_WORKDIR is on a shared file system.
# They live directly in the Toil work directory because that is
# guaranteed to exist on the leader and workers.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment has, independent of the current changes, gone out of date, because the logs can be in --batchLogsDir instead now.

Comment thread src/toil/leader.py
Comment on lines +1685 to +1697
if batch_system_id is None:
variable_message = (
"Toil was not able to look for logs from the "
"batch system for this job; check the batch "
"system's own tools or logs directly."
)
else:
variable_message = (
"Toil looked for the batch system's own logs "
"(see --batchLogsDir) but found none; check the "
"batch system's own tools or logs directly if "
"you are running on a grid engine."
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neither of these is really quite applicable for when we're using a batch system like mesos (or I think even single_machine) which we know in advance don't keep batch logs for us using this system.

When Toil thinks the batch system should have sent batch logs, and it can't find them, it should complain. It can mention --batchLogsDir, and maybe suggest making sure it's on shared storage.

When Toil thinks the batch system shouldn't send batch logs for this job (maybe because batch_system_id is None, which I think could mean we're looking at a batch system that doesn't use the concept or a local job that didn't go to the cluster), it shouldn't complain if they aren't there.

Encouraging the user to ask the batch system themselves can be useful, because e.g. Mesos will have logs for them somewhere. It won't apply to every batch system, though; I don't think single_machine has anything. We could just deal with that, or we could try to figure out when the user has something to find. If we build a system to figure out when it's applicable we might as well make that system let the batch system explain to the user where they need to look ("check the Mesos web UI", etc.).

Comment thread src/toil/leader.py
Comment on lines +1681 to +1683
"Toil does not retain worker logs by default; rerun "
"with --writeLogs=PATH or --writeLogsGzip=PATH to "
"save failed jobs' logs to disk. "

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand how --writeLogs is expected to help here. The log we didn't find for the failing job is the one that would get shipped back to the leader for writing to the --writeLogs directory. Adding the flag would only get a log to appear here in very weird circumstances involving worker failure after saving logs for a job it thinks has succeeded, and that log would not really help a lot for failures that happen after it is written.

The message here makes it sound like in general failed job logs are not available when --writeLogs isn't passed, but that's not true.

Comment thread src/toil/common.py
Comment on lines +399 to +405
# user didn't set explicitly; explicit flags always win. The
# jobStore-derivation branch inside this is only reachable for
# direct callers of the flag-based parser (jobstore_as_flag=True)
# that leave --jobStore unset. The CWL/WDL runners fill in their
# own jobStore default before calling setOptions, and the plain
# `toil` entry point requires jobStore as a positional argument,
# so neither ever reaches it in practice.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't tell what aspect of the code here this long description of interactions between other pieces of code is meant to justify. If we need to explain these interactions, I don't think here's the right place.

Suggested change
# user didn't set explicitly; explicit flags always win. The
# jobStore-derivation branch inside this is only reachable for
# direct callers of the flag-based parser (jobstore_as_flag=True)
# that leave --jobStore unset. The CWL/WDL runners fill in their
# own jobStore default before calling setOptions, and the plain
# `toil` entry point requires jobStore as a positional argument,
# so neither ever reaches it in practice.
# user didn't set explicitly; explicit flags always win.

Comment thread src/toil/common.py
Log the resolved job store, work dir, and coordination dir paths
at INFO, once, so users can find them without tracing flag
precedence themselves. Skips the batch logs dir; the batch system
isn't built yet here, and grid systems log their own dir on startup.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do grid engine batch systems really log their own batch logs directory on startup? If so, why are we also logging it on their behalf in Leader.run()?

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.

2 participants