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.

Reply via email to