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

Refactor Registry internals to make concurrent access safe #1115

Merged
merged 1 commit into from
Apr 13, 2023
Merged

Refactor Registry internals to make concurrent access safe #1115

merged 1 commit into from
Apr 13, 2023

Conversation

zcross
Copy link
Contributor

@zcross zcross commented Feb 24, 2023

Background: #1109

Related PR(s):

This adds simple synchronization (sync.RWMutex) to accesses of the mutable state within Registry.
It also restructures the data to use a map per entity type, rather than a slice, replacing some linear searches of the data.
As recommended by @sunsingerus , I'm starting here with the Registry and planning on moving onto CHI status (and probably the announcer, at least with a slight change to its builder pattern for chaining log line inputs).

For me, the most pressing open question is whether my read of the code using Registry is semi-accurate: it seems that it is used for internal book keeping, and that individual ObjectMeta instances are read in the Walk... functions without direct mutation. As I write that, I'm considering whether that was part of the intent of the original code passing these structs by value rather than by pointer.

Meta: I ran into some issues executing the e2e tests locally (have not fixed yet) and some other simpler (fixed) problems developing this branch on an M1 Mac. I don't want to pull the fixes I made locally into the discussion here, but I won't be shocked if the e2e tests reveal some issue I'm not yet aware of due to this dev environment issue.

Important items to consider before making a Pull Request

Please check items PR complies to:

  • All commits in the PR are squashed. More info
  • The PR is made into dedicated next-release branch, not into master branch1. More info
  • The PR is signed. More info

pkg/model/registry.go Outdated Show resolved Hide resolved
@sunsingerus
Copy link
Collaborator

... Registry ... is used for internal book keeping, and that individual ObjectMeta instances are read ... without direct mutation

Yes, this is correct.

Main idea of the Registry is to keep registry of reconciled objects for two main reasons (at least for now)

  1. To clear (delete) all objects that belong to the CHI but not listed in registry as reconciled - thus removing all remnants of previous iterations of the CHI w/o knowing what they are. Clean all what is not listed in the registry as belonging to current iteration of the CHI
  2. Statistics and counting

Entities inside Registry are not mutated, but are read only, at least for now

@zcross
Copy link
Contributor Author

zcross commented Feb 27, 2023

... Registry ... is used for internal book keeping, and that individual ObjectMeta instances are read ... without direct mutation

Yes, this is correct.

Main idea of the Registry is to keep registry of reconciled objects for two main reasons (at least for now)

1. To clear (delete) all objects that belong to the CHI but not listed in registry as reconciled - thus removing all remnants of previous iterations of the `CHI` w/o knowing what they are. Clean all what is not listed in the registry as belonging to current iteration of the `CHI`

2. Statistics and counting

Entities inside Registry are not mutated, but are read only, at least for now

Perfect, I think that's compatible with the approach taken in this PR in that case.

@zcross
Copy link
Contributor Author

zcross commented Feb 27, 2023

Meta: I understand that the project prefers commits squashed. I tend to incrementally commit to address PR feedback, so that the feedback-inspired changes are visible at the commit level while the PR is under review, and then squash to one commit before merging. Does that work, or do you prefer squashing aggressively during the review process? Thanks!

@zcross
Copy link
Contributor Author

zcross commented Feb 27, 2023

BTW, I see that the project seems to rely on end to end behavioral tests. I was wondering about dropping in some very simple go tests to at least exercise the race detector over simple operations on these types that I'm adding synchronization to. What are your thoughts on that? It seems like it might be the very first such golang test, which gives me some pause as I assume I'd have to make some changes to the CI configuration to ensure that said golang tests get executed. Not trying to add scope creep here, but wanted to at least make explicit that I thought about this and don't mind adding such test coverage if you all are on-board.

Edit: I went ahead and pushed an example of the kind of test I'm describing, in which very basic operations are tested through n>1 (=2) goroutines with some synchronization that ensures they actually begin their workloads in parallel. I didn't do anything to "wire it in" to the repo's CI, but it is at least executable in GoLand (and passing). Hope that is a helpful supplement to my lukewarm proposal to add such a test, but again I don't feel strongly about it if it's way too out of line with the rest of the repo's testing story (in which case, no problem if you all want it dropped from the PR).

@sunsingerus
Copy link
Collaborator

do you prefer squashing aggressively during the review process?

No need to squash immediately, this can be done once at the very end

@sunsingerus
Copy link
Collaborator

... project seems to rely on end to end behavioral tests. ...

The intention is to cover the whole functionality (or at least as much as possible) with e2e tests. Tests are typically added along with new functionality. However, in case any strange / corner case behavior is found later, tests are modified to cover it. Generally speaking, successful run of the test suite gives good hope that the operator can do most of the typical use-case scenarios.

... I was wondering about dropping in some very simple go tests ...

Yes, sure. Tests covering any functionality (new and already existing, and especially complex scenarios) are always welcome.

// NB: These tests mostly exist to exercise synchronization and detect regressions related to them via the
// Golang race detector. See: https://go.dev/blog/race-detector
// In short, add -race to the go test flags when running this.
func Test_Registry_BasicOperations_ConcurrencyTest(t *testing.T) {
Copy link
Contributor Author

@zcross zcross Feb 28, 2023

Choose a reason for hiding this comment

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

@sunsingerus here's the minimal (not assertion-heavy) go test I mentioned. its purpose is to exercise the synchronization primitives and detect any regressions related to concurrency (i.e., via -race)

@zcross
Copy link
Contributor Author

zcross commented Mar 6, 2023

Squashing this as I await feedback and get started on a third that actually touches the reconciliation logic

Background: #1109

Signed-off-by: Zach Cross <zcross@chronosphere.io>
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