Skip to content

Feat message queue #2056

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

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

Stephen-S-H
Copy link

This feature adds an optional message queue that stores messages until a node is reachable and then retries sending.

#1979 (comment)

Enable the message queue
image
image

Send a message to a node that is not reachable
image

When Max Retransmission Attempts are reached, the message enters the queue:
image

Tap the clock icon for more info, to manually retry, or to delete the message from the queue (deletes message entirely)
image

When the node comes online and the app receives a packet from the node, it retries the send.
image

This seems to work on my devices nicely, but I'm new to both Meshtastic and Kotlin so please take a careful look over this PR to make sure I'm not doing anything unwise here.

@CLAassistant
Copy link

CLAassistant commented Jun 8, 2025

CLA assistant check
All committers have signed the CLA.

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@jamesarich jamesarich requested a review from Copilot June 8, 2025 11:54
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Adds an optional message queue feature to automatically retry failed text messages when target nodes become reachable, with UI controls and persistent storage.

  • Introduces string resources and settings for the queue
  • Extends UI components to display queued-for-retry status and allow manual retry/cancel
  • Implements database entities/DAO, retry strategy, and service integration for background processing

Reviewed Changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
app/src/main/res/values/strings.xml Added message queue text, status labels, and actions
app/src/main/java/com/geeksville/mesh/ui/message/components/MessageItem.kt Show “queued for retry” label and Schedule icon
app/src/main/java/com/geeksville/mesh/ui/message/MessageList.kt Updated DeliveryInfo dialog for retry and cancel
app/src/main/java/com/geeksville/mesh/service/RetryStrategy.kt New exponential backoff logic and status descriptions
app/src/main/java/com/geeksville/mesh/service/MessageQueueManager.kt Core queue management, enqueue/dequeue, processing
app/src/main/java/com/geeksville/mesh/service/MeshService.kt Hooks into service for network/node events
app/src/main/java/com/geeksville/mesh/database/entity/QueuedMessage.kt New Room entity for queued messages
app/src/main/java/com/geeksville/mesh/database/dao/MessageQueueDao.kt DAO for queue operations
app/src/main/java/com/geeksville/mesh/database/MeshtasticDatabase.kt Registered new entity and DAO
app/src/main/java/com/geeksville/mesh/database/DatabaseModule.kt Provided MessageQueueDao
app/src/main/java/com/geeksville/mesh/MainActivity.kt Settings dialog integration for toggling the queue
app/src/main/java/com/geeksville/mesh/DataPacket.kt Added MessageStatus.QUEUED_FOR_RETRY
Comments suppressed due to low confidence (1)

app/src/main/java/com/geeksville/mesh/ui/message/MessageList.kt:107

  • R.string.delete is not defined in strings.xml and will cause a build error. Consider replacing this with the existing R.string.message_queue_cancel_retry ("Remove from Queue") or adding a delete string resource if intended.
) { Text(text = stringResource(id = R.string.delete)) }

*/
fun getRetryStatusDescription(queuedMessage: QueuedMessage, currentTime: Long): String {
if (currentTime - queuedMessage.queuedTime > MAX_MESSAGE_AGE_MILLIS) {
return "Message expired (24h limit reached)"
Copy link
Preview

Copilot AI Jun 8, 2025

Choose a reason for hiding this comment

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

User-facing strings are hard-coded in the retry status description. For localization and consistency, move these literals into strings.xml and reference them with string resources.

Copilot uses AI. Check for mistakes.

* Generate a new packet ID for retry attempts
*/
private fun generatePacketId(): Int {
return Random().nextInt()
Copy link
Preview

Copilot AI Jun 8, 2025

Choose a reason for hiding this comment

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

Random().nextInt() can generate negative values, which may produce invalid packet IDs. Consider bounding the result (e.g., abs(random.nextInt())) or using a positive-range generator to ensure packet IDs are always non-negative.

Suggested change
return Random().nextInt()
return Math.abs(Random().nextInt())

Copilot uses AI. Check for mistakes.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead of using Random entirely - maybe use a UUID

Copy link
Collaborator

@jamesarich jamesarich left a comment

Choose a reason for hiding this comment

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

We definitely don't want to modify the current 18.json db schema.

If you revert the changes to the schema json files (and delete 19) and rebuild - it should just generate a new 19.json schema with the migragtions needed for 18-19 and not modify 18.json

*/
fun getRetryStatusDescription(queuedMessage: QueuedMessage, currentTime: Long): String {
if (currentTime - queuedMessage.queuedTime > MAX_MESSAGE_AGE_MILLIS) {
return "Message expired (24h limit reached)"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Yep - let's extract these so they can be localized.

@jamesarich
Copy link
Collaborator

jamesarich commented Jun 8, 2025

I shared your PR with the other contributors on discord and it's generated a lot of excitement, and also some implementation feedback:

  • Instead of a global on/off toggle from the main menu - let's utilize the current retry dialog to allow the user add to the queue.
  • Adjust the inital Retry backoff time - 1 minute seemed like it could be a touch fast depending on the user's LoRa settings and mesh congestion.

@Stephen-S-H
Copy link
Author

Stephen-S-H commented Jun 10, 2025

Thanks for the feedback. I'll clean this up per your suggestions. I originally made this so you needed to hit 'Retry' in the dialog to enter the queue, but it felt like an unnecessary extra step given I know that I always want the queue behaviour. Are you suggesting that the queue is always enabled, and a message enters the queue when you click 'Retry' in the retry dialog? Should I remove the setting from the main menu?

About backoff times. What do you suggest for backoff times? I pulled those out of the air. Presumably if this feature is being utilized the user is not in a congested mesh, but we understandably can't expect everyone to use the feature with consideration of wider impacts. (15m 1h 3h 6h 12h)?

@jamesarich
Copy link
Collaborator

jamesarich commented Jun 10, 2025

Yes, the suggested flow would be the queue is always available, and messages to be added to the queue as an additional option from the retry dialog. (Also removing the main menu setting since it's always "on").

Manual per-message opt-in allows us to mitigate initial impact until we better understand it.

As for the timing, let's roll with it the way it currently is. We can tune it in later iterations.

@Xaositek
Copy link

There is equally inherent 3-attempt logic at the radio level while trying to find a recipient for either direct delivery or relayed DM. I would also ensure we persist this as a DM only feature, not something we have available for channel messages.

@jamesarich
Copy link
Collaborator

hi @Stephen-S-H - any plans to continue work on this?

@Stephen-S-H
Copy link
Author

Stephen-S-H commented Jun 19, 2025 via email

@jamesarich
Copy link
Collaborator

Sounds good, thanks!

@github-actions github-actions bot added the Stale label Jul 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants