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

Use project as targeting key, propagate message context through entity evaluation #3827

Merged
merged 3 commits into from
Jul 15, 2024

Conversation

evankanderson
Copy link
Member

Summary

Update entity evaluation to propagate message context and include data for flag evaluation in the context.

Update flag evaluation context to store user in the same form as project and provider, and make the project ID the primary targeting key for providers that need it (not GoFeatureFlag). Since no one is probably using the current fine-grained targeting, this should be okay.

Change Type

Mark the type of change your PR introduces:

  • Bug fix (resolves an issue without affecting existing features)
  • Feature (adds new functionality without breaking changes)
  • Breaking change (may impact existing functionalities or require documentation updates)
  • Documentation (updates or additions to documentation)
  • Refactoring or test improvements (no bug fixes or new functionality)

Testing

Outline how the changes were tested, including steps to reproduce and any relevant configurations.
Attach screenshots if helpful.

Review Checklist:

  • Reviewed my own code for quality and clarity.
  • Added comments to complex or tricky code sections.
  • Updated any affected documentation.
  • Included tests that validate the fix or feature.
  • Checked that related changes are merged.

@evankanderson evankanderson requested a review from a team as a code owner July 10, 2024 07:59
@evankanderson evankanderson requested a review from dmjb July 10, 2024 08:00
@coveralls
Copy link

coveralls commented Jul 10, 2024

Coverage Status

coverage: 52.839% (+0.6%) from 52.201%
when pulling b4a4fe0 on evankanderson:message-eval-context
into bef2527 on stacklok:main.

Comment on lines 180 to 182
e.cancels = slices.DeleteFunc(e.cancels, func(cf *context.CancelFunc) bool {
return cf == &shutdownCancel
})
Copy link
Contributor

Choose a reason for hiding this comment

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

Since we're appending a new cancelation function on every call to the handler at line 110, should we call this logic to drop shutdownCancel from the e.cancels slice before the return on line 171 too?

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh yeah, this should be a deferred function. Thanks!

Copy link
Member Author

Choose a reason for hiding this comment

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

(also line 143... eek)

Copy link
Contributor

Choose a reason for hiding this comment

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

Awesome, thank you :)

map[string]interface{}{
"project": ec.Project.ID.String(),
"project": ec.Project.ID.String(),
// TODO: is this useful, given how provider names are used?
Copy link
Contributor

Choose a reason for hiding this comment

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

IMHO we should consider removing it, but I don't have strong feelings about it right now.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think you're probably right.

defer cancel()
ctx = engcontext.WithEntityContext(ctx, &engcontext.EntityContext{
Project: engcontext.Project{ID: inf.ProjectID},
// TODO: extract Provider name from ProviderID
Copy link
Contributor

@dmjb dmjb Jul 11, 2024

Choose a reason for hiding this comment

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

If we decide to retain the provider as a feature flag attribute, we should consider using the provider ID instead of the name.

Copy link
Member Author

Choose a reason for hiding this comment

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

Alternatively, we should use the provider class, rather than a particular provider ID. (i.e. "enable this feature for users with the foo provider") It still feels like a stretch, though.

I think I'll just remove this.

@dmjb
Copy link
Contributor

dmjb commented Jul 11, 2024

Aside from the issue which @puerco raised, this looks good to me.

Comment on lines +70 to +78
go func() {
<-ctx.Done()
eh.lock.Lock()
defer eh.lock.Unlock()

for _, cancel := range eh.cancels {
(*cancel)()
}
}()
Copy link
Contributor

Choose a reason for hiding this comment

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

If I got this right, you're still waiting for Done() on what was originally the terminationcontext, but keep track of all downstream contexts to cancel them as well, right?

If yes, was this source of some sort of bug?

Copy link
Member Author

Choose a reason for hiding this comment

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

What I really want is Context.TerminateOnAdditionalContext(ctx, additional). But that doesn't exist.

The problem is that handleEntityEvent is (for performance) extracting the event processing out of the watermill message lifecycle, but we still want to be able to cancel message processing during process shutdown.

The previous way we were doing this was to basically take the background context of the server and sub it in for the message context, but that means that we're losing all the message context in the event handler. The new pattern is to use Context.WithoutCancel to escape from the cancellation of the Watermill event handler (because it only allows one message in flight), but then store the cancels for this loop.

If we didn't have the performance problem with Watermill, we still might want to be able to early-terminate the in-flight event handling when the server shuts down, but we'd do that by passing the termination context to Watermill and have it cancel all the contexts. But when we call WithoutCancel, we end up putting that work on ourselves.

(I'm also back-intuiting some of this from #1654)

Comment on lines 107 to 111
{
e.lock.Lock()
defer e.lock.Unlock()
e.cancels = append(e.cancels, &shutdownCancel)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

praise: TIL that you can introduce arbitrary lexical scopes in Go as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

Same, when I saw it I actually ran a small code snip to make sure I was understanding :)

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually, I think I screwed this up -- defer is at the function scope level. I thought I had a test covering this, but clearly I don't.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ha, I avoided screwing this up by accident! It turns out that the other lock interaction was inside the goroutine, so this one sort of speeds through and doesn't run into the other lock anyway. But I switched things up to reduce the scope of the lock by explicitly calling unlock on the following line.

@evankanderson evankanderson merged commit f3fbc2d into mindersec:main Jul 15, 2024
21 checks passed
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.

5 participants