Skip to content

Don't build a dep map (twice!) when creating a new task.#757

Open
carmiac wants to merge 2 commits into
GothenburgBitFactory:mainfrom
carmiac:create_perf_fix
Open

Don't build a dep map (twice!) when creating a new task.#757
carmiac wants to merge 2 commits into
GothenburgBitFactory:mainfrom
carmiac:create_perf_fix

Conversation

@carmiac

@carmiac carmiac commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Bulk creating tasks is O(n^2) because of a redundant depmap scan.

Replica::create_task builds the full depmap for a brand new task, but that map is only ever queried for the tasks own UUID and a brand new task has no dependencies or dependents.

The map is invalidated on every commit_operations, so bulk creating tasks one at a time (like TDB2::add does) does a full scan on every task. This is the cause of the test timeout on GothenburgBitFactory/taskwarrior#4157

This changes the new task creation to skip the scan and use an empty depmap, which will have the same results for a new task without the scan. Existing tasks still get the real depmap.

This moves bulk task import from O(n^2) to O(n), and new task creation
from O(n) to O(1).
@carmiac
carmiac requested a review from djmitche as a code owner July 24, 2026 13:53

@djmitche djmitche left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm not convinced this is actually fixing a problem. Is the dependency_map function actually making the same DB queries more than once, or is it just called more than once? If it's making the DB queries more than once, then is something invalidating it between those fetches? The invalidation occurs in the commit_operations and commit_reversed_operations methods.

Comment thread src/replica.rs
return Ok(task);
// Check for task existence without building the depmap if it doen't exist.
if let Some(taskdata) = self.get_task_data(uuid).await? {
let depmap = self.dependency_map(false).await?;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The dependency_map function does nothing if the Replica already has a valid dependency map, so calling it twice isn't actually an issue.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ah, I missed the caching semantics, thanks!

Comment thread src/replica.rs
// map is fine and much faster.
Ok(Task::new(
TaskData::create(uuid, ops),
Arc::new(DependencyMap::new()),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is incorrect, although in a relatively harmless fashion. The depmap is a map for all tasks, although it's currently only used for dependencies related to this task, of which there are none in htis case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good point, I've updated the comment to better explain the change.

@carmiac

carmiac commented Jul 25, 2026

Copy link
Copy Markdown
Contributor Author

Is the dependency_map function actually making the same DB queries more than once, or is it just called more than once? If it's making the DB queries more than once, then is something invalidating it between those fetches? The invalidation occurs in the commit_operations and commit_reversed_operations methods.

Yes, the invalidation is called in TaskWarriors TDB2::add when using Task rather than raw TaskData, which makes bulk task creation slow. I've updated the PR description and comments with the full logic.

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