View difference between Paste ID: Yn4vemKr and dvHsJZJV
SHOW: | | - or go back to the newest paste.
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-
  func SetRollback()
29+
  SetRollback()
30-
  func IsRolledBack() bool
30+
  IsRolledBack() bool
31
}
32
33
type ContextSpi interface {
34-
  func exit()
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-
  func Run(ctx *Context, args ...interface{}) (results ...interface{})
53+
  Run(ctx *Context, args ...interface{}) (result interface{})
54
}
55-
  // transactional mode that this command should run in
55+
56-
  func Mode() Mode
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-
func (cmd Command*) Execute(parent *Context, args... interface{}) (err error, r ...interface{}) {
62+
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-
  ctx := spiFactory(cmd.Mode(), parent)
72+
73
      err = e.(Error) // re-panic if not a tx error
74
    }
75
  }
76
77
  // establish new, or lookup current, tx context
78-
  results = cmd.Run(ctx, args...)
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
}