On Sat, Jan 21, 2012 at 17:08, AndyD <[email protected]> wrote: > Yes, I see what you're saying. But in looking at both your and Robert's > approaches, I still have questions. > > Jeff, how does your approach avoid executing the operation twice? Let's say > there's a web page with a submit button that triggers the process, and the > user clicks that button twice. The algorithm you outlined... > > Create TxnID entity instance > Repeat until success { > * start txn > * load TxnID (if null, return success immediately) > * debit $5 from AccountA > * credit $5 to AccountB > * delete TxnID > * commit txn > } > > ...if run serially, would do the operation twice, wouldn't it? The first > execution would create the TxnID entity, then in the loop, it would load > that entity successfully, perform the operation, then delete the TxnID > entity. Then, when the 2nd button click is handled, the operation would be > be performed again. > > Robert, I didn't quite follow your algorithm. Does your marker indicate > that the operation has not yet been performed? If so, shouldn't 2b say "if > the marker is not found, abort? Also, is there an implied retry mechanism > (e.g. a task) encompassing step 2?
So my approach is effectively what Jeff had outlined, the difference is that I use the existence of a marker to indicate that the transaction has been completed. There may or may not be an implied retry around step 2, but yes -- I generally run things like this in a task. > > It seems to me there are multiple challenges: > 1) eliminate the potential of concurrency > 2) retry on failure > 3) only do the operation once > > If we don't eliminate concurrency, then two parallel threads, in the same or > different instances, could find the operation not yet completed and both > perform it. If we don't indicate (by the presence or absence of a marker) > that the operation has been completed, then two serial executions would both > perform it. And if we don't have a retry mechanism, there is a risk that > the operation won't be performed at all. > > It still seems to me that the task queue approach addresses 1 (by not > dequeing a task to two handlers simultaneously and also not accepting a > duplicate task into the queue) and 2 (by automatically retrying on failure), > but as Jeff pointed out, not 3. If you add an "operation not performed yet" > marker entity to the picture that gets deleted in the same transaction as > the run-once operation, does that cover all the bases? There is a vey important note here. Tasks *do* occasionally run multiple times. You should never assume that a task will be run exactly once, rather the opposite; assume your task will be run twice, either simultaneously or serially, which ever is worse for your app. Robert > > Just to make it interesting, what if the operation to be performed once > was not a datastore operation? What if it was a call to an external system > (like a credit card processing service)? You could successfully make the > external call, but fail to write the "operation complete" entity. > > -Andy > > -- > You received this message because you are subscribed to the Google Groups > "Google App Engine" group. > To view this discussion on the web visit > https://groups.google.com/d/msg/google-appengine/-/YU3N7fnt8rAJ. > > To post to this group, send email to [email protected]. > To unsubscribe from this group, send email to > [email protected]. > For more options, visit this group at > http://groups.google.com/group/google-appengine?hl=en. -- You received this message because you are subscribed to the Google Groups "Google App Engine" group. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/google-appengine?hl=en.
