Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AttachContext is not transitive #67

Closed
rantav opened this issue Feb 2, 2020 · 5 comments
Closed

AttachContext is not transitive #67

rantav opened this issue Feb 2, 2020 · 5 comments

Comments

@rantav
Copy link
Contributor

rantav commented Feb 2, 2020

I realized there is another problem with AttachContext and friends.
Attaching a context and then creating a new context and attaching it, makes RequestCtx forget the previous attached context, which to me seems like a breach of the context contract

Example test:

func Test_ValueTransitive(t *testing.T) {
	ctx := acquireRequestCtx(new(fasthttp.RequestCtx))

	ctx2 := context.WithValue(ctx, "k2", "v2")
	ctx.AttachContext(ctx2)
	if v := ctx.Value("k2"); v != "v2" {
		t.Errorf("Value() key '%s' == %v, want %v", "k2", v, "v2")
	}

	ctx3 := context.WithValue(ctx, "k3", "v3")
	ctx.AttachContext(ctx3)
	// Should remember *both* k2 and k3
	if v := ctx.Value("k2"); v != "v2" {
		t.Errorf("Value() key '%s' == %v, want %v", "k2", v, "v2") // THIS FAILS, but should not
	}
	if v := ctx.Value("k3"); v != "v3" {
		t.Errorf("Value() key '%s' == %v, want %v", "k3", v, "v3")
	}
}

I'll send a pull request with fix suggestion soon

rantav added a commit to rantav/atreugo that referenced this issue Feb 2, 2020
rantav added a commit to rantav/atreugo that referenced this issue Feb 2, 2020
@savsgio
Copy link
Owner

savsgio commented Feb 3, 2020

Hi @rantav,

If you create a second context with value, you must use ctx.AttachedCtx() not the RequestCtx again, because fasthttp doesn't support overwrite the original RequestCtx. Atreugo only adds an extra layer to support attached context.

So must do:

// First time
ctx2 := context.WithValue(ctx, "k2", "v2")
ctx.AttachContext(ctx2)

// Second time or more
ctx3 :=  context.WithValue(ctx.AttachedContext(), "k3", "v3")
ctx.AttachContext(ctx3)

@savsgio savsgio closed this as completed Feb 3, 2020
@rantav
Copy link
Contributor Author

rantav commented Feb 4, 2020

Thanks @savsgio I understand your point. Let me try to add another angle if I may.
Two points I think needs to be considered.

  1. It's easy to miss the "use ctx.AttachedContext() instead of ctx" instruction. There's no compiler help and by convention everyone using a context.Context are already wrapping one context inside another so that would be their first instinct, to simply use ctx to attach a new value. So in terms of API ergonomics this could be a potential pitfall.
  2. What if I attach a value to ctx and I also attach another value to actx. Now I want to add a third value, preserving the last two? a) I can't do that and b) there's a surprise element. Example:
ctx := acquireRequestCtx(new(fasthttp.RequestCtx))
ctx.SetUserValue("k1", "v1") // ctx contains k1
ctx2 := context.WithValue(ctx, "k2", "v2")
ctx.AttachContext(ctx2) // ctx contains k1, k2

// I want ctx3 to wrap both k1 and k2, but I can't. 
// Option 1
ctx3 := context.WithValue(ctx, "k3", "v3") // ctx3 contains k3, k1 and k2
// but if we do that: surprise 
ctx.AttachContext(ctx3) // now ctx contains k1, k3 (not k2) 

// Option 2 (instead of 1) 
ctx3 := context.WithValue(ctx2, "k3", "v3") // ctx3 contains k3, k2
// And now, surprise
ctx.AttachContext(ctx3) // now ctx contains k1, k3 (not k2) 

@savsgio
Copy link
Owner

savsgio commented Feb 4, 2020

I'm sorry, but your example is good, the ctx has the 3 keys:

fmt.Println(ctx.Value("k1"), ctx.Value("k2"), ctx.Value("k3"))
# Print: v1 v2 v3

Keep in mind, that you could only attach one context, if you attach another, you override the previous, that's why to use the ctx.AttachedContext() to keep the previously.
About use always the ctx and not the ctx.AttachedContext(), fasthttp and atreugo only implement the interface of context.Context to use as a simple context but not as a context with values, because it's generate some garbage and reduce the performance significally.

Maybe in a future, @erikdubbelboer or @kirillDanshin will implement it, but now, it's not possible.

@rantav
Copy link
Contributor Author

rantav commented Feb 4, 2020

ok, I get it, it's a limitation or "by design" of fasthttp.

@savsgio
Copy link
Owner

savsgio commented Feb 4, 2020

Sorry, use attached context carefully.
I could't say you anything more!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants