[Zope3-dev] ZopePublication performs auth outside transaction

Phillip J. Eby pje@telecommunity.com
Fri, 25 Apr 2003 18:25:40 -0400


At 05:43 PM 4/25/03 -0400, Shane Hathaway wrote:

>get_transaction() is helpful for multi-threaded applications,

As compared to what?  If specifying a transaction manager is how you open a 
connection, then why would you need get_transaction()?


>but a burden for simple event-driven applications.  It could be a big 
>burden for complex event-driven applications where it's difficult to know 
>the right places to call suspend()/resume().  In a GUI application, for 
>example, there is usually no particular entry point for handling events; 
>event handlers are spread all over the code.  Thus, many event-driven 
>applications should not use get_transaction()--it just adds complexity.
>
>But the lines aren't always so clear.  Some applications will combine the 
>thread model with the event-driven model, especially as people add things 
>to Zope 3.  When event-driven code calls code that assumes threads and 
>uses get_transaction(), the event-driven code should set the current 
>transaction (to match an existing transaction) and clear the current 
>transaction when the call is finished.  In fact, there also ought to be 
>something like "set_no_transaction()" that causes get_transaction() to fail.

Note that all this would be avoided by putting the knowledge of the 
transaction in the connection.  Then, event driven code working with 
persistent objects from a given connection has a well-defined association 
with a transaction -- regardless of what thread the GUI event runs in!


>You could implement what I just described using resume() to set the 
>current transaction and suspend() to clear it.  That's not a very obvious 
>interface, but it would work.
>
>FWIW, here's an unbaked example of a simple GUI class that uses transactions:
>
>
>class MyDialog(Dialog):
>
>     def __init__(self, db):
>         self.conn = db.open()
>         self.conn.setLocalTransaction()
>         self.data = conn['MyApp'].dialog_data
>
>     def onClickedChoice1RadioButton(self, event):
>         self.data.choice1 = event.button.getValue()
>
>     def onClickedChoice2Checkbox(self, event):
>         self.data.choice2 = event.button.getValue()
>
>     def onClickedOk(self, event):
>         self.conn.getTransaction().commit()
>         self.close()
>
>     def onClickedCancel(self, event):
>         self.conn.getTransaction().abort()
>         self.close()
>
>
>If this had to use get_transaction(), each of the 4 event handlers would 
>have to resume() and suspend() to avoid getting mixed up with simultaneous 
>dialogs and/or windows.  The GUI framework might provide a way to invoke 
>some code before and after all event handlers, but it might not, and it's 
>cleaner to just not have to do that.

What I'm proposing is that (in effect), the 'db.open()' above would require 
a transaction as an input parameter.  So you'd spell it:

     self.conn = db.open(TransactionService())

or something like that, if you wanted to create a new transaction and 
didn't need to keep a reference to it.  (I like the 'conn.getTransaction()' 
method; it makes sense as a way of passing fewer objects around.)