-
Notifications
You must be signed in to change notification settings - Fork 57
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
feat(state): add core support for Pebble Notices #295
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks reasonable and inline with what we discussed together.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great piece of work and everything makes perfect sense for me. I have an overall comment about the concept of repeat
, which probably makes up 70% of my comments directly or indirectly. The rest is all super nitty subjective preferences.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Ben, this looks like a great start.
internals/overlord/state/notices.go
Outdated
// and sends to any that match the filters. | ||
func (s *State) processNoticeWaiters() { | ||
for waiterId, waiter := range s.noticeWaiters { | ||
notices := s.Notices(waiter.filters) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the way we process waiters is to, for each waiter, go over the entire list applying the filter, this whole mechanism feels a bit heavy for what it does. We could allow the code in WaitNotices itself to run this, and just have a single sync.Cond
on the state which is broadcast to when notices are added. The cost of the process would be exactly the same, in that each waiter runs once through the list, and this woud save all these intermediate data structures and communication primitives.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually wrote it using sync.Cond
initially, and it was a bit simpler, but unfortunately Cond
with its Wait()
method doesn't support cancellation/timeout. You can fake it by firing up a goroutine and pushing to a channel, but then you'll leak the goroutine if the timeout occurs before the event. So I went with a list of channels so that I can select on them.
Lots more reading here, where Brad Fitzpatrick suggests that Go adds a channel version of Cond.Wait
to allow cancellation / selecting on it. But that hasn't been done or accepted yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please have a look here: https://pkg.go.dev/context#AfterFunc
This seems to solve the problem by allowing cancellation of the context to broadcast on the Cond, so the function can simply Wait on the Cond, and when a notice arrives it calls Broadcast for the waiters to observe as desired.
Probably not as a coincidence, the example there turns out to be about Cond too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting! I hadn't seen context.AfterFunc
before, and I hadn't thought of making the context cancellation do a broadcast. Unfortunately we can't use AfterFunc
directly as it just arrived in Go 1.21 (we're on 1.20), but it's fairly easy to emulate what we need of it with a few lines of code.
Anyway, I've updated the PR to use sync.Cond
(diff here). It's definitely simpler and avoids the tedious data structures and channel management, and will simplify further once we can use context.AfterFunc
directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. Took a while to understand (and I should have read the comments) that the cond is hanging off the state's mutex. 👍
snapd has already grown some internal infrastructure in this area, maybe you can sync about this with @Meulengracht |
This looks aligned to what I would expect, using the sync.Cond doesn't have the issues I was worried about in the draft. I will ask the @Meulengracht to also take a look |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, looks nice indeed. Just a couple of future trivials. Merging now so we can move on.
return nil, ctxErr | ||
} | ||
|
||
// Otherwise check if there are now matching notices. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/now/new/?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually meant "now", as in "after the above", or "at this point in time" ... but either works. I'll leave as is now. :-)
modified: true, | ||
cache: make(map[interface{}]interface{}), | ||
} | ||
st.noticeCond = sync.NewCond(st) // use State.Lock and State.Unlock |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's handy.
This is a port from Pebble into snapd of previous work by Ben Hoyt to provide the core support for Notices. The original PR can be found at: canonical/pebble#295 Signed-off-by: Oliver Calder <[email protected]>
This is a port from Pebble into snapd of previous work by Ben Hoyt to provide the core support for Notices. The original PR can be found at: canonical/pebble#295 Signed-off-by: Oliver Calder <[email protected]>
* overlord: add core support for Notices This is a port from Pebble into snapd of previous work by Ben Hoyt to provide the core support for Notices. The original PR can be found at: canonical/pebble#295 Signed-off-by: Oliver Calder <[email protected]> * overlord: adapt tests to include notices in state Signed-off-by: Oliver Calder <[email protected]> * o/state: remove references to Pebble and the `pebble notices` CLI Signed-off-by: Oliver Calder <[email protected]> * o/state: removed "custom" notice type There is no plan for a notices CLI in snapd, so there is no need to have custom user-defined notices. Signed-off-by: Oliver Calder <[email protected]> * o/state: set `noticeCond` correctly in `ReadState` This corresponds to an upstream fix in pebble: canonical/pebble#320 Signed-off-by: Oliver Calder <[email protected]> --------- Signed-off-by: Oliver Calder <[email protected]> Co-authored-by: Ben Hoyt <[email protected]>
* overlord: add core support for Notices This is a port from Pebble into snapd of previous work by Ben Hoyt to provide the core support for Notices. The original PR can be found at: canonical/pebble#295 Signed-off-by: Oliver Calder <[email protected]> * overlord: adapt tests to include notices in state Signed-off-by: Oliver Calder <[email protected]> * o/state: remove references to Pebble and the `pebble notices` CLI Signed-off-by: Oliver Calder <[email protected]> * o/state: removed "custom" notice type There is no plan for a notices CLI in snapd, so there is no need to have custom user-defined notices. Signed-off-by: Oliver Calder <[email protected]> * o/state: set `noticeCond` correctly in `ReadState` This corresponds to an upstream fix in pebble: canonical/pebble#320 Signed-off-by: Oliver Calder <[email protected]> --------- Signed-off-by: Oliver Calder <[email protected]> Co-authored-by: Ben Hoyt <[email protected]>
* overlord: add core support for Notices This is a port from Pebble into snapd of previous work by Ben Hoyt to provide the core support for Notices. The original PR can be found at: canonical/pebble#295 Signed-off-by: Oliver Calder <[email protected]> * overlord: adapt tests to include notices in state Signed-off-by: Oliver Calder <[email protected]> * o/state: remove references to Pebble and the `pebble notices` CLI Signed-off-by: Oliver Calder <[email protected]> * o/state: removed "custom" notice type There is no plan for a notices CLI in snapd, so there is no need to have custom user-defined notices. Signed-off-by: Oliver Calder <[email protected]> * o/state: set `noticeCond` correctly in `ReadState` This corresponds to an upstream fix in pebble: canonical/pebble#320 Signed-off-by: Oliver Calder <[email protected]> --------- Signed-off-by: Oliver Calder <[email protected]> Co-authored-by: Ben Hoyt <[email protected]>
This implements the core state aspects of Pebble Notices -- spec JU048.
The key user-visible methods are
State.AddNotice
to add a new notices,State.Notices
andState.Notice
to fetch and filter existing notices, andState.WaitNotices
to wait (or long-poll) for notices as they occur.The follow-on changes are in separate PRs for easier review:
/v1/notices
and/v1/notices/{id}
-- feat(daemon): add notices API #296pebble notices
,pebble notice
, andpebble notify
) -- feat(cli): add CLI commands for notices (notices, notice, notify) #298change-update
notices whenever a change is spawned/updated - TODO