Skip to content

ref: Remove string, msg and activation clones#674

Open
untitaker wants to merge 3 commits into
mainfrom
remove-clones
Open

ref: Remove string, msg and activation clones#674
untitaker wants to merge 3 commits into
mainfrom
remove-clones

Conversation

@untitaker
Copy link
Copy Markdown
Member

@untitaker untitaker commented Jun 4, 2026

  • Remove one unnecessary Arc<OwnedMessage> in favor of passing &OwnedMessage. This does not avoid string copies but avoids 1 refcount, and one awkward try_unwrap.
  • Refactor activationstore to take slices of activations. This avoids a clone on the caller side, and it seems activationstores are mostly fine with borrowing.
  • Refactor TableRow to borrow from activation. Note we have to borrow activations now, so we can't convert by move. We definitely want to keep a separate struct because otherwise activation will have to derive sqlx traits, and that'll be messy as sqlx is an impl detail of each activation, and the schemas might want to diverge. But we can manually impl FromRow for TableRow and therefore use Cow<'a, str>. When reading activations from the DB, sqlx will allocate strings for us (Cow::Owned) and there's no way to avoid that.

Most likely this will not accomplish any perf speedup, but it's not introducing too many painful lifetimes so still worth it imo.

@untitaker untitaker requested a review from a team as a code owner June 4, 2026 11:38
Comment thread src/upkeep.rs
Comment on lines 541 to 547
/// Create a new activation that is a 'retry' of the passed activation
/// The retry_state.attempts is advanced as part of the retry state machine.
#[instrument(skip_all)]
fn create_retry_activation(activation: &TaskActivation) -> TaskActivation {
let mut new_activation = activation.clone();

fn create_retry_activation(mut new_activation: TaskActivation) -> TaskActivation {
let now = Utc::now();
new_activation.id = Uuid::new_v4().into();
new_activation.received_at = Some(Timestamp {
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: Calling .and_then() on new_activation.retry_state consumes the Option, preventing the retry attempts counter from being incremented in the subsequent if let block.
Severity: CRITICAL

Suggested Fix

To avoid consuming the Option, use as_ref() before calling and_then. The line should be changed to new_activation.delay = new_activation.retry_state.as_ref().and_then(|retry_state| retry_state.delay_on_retry);. This borrows the retry_state instead of moving it, allowing the subsequent code to access and modify it.

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: src/upkeep.rs#L541-L547

Potential issue: In the `create_retry_activation` function, the call to `.and_then()` on
`new_activation.retry_state` consumes the `Option`, moving the value out and leaving
`new_activation.retry_state` as `None`. As a result, the subsequent `if let` block that
is supposed to increment the `attempts` counter will never execute. This breaks the
retry state machine, causing tasks to be retried without their attempt counters being
incremented, which can lead to infinite retries.

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

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.

honestly this kind of comment is why i wish these bots would just wait for the thing to compile

@evanh
Copy link
Copy Markdown
Member

evanh commented Jun 5, 2026

Took some thinking but I think this all makes sense.

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