Skip to content

Add MarQS requeueMessage, an atomic version of replaceMessage #1717

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 19, 2025

Conversation

ericallam
Copy link
Member

@ericallam ericallam commented Feb 19, 2025

Summary by CodeRabbit

  • New Features

    • Introduced an enhanced message requeue mechanism to streamline task retries and improve system stability.
    • Extended subscriber capabilities for better tracking and handling of queued operations.
  • Refactor

    • Simplified message replacement flows for more consistent task processing.
    • Updated various service interactions to leverage the new requeue functionality, ensuring smoother and more reliable task handling.

Copy link
Contributor

coderabbitai bot commented Feb 19, 2025

Walkthrough

The changes refactor how the messaging system handles message updates and requeuing. The MarQS class in the web application has been updated by simplifying the replaceMessage method and introducing a new requeueMessage method, alongside an accompanying Redis command. Several service files now call requeueMessage instead of replaceMessage for various retry and resume scenarios. Additionally, a method for handling requeued messages has been added to the TaskRunConcurrencyTracker class and the MessageQueueSubscriber interface has been extended to support requeued message notifications.

Changes

File(s) Change Summary
apps/webapp/.../marqs/index.server.ts Updated the MarQS class: removed extra parameters from replaceMessage, added requeueMessage, and introduced a new Redis command for message requeuing.
apps/webapp/.../marqs/types.ts Extended the MessageQueueSubscriber interface with a new messageRequeued method to support requeuing notifications.
apps/webapp/.../services/{completeAttempt,createCheckpoint,resumeBatchRun,resumeTaskDependency}.server.ts Replaced calls to replaceMessage with requeueMessage to update messaging behavior across various retry/resume scenarios; added a new static enqueue method in resumeBatchRun.server.ts.
apps/webapp/.../services/taskRunConcurrencyTracker.server.ts Added a new messageRequeued method to log and process requeued message data, including error handling for message parsing.

Sequence Diagram(s)

sequenceDiagram
  participant S as Service (e.g., CompleteAttemptService)
  participant M as MarQS
  participant R as Redis
  participant C as TaskRunConcurrencyTracker

  S->>M: Call requeueMessage(messageId, messageData, timestamp)
  M->>R: Execute Redis command requeueMessage
  R-->>M: Return acknowledgement
  M->>C: Trigger messageRequeued(oldQueue, message)
  C-->>M: Log and process message data
Loading

Possibly related PRs

Poem

Oh, I'm a bunny with code so bright,
Hopping through queues both day and night,
Replacing and requeuing with a joyful cheer,
Simplifying logic, making things clear,
With each line of change, my heart takes flight!
🐇💻 Celebrate the flow and keep it light!

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

changeset-bot bot commented Feb 19, 2025

⚠️ No Changeset found

Latest commit: 76d14d6

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@ericallam ericallam merged commit 23b4c2a into main Feb 19, 2025
10 of 11 checks passed
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/webapp/app/v3/marqs/index.server.ts (1)

1204-1274: Add error handling for the internal #callRequeueMessage
This helper updates Redis to requeue the message and logs the result, always returning true. Consider handling unexpected Redis errors or null responses in the future.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7b1159e and 76d14d6.

📒 Files selected for processing (7)
  • apps/webapp/app/v3/marqs/index.server.ts (7 hunks)
  • apps/webapp/app/v3/marqs/types.ts (1 hunks)
  • apps/webapp/app/v3/services/completeAttempt.server.ts (2 hunks)
  • apps/webapp/app/v3/services/createCheckpoint.server.ts (2 hunks)
  • apps/webapp/app/v3/services/resumeBatchRun.server.ts (1 hunks)
  • apps/webapp/app/v3/services/resumeTaskDependency.server.ts (1 hunks)
  • apps/webapp/app/v3/services/taskRunConcurrencyTracker.server.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/webapp/app/v3/services/completeAttempt.server.ts
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (11)
apps/webapp/app/v3/marqs/index.server.ts (5)

274-306: Clarify data layering when preserving trace context
The final spread of traceContext could overwrite fields in messageData if they share keys like traceparent or tracestate. Confirm this layering is the intended behavior.


310-365: Requeue logic appears valid; confirm behavior for identical queues
This new requeueMessage method handles old-message retrieval, sets new data, and re-queues. For clarity, verify the scenario when the old and new queues are the same.


643-643: Confirm timestamp usage when replacing message
Using the retryAt value as the new timestamp adjusts scheduling. Double-check that this aligns with existing retry semantics and doesn’t introduce unexpected timing logic.


1700-1754: New Redis requeueMessage command
The Lua script removes the message from the old queue, reenqueues it in the new queue, then rebalances. Confirm that removing and re-adding the same queue reference is intentional if the queue names match.


