Skip to content
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

Support retriable Chelonia actions #1768

Merged
merged 10 commits into from
Dec 7, 2023

Conversation

snowteamer
Copy link
Collaborator

@snowteamer snowteamer commented Oct 16, 2023

Closes #1755

Summary of changes:

Copy link
Member

@taoeffect taoeffect left a comment

Choose a reason for hiding this comment

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

Preliminary review - looking really solid so far...!

frontend/main.js Outdated Show resolved Hide resolved
shared/domains/chelonia/persistent-actions.js Outdated Show resolved Hide resolved
shared/domains/chelonia/persistent-actions.js Outdated Show resolved Hide resolved
'chelonia.persistentActions/_init' (): void {
sbp('okTurtles.events/on', LOGIN, (function () {
this.actionsByID = Object.create(null)
this.databaseKey = `chelonia/persistentActions/${sbp('state/vuex/getters').ourIdentityContractId}`
Copy link
Member

Choose a reason for hiding this comment

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

Similarly, as this is a utility for Chelonia (and possibly in the future, an independent library), we cannot access 'state/vuex/getters' either

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ditto

Copy link
Member

@taoeffect taoeffect left a comment

Choose a reason for hiding this comment

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

Looking really good @snowteamer!!

I'm wondering if we can also adjust the default options? Perhaps using the existing selector 'chelonia.persistentActions/configure'?

@taoeffect taoeffect requested a review from corrideat October 24, 2023 17:43
frontend/main.js Outdated
const databaseKey = `chelonia/persistentActions/${sbp('state/vuex/getters').ourIdentityContractId}`
sbp('chelonia.persistentActions/configure', { databaseKey })
await sbp('chelonia.persistentActions/load')
await sbp('chelonia.persistentActions/retryAll')
Copy link
Member

Choose a reason for hiding this comment

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

I think 'chelonia.persistentActions/load' should automatically start the retries

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That would be fine, but on the other hand /save doesn't stop the pending retries

Copy link
Member

Choose a reason for hiding this comment

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

It shouldn't. The persistent action queue is something that is always active as long as it has actions inside of it while the app is running.

Copy link
Member

@taoeffect taoeffect left a comment

Choose a reason for hiding this comment

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

I think this is almost ready to merge.

@snowteamer - I think you might have missed this request from the previous review:

I'm wondering if we can also adjust the default options? Perhaps using the existing selector 'chelonia.persistentActions/configure'?

This way we can override the default options for values like maxAttempts and retrySeconds.

Also added one more request about emitting events.

Comment on lines 97 to 98
this.options.successInvocationSelector &&
await this.trySBP([this.options.successInvocationSelector, result])
Copy link
Member

Choose a reason for hiding this comment

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

Can we refactor this to use events instead of options.successInvocationSelector?

I think it makes sense to add at least two events using okTurtles.events:

  • CHEL_PERSISTENT_ACTIONS_SUCCESS - would be emitted above here
  • CHEL_PERSISTENT_ACTIONS_FAILURE - would be emitted next to line 82 above, next to the errorInvocation, along with the error object

And if you think of any others that we should emit, feel free to suggest

@snowteamer snowteamer force-pushed the retriable-chelonia-actions branch from d12f7aa to 6192b3a Compare October 30, 2023 17:11
Copy link
Member

@Silver-IT Silver-IT left a comment

Choose a reason for hiding this comment

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

Really impressive PR, @snowteamer. 👍👍👍
Just left a few comments.

status.lastError = error.message
const anyAttemptLeft = options.maxAttempts > status.failedAttemptsSoFar
if (!anyAttemptLeft) status.resolved = true
status.nextRetry = anyAttemptLeft && !status.resolved
Copy link
Member

Choose a reason for hiding this comment

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

!status.resolved could be ignored, I think.

status.nextRetry = anyAttemptLeft ? new Date(Date.now() + options.retrySeconds * 1e3).toISOString() : ''

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

status.resolved can be set independently by .cancel(), therefore checking for anyAttemptLeft was not enough

shared/domains/chelonia/persistent-actions.js Show resolved Hide resolved
}
},

'chelonia.persistentActions/enqueue' (...args): UUIDV4[] {
Copy link
Member

Choose a reason for hiding this comment

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

I think it's preferable to declare possible types of args as a comment above this function.
BTW: Why is it necessary to accept two types of arg for PersistantAction?

[invocation, options]
{ invocation, ...options }

I think the second format isn't necessary. And also invocation could be added in options of PersistantAction instance.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good point about the args comment.

Why is it necessary to accept two types of arg for PersistantAction?

Here I just followed the issue requirements.

And also invocation could be added in options of PersistantAction instance.

An invocation is required in any case, so I don't think invocation should go in options since it's not actually optional.

Copy link
Member

@taoeffect taoeffect Nov 9, 2023

Choose a reason for hiding this comment

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

Here I just followed the issue requirements.

Oh, it was a good thing @Silver-IT brought this up, because I missed this in my review.

This is not what the args look like according to the issue:

[invocation, options]
{ invocation, ...options }

Rather, they look like this:

[selector, arg1, arg2, arg3]
{ invocation, ...options }

So to answer @Silver-IT's question: the reason for allowing both types is because it makes the syntax when enqueing invocations simpler.

In most cases you can just pass in the sbpInvocation like so: [selector, arg1, arg2, arg3]

But when you want to adjust the defaults for a specific invocation, you use the second syntax.

I'm noticing that this code does not correctly handle the first syntax.

EDIT: oops, my mistake, the code seems to correctly handle this.

Comment on lines +189 to +190
// TODO: find a cleaner alternative.
this.actionsByID[id].id = id
Copy link
Member

Choose a reason for hiding this comment

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

Why not to add id in the arguments of PersistantAction constructor?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's a possible choice, but I felt like it was not that great either

'chelonia.persistentActions/_init' (): void {
this.actionsByID = Object.create(null)
// Necessary for now as _init cannot be async. Becomes true when 'configure' has been called.
this.ready = false
Copy link
Member

Choose a reason for hiding this comment

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

I can't find the place to set this.ready true in configure function. Did you miss it by mistake?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Indeed, good catch. BTW I've since replaced it by a direct check for availability of databaseKey

const anyAttemptLeft = options.maxAttempts > status.failedAttemptsSoFar
if (!anyAttemptLeft) status.resolved = true
status.nextRetry = anyAttemptLeft && !status.resolved
? new Date(Date.now() + options.retrySeconds * 1e3).toISOString()
Copy link
Member

Choose a reason for hiding this comment

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

Using Date is a bad idea for anything besides maybe logging. The reason is that the date could change arbitrarily. The better alternative is to use a monotonic timer for this, such as performance.now(). I'm not sure exactly what this nextRetry field is used for, exactly, though.

The second issue is the, like for the this.timer assignment below, that 1e3 seems like a pretty low value for a retry

Copy link
Collaborator Author

@snowteamer snowteamer Nov 7, 2023

Choose a reason for hiding this comment

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

The nextRetry field is actually used for logging as per the issue requirement for 'chelonia.persistentActions/status'

1e3 seems like a pretty low value for a retry

options.retrySeconds is a number of seconds, but we need milliseconds here, hence the 1e3 multiplier

// Schedule a retry if appropriate.
if (status.nextRetry) {
// Note: there should be no older active timeout to clear.
this.timer = setTimeout(() => this.attempt(), this.options.retrySeconds * 1e3)
Copy link
Member

Choose a reason for hiding this comment

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

1e3 seems like a pretty low value

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ditto

Copy link
Member

@taoeffect taoeffect left a comment

Choose a reason for hiding this comment

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

Nice work @snowteamer. Looks almost ready to merge. Seems like the only thing remaining are the logging out related changes we discussed today.

@snowteamer snowteamer changed the title WIP - Support retriable Chelonia actions Support retriable Chelonia actions Nov 9, 2023
Copy link
Member

@taoeffect taoeffect left a comment

Choose a reason for hiding this comment

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

Great work @snowteamer! Approved & merged! 😄 🎉

@taoeffect taoeffect merged commit bba9016 into okTurtles:master Dec 7, 2023
4 checks passed
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.

Implement a persistent action queue
4 participants