go Realize business rollback (defer One of the magic effects )

Recently in use GO Override this group's OSS system , It is necessary to implement the business rollback function ( In a process , First with A Interact , And then I'll talk to you B Interact , If B failed , Rollback and A This is the logic of interaction ). in addition , During initialization, it is often necessary to roll back other modules when any module fails to initialize

Too long to read

defer Is a delay call function , It is called when the function exits , and defer The calling order of functions is also called first and then executed , so defer Its function is more suitable for rollback .

The general idea is to execute an initialization function ok after , use defer Corresponding to its fallback function , In the fallback function, the error Judge , if error Not empty , It indicates that it is an abnormal exit , Go back ; if error It's empty , Is a normal exit function , No rollback is required .( This part of logic is implemented in the closure function )

Here, the initialization of the function is used as demo
package main import "fmt" import "errors" func helloWolrd() (err error) {
rollback :=func(rollback func()) { if err != nil { rollback() } } err = initA()
if err != nil { return err } defer rollback(rollbackInitA) err = initB() if err
!=nil { return err } defer rollback(rollbackInitB) err = initC() if err != nil {
return err } defer rollback(rollbackInitC) return nil } func main(){
helloWolrd() }func initA() (err error) { fmt.Println("init A") return nil } func
initB() (err error) { fmt.Println("init B") return nil } func initC() (err
error) { fmt.Println("init C") return errors.New("roll back test") } func
rollbackInitA() { fmt.Println("rollback Init A") } func rollbackInitB() {
fmt.Println("rollback Init B") } func rollbackInitC() { fmt.Println("rollback
Init C") }
Code interpretation :

* rollback as well as defer
rollback(xx), Yes error Judge , if error Not empty , It indicates that it is an abnormal exit , Go back ; if error It's empty , Is a normal exit function , No rollback is required
* Code details :defer as well as error judge , It's judgment first error, Reuse defer Delayed release .
*
There are limitations to this approach : Depends on the function return value error, You can't modify it ; If the rollback function needs different input parameters , You need to declare more than one rollback:rollback1,rollback2…..
Program output
feiqianyousadeMacBook-Pro:go yousa$ ./hello init A init B init C rollback Init
B rollback Init A
Too long version

Business fallback in the implementation of the time , Let's start with a more rudimentary fallback method , so much trouble

1. Error handling , The initialization fallback function of the initialization module before calling manually in reverse order
package main import "fmt" import "errors" func helloWolrd() (err error) { err
= initA()if err != nil { return err } err = initB() if err != nil {
rollbackInitA()return err } err = initC() if err != nil { rollbackInitB()
rollbackInitA()return err } return nil } //... The others are too long
2.GOTO edition

GOTO This version is more similar C The style of , On the whole, in addition to more jumps , The code is clean and tidy
func helloWolrd() (err error) { err = initA() if err != nil { goto ROLLBACK_A }
err = initB() if err != nil { goto ROLLBACK_B } err = initC() if err != nil {
goto ROLLBACK_C } return nil ROLLBACK_C: rollbackInitB() ROLLBACK_B:
rollbackInitA() ROLLBACK_A: returnerr }
use goto Obvious shortcomings :

Don't use it casually :=, It is recommended that you declare variables at the beginning of a function , Otherwise, it is easy to report errors ”goto xx jumps over declaration of”

For example, change the code to the following :
func helloWolrd() (err error) { err = initA() if err != nil { goto ROLLBACK_A
} err = initB()if err != nil { goto ROLLBACK_B } err = initC() if err != nil {
goto ROLLBACK_C } hello := "hello" fmt.Println(hello) return nil ROLLBACK_C:
rollbackInitB() ROLLBACK_B: rollbackInitA() ROLLBACK_A:return err } // report errors
feiqianyousadeMacBook-Pro:go yousa$ go build hello.go # command-line-arguments
./hello.go:40: goto ROLLBACK_A jumps over declaration of hello at ./hello.go:52
./hello.go:45: goto ROLLBACK_B jumps over declaration of hello at ./hello.go:52
./hello.go:49: goto ROLLBACK_C jumps over declaration of hello at ./hello.go:52
You can also try to define an array of functions , Calling a function in a loop , If there is an error, the rollback function is called in a loop . But I always feel that it is more difficult to realize , Not easy to maintain .

epilogue

This paper mainly introduces how to use it go When implementing business roll back, first start from “ The initialization fallback function of the initialization module before calling manually in reverse order ” reach “goto Jump rollback ” Again “ use defer Call the rollback function ” Three writing methods to realize rollback function

If there is a better way to write, welcome to communicate

Technology
©2019-2020 Toolsou All rights reserved,
Final review of database : Summary of comprehensive application questions use Python Make simple games Laplance operator ( Second derivative ) Convert hard disk to GPT Partition format Python Implementation of Hanoi Tower code about String How to create objects vue3 Learning journey 1—— establish vue3 project java String from back to front _Java String String summary use Python Write a story about plants versus zombies 【 Greedy Algorithm 】 Huffman coding problem