Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package tx
- //
- // thoughts on container-managed transactions in google go
- //
- //
- // approach 1: via tx-command pattern. Downside to this (??) is that there is
- // no analog for thread-locals in go, so ctx must be passed around all the time
- // once you cross the first tx barrier
- //
- // Error satisfies the error interface
- type Error string
- func (e Error) Error() string {
- return string(e)
- }
- type Mode int
- const (
- Supports Mode = iota
- Required
- RequiresNew
- Mandatory
- Never
- )
- type Context interface {
- SetRollback()
- IsRolledBack() bool
- }
- type ContextSpi interface {
- exit()
- Context
- }
- // factory method for creating context service provider implementations.
- // implementation should function along the lines of get-or-create-context
- type ContextSpiFactory func(m Mode, parent *Context) (*ContextSpi)
- // current, global context spi
- var spiFactory ContextSpiFactory
- // client method to allow setting global context spi
- func() SetSpiFactory (f ContextSpiFactory) {
- spiFactory = f
- }
- type Command interface {
- // implementation of the command that does work in some tranactional context
- Run(ctx *Context, args ...interface{}) (result interface{})
- }
- // adapter that allows use of ordinary functions in transactional contexts
- type CommandFunc func(ctx *Context, args ...interface{}) (result interface{})
- // allows clients to say things like: tx.Run(nil,tx.Requires,tx.CommandFunc(initializeCacheFromDb))
- func (f CommandFunc) Run(ctx *Context, args... interface{}) (result interface{}) {
- r = f(ctx, args)
- return
- }
- // clients call this method to execute an instance of a Command.
- // if a client has not yet started a tx then ctx = nil is fine, otherwise the
- // client should pass in the current tx context.
- func Execute(parent *Context, m Mode, cmd *Command, args ...interface{}) (err error, r interface{}) {
- defer func() {
- if e = recover(); e != nil {
- // XXX any cleanup should happen here...
- err = e.(Error) // re-panic if not a tx error
- }
- }
- // establish new, or lookup current, tx context
- ctx := spiFactory(m, parent)
- defer func() {
- ctx.exit()
- }
- r = cmd.Run(ctx, args...)
- return
- }
- type AbstractCommand struct {
- mode Mode
- }
- func (cmd *AbstractCommand) Execute(parent *Context, args ...interface{}) (err error, r interface{}) {
- err, r = Execute(parent, cmd.mode, CommandFunc(cmd.Run), args...)
- return
- }
- // basic, mock implementation of a context service provider
- type MockCtx struct {
- ContextSpi
- rolledBack bool
- mode Mode
- parent *MockCtx
- }
- func (ctx *MockCtx) IsRolledBack() (bool) {
- return ctx.rolledBack
- }
- func (ctx *MockCtx) SetRollback() {
- ctx.rolledBack = true
- }
- // factory method for mock context. obviously not implemented fully
- func NewMockCtx(m Mode, p *Context) (*ContextSpi) {
- switch m {
- case Supports:
- case Required:
- case RequiresNew:
- case Mandatory:
- case Never:
- default:
- panic(Error(fmt.Sprintf("tx: unsupported tx-mode ", m)))
- }
- return &MockCtx{mode: m, parent: p}
- }
- // assume that commands are not allowed to throw their own errors across tx
- // boundaries
- func (ctx *MockCtx) exit() {
- if err = recover(); err != nil || ctx.rolledBack {
- // XXX rollback
- }
- else {
- // XXX (2-phase) commit? (if necessary)
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement