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

feat(state): add core support for Pebble Notices #295

Merged
merged 11 commits into from
Oct 6, 2023

Conversation

benhoyt
Copy link
Contributor

@benhoyt benhoyt commented Sep 5, 2023

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 and State.Notice to fetch and filter existing notices, and State.WaitNotices to wait (or long-poll) for notices as they occur.

The follow-on changes are in separate PRs for easier review:

internals/overlord/state/notices.go Show resolved Hide resolved
internals/overlord/state/notices.go Show resolved Hide resolved
internals/overlord/state/notices.go Show resolved Hide resolved
internals/overlord/state/notices.go Outdated Show resolved Hide resolved
Copy link
Member

@hpidcock hpidcock left a 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.

internals/overlord/state/export_test.go Outdated Show resolved Hide resolved
internals/overlord/state/notices.go Show resolved Hide resolved
internals/overlord/state/state.go Outdated Show resolved Hide resolved
@jnsgruk jnsgruk added the High Priority Look at me first label Sep 12, 2023
@flotter flotter self-requested a review September 12, 2023 18:36
Copy link
Contributor

@flotter flotter left a 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.

internals/overlord/state/notices.go Outdated Show resolved Hide resolved
internals/overlord/state/notices.go Outdated Show resolved Hide resolved
internals/overlord/state/notices.go Outdated Show resolved Hide resolved
internals/overlord/state/notices.go Outdated Show resolved Hide resolved
internals/overlord/state/notices.go Outdated Show resolved Hide resolved
internals/overlord/state/notices.go Show resolved Hide resolved
internals/overlord/state/notices.go Outdated Show resolved Hide resolved
internals/overlord/state/notices.go Show resolved Hide resolved
internals/overlord/state/notices.go Outdated Show resolved Hide resolved
internals/overlord/state/state.go Outdated Show resolved Hide resolved
Copy link
Contributor

@niemeyer niemeyer left a 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 Show resolved Hide resolved
internals/overlord/state/notices.go Outdated Show resolved Hide resolved
internals/overlord/state/notices.go Outdated Show resolved Hide resolved
internals/overlord/state/notices.go Show resolved Hide resolved
internals/overlord/state/notices.go Outdated Show resolved Hide resolved
internals/overlord/state/notices.go Show resolved Hide resolved
internals/overlord/state/notices.go Show resolved Hide resolved
internals/overlord/state/notices.go Outdated Show resolved Hide resolved
internals/overlord/state/notices.go Show resolved Hide resolved
// and sends to any that match the filters.
func (s *State) processNoticeWaiters() {
for waiterId, waiter := range s.noticeWaiters {
notices := s.Notices(waiter.filters)
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Member

@hpidcock hpidcock left a 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. 👍

@pedronis
Copy link

pedronis commented Oct 6, 2023

Recording change-update notices whenever a change is spawned/updated - TODO

snapd has already grown some internal infrastructure in this area, maybe you can sync about this with @Meulengracht

@jnsgruk jnsgruk requested a review from niemeyer October 6, 2023 09:28
@pedronis
Copy link

pedronis commented Oct 6, 2023

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

Copy link
Contributor

@niemeyer niemeyer left a 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.

internals/overlord/state/notices.go Show resolved Hide resolved
return nil, ctxErr
}

// Otherwise check if there are now matching notices.
Copy link
Contributor

Choose a reason for hiding this comment

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

s/now/new/?

Copy link
Contributor Author

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
Copy link
Contributor

Choose a reason for hiding this comment

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

That's handy.

internals/overlord/state/notices.go Show resolved Hide resolved
@niemeyer niemeyer merged commit d3a4c78 into canonical:master Oct 6, 2023
@benhoyt benhoyt deleted the notices-state branch October 6, 2023 19:29
olivercalder pushed a commit to olivercalder/snapd that referenced this pull request Oct 12, 2023
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]>
olivercalder pushed a commit to olivercalder/snapd that referenced this pull request Oct 19, 2023
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]>
ernestl pushed a commit to canonical/snapd that referenced this pull request Oct 31, 2023
* 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]>
fredldotme pushed a commit to fredldotme/snapd that referenced this pull request Mar 2, 2024
* 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]>
fredldotme pushed a commit to fredldotme/snapd that referenced this pull request Jun 4, 2024
* 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]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
High Priority Look at me first
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants