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

Create Generic Transactions to Bundle Async Operations #257

Closed
vijayee opened this issue May 31, 2017 · 6 comments
Closed

Create Generic Transactions to Bundle Async Operations #257

vijayee opened this issue May 31, 2017 · 6 comments
Assignees
Labels
App:Frontend Kind:Core Anything that changes or affects the fundamental core data structures & design of the application. Kind:Enhancement Improvements, new features, performance upgrades, etc. Level:Advanced

Comments

@vijayee
Copy link
Contributor

vijayee commented May 31, 2017

Problem

When async calls fail the leave the application in an invalid state

Solutions

Create a Generic Transaction object that can be use to bundle async operations together in a way that can be resumed when an error occurs. If an error occurs the failure should save the transaction state in the database to be resumed later.

@vijayee vijayee added Kind:Enhancement Improvements, new features, performance upgrades, etc. Level:Advanced labels May 31, 2017
@taoeffect
Copy link
Member

taoeffect commented May 31, 2017

Notes:

  • this might not need to necessarily be an "object", but could just be function calls like "beginAsyncTransactions" / "revertAsyncTransactions", etc.
  • it's fairly easy to save the vuex state, but other state that isn't part of the vuex state should be carefully and systematically handled too, this includes: socket connections, pub/sub subscriptions, and individual component data/state

In the meantime, please add // TODO:s in appropriate places in the code where this needs to be done, referencing this issue.

Edit: also useful: https://www.cs.utexas.edu/~lorenzo/papers/SurveyFinal.pdf

@taoeffect taoeffect added App:Frontend Kind:Core Anything that changes or affects the fundamental core data structures & design of the application. labels May 31, 2017
@taoeffect
Copy link
Member

Also note this goes hand-in-hand with #175 — Error notification system.

@vijayee vijayee mentioned this issue May 31, 2017
@taoeffect
Copy link
Member

taoeffect commented Jun 13, 2017

OK, so basic psuedocode + notes from the call on how this is done:

Pseudocode

In the .vue files, for every "submit"-like action that has a series of asynchronous call steps and publishLogEntry steps, we do the following:

  1. We split up the async calls and the publishLogEntry stuff into two sections, with the async calls being done first, and the publishLogEntries being done only if all the async calls succeeded.
  2. For the async calls: before doing any of the async calls, we create a saved snapshot of the vuex state. If any of the async calls fails: we revert the app to that saved snapshot, we display an error message to the user, and we do not proceed to the publishLogEntry section.
  3. If all the async calls succeeded, we build up a series of serialized actions that are to be added to a queue, as well as the names of any "services" that they might need to create the Events/Entries to be published. This "saving of service names" part is called "dependency injection", and we do it because, for example, we might need the latest hash of the group log before we send any message, and that might change if something failed on our end, and someone added something to the log in the meantime. So, all of these actions, corresponding data, and corresponding services, are placed into an array that will be added to the queue. For other information that's stored with these actions, see notes below.
  4. Add this array of actions to the queue.
  5. The queue itself then goes through the actions, and it, and only it, calls publishLogEntry. No place else in the code should call publishLogEntry (as far as I can tell)

Notes

  • Each action that's added to the queue will probably be an object, that besides containing the data and services needed for creating that event, could have a key containing information about various parts of the UI that are disabled until this action/event is successfully sent out to the server.
  • In order to make the "revert vuex state" stuff work correctly, we need to tie the Pubsub to the vuex state by having the Pubsub maintain a list of websockets it has open, and have the vuex store also contain this same list in it under a key called "pubsub". Then have the Pubsub watch the vuex store for any modifications to that key. For example, if the vuex "pubsub" key has 1 item in it that the Pubsub list does not, then the Pubsub takes that to mean that it should open a websocket connection. If the vuex list does not have an item that the Pubsub does have, then the Pubsub takes that to mean it should unsubscribe/close the websocket.

Examples

Examples of sections that would be converted to using this method include:

@taoeffect
Copy link
Member

Per today's convo, it turns out the that handling the first type of "revertable transactions" (i.e. the "asynchronous call step" part) is not necessary, and in fact trying to save+revert state would break things in the event that a handleEvent modified the state in between.

So it makes sense just do nothing about that, and the async backend.subscribe calls are instead being handled automatically by keeping that pubsub system in sync with the vuex store (issue #298).

Therefore this issue and any related PRs is just about the 2nd type of transactions, the ones that modify external state / the log (i.e. the ones calling publishLogEntry). Nothing should be done regarding the first type (the local async actions).

@taoeffect
Copy link
Member

taoeffect commented Sep 10, 2017

  1. Add this array of actions to the queue.

Hmm, we should make sure to first serialize this array to disk before doing anything else, and to do so in a manner that guarantees that all the steps are serialized correctly (and not say, 2 out of 3 steps before data corruption caused by power failure).

To do that we can hash the steps together in a json blob and save them to the database along with the hash of the blob. If upon deserialization the hash doesn't match the blob, then we know something got corrupted and the entire series of steps should be tossed out.

Also:

Each action that's added to the queue will probably be an object, that besides containing the data and services needed for creating that event, could have a key containing information about various parts of the UI that are disabled until this action/event is successfully sent out to the server.

PR #320 needs to not forget about handling that part ☝️

See especially this review comment

@taoeffect taoeffect changed the title Create Generic Transactions to Bundle Aync Operations Create Generic Transactions to Bundle Async Operations Oct 16, 2017
@taoeffect taoeffect self-assigned this May 15, 2018
@taoeffect
Copy link
Member

Think this isn't necessary anymore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
App:Frontend Kind:Core Anything that changes or affects the fundamental core data structures & design of the application. Kind:Enhancement Improvements, new features, performance upgrades, etc. Level:Advanced
Projects
None yet
Development

No branches or pull requests

2 participants