background

Hello everybody, Xiao Matsuko, come back again. I have been busy with work recently and haven’t updated my article for a long time. I will update it later. Today, I would like to share with you a daily development is more prone to error, that is, contxt misuse caused by the bug, I myself because of misuse caused asynchronous update cache failed, what is the cause? Looking at an example like this, can you see any bugs just by looking at the code?

func AsyncAdd(run func(a) error)  {
	//TODO:Throw it into the asynchronous coroutine pool
	go run()
}

func GetInstance(ctx context.Context,id uint64) (string, error) {
	data,err := GetFromRedis(ctx,id)
	iferr ! =nil&& err ! = redis.Nil{return "", err
	}
	// No data found
	if err == redis.Nil {
		data,err = GetFromDB(ctx,id)
		iferr ! =nil{
			return "", err
		}
		AsyncAdd(func(a) error{
			return UpdateCache(ctx,id,data)
		})
	}
	return data,nil
}

func GetFromRedis(ctx context.Context,id uint64) (string,error) {
	// TODO:Get information from Redis
	return "".nil
}

func GetFromDB(ctx context.Context,id uint64) (string,error) {
	// TODO:Get information from DB
	return "".nil
}

func UpdateCache(ctx context.Context,id interface{},data string) error {
	// TODO: Updates the cache information
	return nil
}

func main(a)  {
	ctx,cancel := context.WithTimeout(context.Background(), 3 * time.Second)
	defer cancel()
	_,err := GetInstance(ctx,2021)
	iferr ! =nil{
		return}}Copy the code

Analysis of the

Let’s take a quick look at what this piece of code does. If we don’t get the information from the cache, we get it from DB. When we get the information from DB, we update the cache in the coroutine pool asynchronously. Isn’t the design perfect, but in practice asynchronous cache updates never work?

The reason for the failure lies in this code:

	AsyncAdd(func(a) error{
			return UpdateCache(ctx,id,data)
		})
Copy the code

The only reason for the error is this CTX, if changed to this, nothing will happen.

AsyncAdd(func(a) error{
			ctxAsync,cancel := context.WithTimeout(context.Background(),3 * time.Second)
			defer cancel()
			return UpdateCache(ctxAsync,id,data)
		})
Copy the code

Now, you already know why, right?

In this CTX tree, cancel() occurs at the root node, which synchronizes the signal to the lower level immediately. Since the CTX of the asynchronous task is also on the node of the tree, when the main Goroutine cancels the CTX, the asynchronous task is also cancelled, causing the cache update to fail.

Because I wrote a detailed description of the Context package before, this is enough!! The article, is not here to elaborate on its principle, want to know how to realize its internal, look before this article can. Here we share with you the principles of using context to avoid stomping holes.

  • Context.Background applies only to the highest level and is the root of all derived contexts.
  • Context cancellation is recommended, and these functions may take some time to clean up and exit.
  • Don’t put theContextPut it in a structure and pass it as a parameter.
  • In order toContextFunction methods as arguments should be calledContextAs the first argument, put first.
  • When you pass Context to a function method, don’t pass nil, if you don’t know what to pass, use context.todo
  • The Context’s value-related methods should pass the necessary data, not everything. Context. Value should be used sparingly, and it should not be used to pass optional arguments. This makes the API implicit and error-inducing. Instead, these values should be passed as parameters.
  • Context is thread-safe and can be passed safely across multiple Goroutines. The same Context can be passed to multiple Goroutines using it, and the Context can be securely accessed by multiple Goroutines simultaneously.
  • The Context structure has no cancellation method, because only functions that derive the Context should cancel the Context.

Context is used in the Go language to synchronize cancellation signals in the tree consisting of multiple Goroutines to reduce resource consumption and consumption. Although it also has the function of passing values, this function is rarely used. Context.Context is a very poor design for passing all parameters of a request. The most common use scenario is to pass the authentication token of the user corresponding to the request and the request ID for distributed tracing.

conclusion

The purpose of writing this article is to share the bugs I write daily to prevent posterity from stepping on the pit. Don’t step on the pit you have already stepped on, save the time to find bugs, learn more about other knowledge, he is not sweet ~.

Well, that’s all for this article, the three qualities (share, like, read) are the author’s motivation to continue to create more quality content!

We have created a Golang learning and communication group. Welcome to join the group and we will learn and communicate together. Join the group: add me vX pull you into the group, or the public number to get into the group two-dimensional code

At the end, I will send you a small welfare. Recently, I was reading the book [micro-service architecture design mode], which is very good. I also collected a PDF, which can be downloaded by myself if you need it. Access: Follow the public account: [Golang Dreamworks], background reply: [micro service], can be obtained.

I have translated a GIN Chinese document, which will be maintained regularly. If you need it, you can download it by replying to [GIN] in the background.

Translated a Machinery Chinese document, will be regularly maintained, there is a need for friends to respond to the background [Machinery] can be obtained.

I am Asong, an ordinary programming ape. Let’s get stronger together. We welcome your attention, and we’ll see you next time

Recommended previous articles:

  • Unsafe package
  • Source analysis panic and recover, do not understand you hit me!
  • Atomic Operations: The basics of concurrent programming
  • Detail the implementation mechanism of defer
  • The scene of large face blows caused by empty structures
  • Leaf-segment Distributed ID Generation System (Golang implementation version)
  • 10 GIFs to help you understand sorting algorithms (with go implementation code)
  • Go parameter transfer type
  • Teach my sister how to write message queues
  • Cache avalanche, cache penetration, cache breakdown
  • Context package, read this article enough!!
  • Traffic Limiting Strategies for High Concurrency systems: Leaky buckets and token buckets
  • Interviewer: Have you used for-range in go? Can you explain the reasons for these problems