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,
Error summary -myBatis plus paging use easyPOI Import Excel data In the problem of target detection “ recall Recall”,“ Accuracy Precision”vue use vue-clipboard2 Realize the function of copy link C In language switch sentence Wechat applet (uni-app)url Parameter transfer object hive compress &&hdfs Merge small files hive Summary of processing methods for a large number of small files use spring Of AntPathMatcher matching url route Linux Page replacement algorithm C Language implementation