Skip to content

feat: Add volatile schedule storage#605

Merged
markstory merged 3 commits intomainfrom
feat-volatile-run-storage
Apr 24, 2026
Merged

feat: Add volatile schedule storage#605
markstory merged 3 commits intomainfrom
feat-volatile-run-storage

Conversation

@markstory
Copy link
Copy Markdown
Member

Seer has been running without durable storage for scheduled tasks, and it also does not have a redis instance provisioned. With seer's scheduled tasks all using crontab expressions, adding a durable storage is very low value, as durable storage is only required for timedelta schedules.

Having a volatile storage backend will make it simpler and cheaper to shift seer to taskbroker.

Seer has been running without durable storage for scheduled tasks, and
it also does not have a redis instance provisioned. With seer's
scheduled tasks all using `crontab` expressions, adding a durable
storage is very low value, as durable storage is only required for
`timedelta` schedules.

Having a volatile storage backend will make it simpler and cheaper to
shift seer to taskbroker.
@markstory markstory requested a review from a team as a code owner April 22, 2026 19:59
Comment thread clients/python/src/taskbroker_client/scheduler/storage.py Outdated


def test_volatile_run_storage_set_returns_true() -> None:
storage = VolatileRunStorage()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is the redis version covered by the previous RunStorage tests ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, all the existing scheduler tests use Redis storage currently.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0883c0d. Configure here.

value = self._data.get(key, None)
if value is None:
return None
return value[0]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

read and read_many ignore expiry in volatile storage

Medium Severity

VolatileRunStorage.read() and read_many() return the stored timestamp without checking whether the entry has expired (_data[key][1] <= now). The set() method correctly checks the expiry time before allowing overwrites, but read() ignores it entirely. This violates the interface contract documented in the docstring ("Returns None if last run time has expired or is unknown") and breaks behavioral parity with the Redis-backed RunStorage, where expired keys automatically return None via TTL. If _load_last_run is invoked (e.g. after add() clears the heap) while expired entries exist, stale last-run times will be loaded instead of None.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0883c0d. Configure here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

That's ok. The if the time is in the past, the schedules will still advance.

Comment thread clients/python/src/taskbroker_client/scheduler/storage.py Outdated
self._redis.delete(self._make_key(key))


RedisRunStorage = RunStorage
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unused RedisRunStorage alias is dead code

Low Severity

RedisRunStorage is defined as an alias for RunStorage but is never imported or referenced anywhere in the codebase. It isn't included in any __all__, isn't documented, and has no consumers. This dead code could confuse developers about whether RunStorage and RedisRunStorage are distinct classes.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0883c0d. Configure here.

Comment on lines +86 to +96

Returns a mapping keyed by new storage_key.
"""
results: dict[str, datetime | None] = {}
for key in storage_keys:
value = self._data.get(key, None)
if value is None:
results[key] = value
else:
results[key] = value[0]
return results
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: VolatileRunStorage.read() and read_many() do not check for expiry, which is inconsistent with the set() method and violates the RunStorageProtocol contract.
Severity: LOW

Suggested Fix

Add an expiry check in VolatileRunStorage.read() and read_many(). Before returning a value, check if its expiry timestamp has passed. If it has, the entry should be treated as expired and not be returned, similar to the logic in the set() method.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: clients/python/src/taskbroker_client/scheduler/storage.py#L83-L96

Potential issue: The `VolatileRunStorage.read()` and `read_many()` methods return stored
`run_time` values without checking if the associated expiry datetime has passed. This is
inconsistent with the `set()` method, which does perform an expiry check, and violates
the contract defined by `RunStorageProtocol`. While the current scheduler implementation
does not trigger incorrect behavior because it only calls these methods on an empty
storage, this inconsistency makes the code brittle. Future refactoring or changes in how
the scheduler uses the storage could lead to bugs where stale, unexpired data is read
and processed, potentially causing missed task executions.

Also affects:

  • clients/python/src/taskbroker_client/scheduler/storage.py:73~81

Did we get this right? 👍 / 👎 to inform future reviews.

@markstory markstory merged commit 0f4cc6c into main Apr 24, 2026
40 of 45 checks passed
@markstory markstory deleted the feat-volatile-run-storage branch April 24, 2026 15:25
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