Advertisement
jdefelice

tx-command-api-v2.go

Jul 10th, 2012
281
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. package tx
  2.  
  3. //
  4. // thoughts on container-managed transactions in google go
  5. //
  6.  
  7. //
  8. // approach 1: via tx-command pattern.  Downside to this (??) is that there is
  9. // no analog for thread-locals in go, so ctx must be passed around all the time
  10. // once you cross the first tx barrier
  11. //
  12.  
  13. // Error satisfies the error interface
  14. type Error string
  15. func (e Error) Error() string {
  16.   return string(e)
  17. }
  18.  
  19. type Mode int
  20. const (
  21.     Supports Mode = iota
  22.     Required
  23.     RequiresNew
  24.     Mandatory
  25.     Never
  26. )
  27.  
  28. type Context interface {
  29.   SetRollback()
  30.   IsRolledBack() bool
  31. }
  32.  
  33. type ContextSpi interface {
  34.   exit()
  35.   Context
  36. }
  37.  
  38. // factory method for creating context service provider implementations.
  39. // implementation should function along the lines of get-or-create-context
  40. type ContextSpiFactory func(m Mode, parent *Context) (*ContextSpi)
  41.  
  42. // current, global context spi
  43. var spiFactory ContextSpiFactory
  44.  
  45. // client method to allow setting global context spi
  46. func() SetSpiFactory (f ContextSpiFactory) {
  47.   spiFactory = f
  48. }
  49.  
  50. type Command interface {
  51.  
  52.   // implementation of the command that does work in some tranactional context
  53.   Run(ctx *Context, args ...interface{}) (result interface{})
  54. }
  55.  
  56. // adapter that allows use of ordinary functions in transactional contexts
  57. type CommandFunc func(ctx *Context, args ...interface{}) (result interface{})
  58.  
  59. // allows clients to say things like: tx.Run(nil,tx.Requires,tx.CommandFunc(initializeCacheFromDb))
  60. func (f CommandFunc) Run(ctx *Context, args... interface{}) (result interface{}) {
  61.   r = f(ctx, args)
  62.   return
  63. }
  64.  
  65. // clients call this method to execute an instance of a Command.
  66. // if a client has not yet started a tx then ctx = nil is fine, otherwise the
  67. // client should pass in the current tx context.
  68. func Execute(parent *Context, m Mode, cmd *Command, args ...interface{}) (err error, r interface{}) {
  69.  
  70.   defer func() {
  71.     if e = recover(); e != nil {
  72.       // XXX any cleanup should happen here...
  73.       err = e.(Error) // re-panic if not a tx error
  74.     }
  75.   }
  76.  
  77.   // establish new, or lookup current, tx context
  78.   ctx := spiFactory(m, parent)
  79.  
  80.   defer func() {
  81.     ctx.exit()
  82.   }
  83.  
  84.   r = cmd.Run(ctx, args...)
  85.   return
  86. }
  87.  
  88. type AbstractCommand struct {
  89.   mode Mode
  90. }
  91.  
  92. func (cmd *AbstractCommand) Execute(parent *Context, args ...interface{}) (err error, r interface{}) {
  93.   err, r = Execute(parent, cmd.mode, CommandFunc(cmd.Run), args...)
  94.   return
  95. }
  96.  
  97. // basic, mock implementation of a context service provider
  98. type MockCtx struct {
  99.   ContextSpi
  100.   rolledBack bool
  101.   mode Mode
  102.   parent *MockCtx
  103. }
  104.  
  105. func (ctx *MockCtx) IsRolledBack() (bool) {
  106.   return ctx.rolledBack
  107. }
  108.  
  109. func (ctx *MockCtx) SetRollback() {
  110.   ctx.rolledBack = true
  111. }
  112.  
  113. // factory method for mock context.  obviously not implemented fully
  114. func NewMockCtx(m Mode, p *Context) (*ContextSpi) {
  115.   switch m {
  116.     case Supports:
  117.     case Required:
  118.     case RequiresNew:
  119.     case Mandatory:
  120.     case Never:
  121.     default:
  122.       panic(Error(fmt.Sprintf("tx: unsupported tx-mode ", m)))
  123.   }
  124.   return &MockCtx{mode: m, parent: p}
  125. }
  126.  
  127. // assume that commands are not allowed to throw their own errors across tx
  128. // boundaries
  129. func (ctx *MockCtx) exit() {
  130.   if err = recover(); err != nil || ctx.rolledBack {
  131.     // XXX rollback
  132.   }
  133.   else {
  134.     // XXX (2-phase) commit? (if necessary)
  135.   }
  136. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement