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

Ensure that editing fetched Attributes is race-free in any case. #1033

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

AndreasBergmeier6176
Copy link
Contributor

No description provided.

@AndreasBergmeier6176
Copy link
Contributor Author

Fixes #1032

Signed-off-by: Andreas Bergmeier <andreas.bergmeier@otto.de>
@embano1
Copy link
Member

embano1 commented Apr 6, 2024

Thx for the PR! Definitely a possibility for races there. However, your code is technically still not race free because during your copy operation of the map, the map could still be modified. IMHO the best option here would be to add a comment on the functions that the passed map is not supposed to be modified concurrently/not protected during concurrent operations. So callers can pass a copy of the map before using WithCustomAttributes. A related question: do you see the sdk-go code concurrently using and modifying this map in our code base today? Or perhaps done so in the pubsub SDK where these attributes are used? Then we definitely need to fix that on our end to protect the map.

@AndreasBergmeier6176
Copy link
Contributor Author

Thx for the PR! Definitely a possibility for races there. However, your code is technically still not race free because during your copy operation of the map, the map could still be modified.

FWIU, that would then be a race problem introduced by the call site - AKA not our problem.

IMHO the best option here would be to add a comment on the functions that the passed map is not supposed to be modified concurrently/not protected during concurrent operations. So callers can pass a copy of the map before using WithCustomAttributes.

Well I think that is common sense: "As long as a call is processing, don't modify the arguments".

And actually this PR is necessary because the implementation violated that common sense by storing the map (pointer).
Thus this PR is restoring the common sense usage of the functions.

A related question: do you see the sdk-go code concurrently using and modifying this map in our code base today?

Nope. Then again it is perhaps not by the lack of sdk-go code trying. I mean there is a lot of modifying maps between functions and ownership/concurrency was not very clear to me on first glance.

cp[k] = v
}
attrs = cp
}
return context.WithValue(ctx, withCustomAttributes{}, attrs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: how about making this a no-op instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I am not following. No-op instead of what?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, right now this code means the context key withCustomAttributes{} is assigned nil if attrs is nil. So I was wondering why not just return without modifying the context instead of setting it to nil (you set the value from attrs which in nil case is in fact nil).

Would still work with your code above, just save a context operation.

return binding.GetOrDefaultFromCtx(ctx, withCustomAttributes{}, make(map[string]string)).(map[string]string)
ctxVal := binding.GetOrDefaultFromCtx(ctx, withCustomAttributes{}, nil)
if ctxVal == nil {
return make(map[string]string, 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: is it also ok to return nil instead of allocating? Not sure how PubSub handles nil maps though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not because the result immediately gets later assigned to Attributes and then the rest of the sdk code just assumes there is a map there.

cp[k] = v
}
attrs = cp
}
return context.WithValue(ctx, withCustomAttributes{}, attrs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, right now this code means the context key withCustomAttributes{} is assigned nil if attrs is nil. So I was wondering why not just return without modifying the context instead of setting it to nil (you set the value from attrs which in nil case is in fact nil).

Would still work with your code above, just save a context operation.

cp[k] = v
}
attrs = cp
}
return context.WithValue(ctx, withCustomAttributes{}, attrs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return context.WithValue(ctx, withCustomAttributes{}, attrs)
return

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, right now this code means the context key withCustomAttributes{} is assigned nil if attrs is nil. So I was wondering why not just return without modifying the context instead of setting it to nil (you set the value from attrs which in nil case is in fact nil).

This then removes the ability to reset the Attributes saved on the Context. Might not be necessary, IDK.
That being said IF we should do that we def. need to document it because it no longer is a naive/straight-forward implementation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point. However, resetting might not be needed since this is all using context logic which could easily be done by passing a new/fresh (root) context without this key. So I wonder whether we really need the possibility to reset the key? Especially since nil is not really valid as attributes, which you work around in your other function.

We could document that passing nil as attributes is a no-op and does not modify the context?
Again, only unless nil is an explicitly allowed/useful value for PubSub?

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 this pull request may close these issues.

None yet

2 participants