260-264:

✅ Verification successful

Simplify method signature and check usage updates
Removing the priority and inplace parameters yields a cleaner interface for replaceMessage, but verify that no external calls depend on them.

Run the script below to locate any remaining references to the old signature:


🏁 Script executed:

#!/bin/bash
rg -A 5 'replaceMessage'

Length of output: 3292


Signature Simplification Verified – No External Dependencies on Removed Parameters
The search confirms that all external calls to replaceMessage only pass the three arguments (messageId, messageData, and timestamp), and there are no invocations relying on the removed priority or inplace parameters.

  • Verified in apps/webapp/app/v3/services/createCheckpoint.server.ts that the calls use the new two/three argument structure.
  • Internal calls in apps/webapp/app/v3/marqs/index.server.ts are consistent with the simplified signature.
apps/webapp/app/v3/marqs/types.ts (1)

103-103: New messageRequeued interface method
Adding messageRequeued contributes clear handling for requeue operations. Ensure all implementations of MessageQueueSubscriber provide consistent logic or no-ops as needed.

apps/webapp/app/v3/services/resumeTaskDependency.server.ts (1)

92-107: Switched from replaceMessage to requeueMessage
Using requeueMessage clears concurrency sets and re-injects the message with updated attributes, which can better handle long-lived dependencies. Confirm no side effects if the dependent attempt relies on old concurrency data.

apps/webapp/app/v3/services/createCheckpoint.server.ts (2)

170-179: LGTM! Atomic message requeuing for duration-based resumption.

The change to use requeueMessage for scheduling message resumption after a duration is appropriate and maintains the atomic nature of the operation.


300-302: LGTM! Simplified message replacement for batch waiting.

The simplified replaceMessage call is appropriate here as it only needs to update the checkpoint event ID while maintaining the message in place.

apps/webapp/app/v3/services/resumeBatchRun.server.ts (2)

255-273: LGTM! Atomic message requeuing for dependent task resumption.

The change to use requeueMessage for resuming dependent tasks is appropriate and maintains the atomic nature of the operation. The original queue timestamp is correctly used to maintain message ordering.


332-349: LGTM! Well-structured batch run enqueue method.

The new static enqueue method is well-designed:

  • Takes all necessary parameters including transaction support
  • Provides flexible job key handling
  • Supports scheduled execution through optional runAt

Comment on lines +124 to +148
async messageRequeued(oldQueue: string, message: MessagePayload): Promise<void> {
logger.debug("TaskRunConcurrencyTracker.messageRequeued()", {
data: message.data,
messageId: message.messageId,
oldQueue,
});

const data = this.getMessageData(message);

if (!data) {
logger.info(
`TaskRunConcurrencyTracker.messageReplaced(): could not parse message data`,
message
);
return;
}

await this.executionFinished({
projectId: data.projectId,
taskId: data.taskIdentifier,
runId: message.messageId,
environmentId: data.environmentId,
deployed: data.environmentType !== "DEVELOPMENT",
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix incorrect error message in messageRequeued method.

The error message uses "messageReplaced" instead of "messageRequeued" which could be confusing for debugging.

Apply this diff to fix the error message:

-        `TaskRunConcurrencyTracker.messageReplaced(): could not parse message data`,
+        `TaskRunConcurrencyTracker.messageRequeued(): could not parse message data`,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async messageRequeued(oldQueue: string, message: MessagePayload): Promise<void> {
logger.debug("TaskRunConcurrencyTracker.messageRequeued()", {
data: message.data,
messageId: message.messageId,
oldQueue,
});
const data = this.getMessageData(message);
if (!data) {
logger.info(
`TaskRunConcurrencyTracker.messageReplaced(): could not parse message data`,
message
);
return;
}
await this.executionFinished({
projectId: data.projectId,
taskId: data.taskIdentifier,
runId: message.messageId,
environmentId: data.environmentId,
deployed: data.environmentType !== "DEVELOPMENT",
});
}
async messageRequeued(oldQueue: string, message: MessagePayload): Promise<void> {
logger.debug("TaskRunConcurrencyTracker.messageRequeued()", {
data: message.data,
messageId: message.messageId,
oldQueue,
});
const data = this.getMessageData(message);
if (!data) {
logger.info(
`TaskRunConcurrencyTracker.messageRequeued(): could not parse message data`,
message
);
return;
}
await this.executionFinished({
projectId: data.projectId,
taskId: data.taskIdentifier,
runId: message.messageId,
environmentId: data.environmentId,
deployed: data.environmentType !== "DEVELOPMENT",
});
}

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.

1 participant