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

Add a reloadable objects registry #8205

Merged
merged 6 commits into from
Sep 6, 2018

Conversation

exekias
Copy link
Contributor

@exekias exekias commented Sep 3, 2018

This change allows us to register reloadable blocks of configurations while instantiating them. Central management will use this registry to retrieve reloadable blocks and handle their configs.

This PR also unifies reload mechanism to be used by central management
@exekias exekias requested a review from ph September 3, 2018 18:13
@exekias exekias added the needs_backport PR is waiting to be backported to other branches. label Sep 3, 2018
@exekias exekias force-pushed the reloadable-registry branch from 0f90819 to 9b8f9a4 Compare September 3, 2018 18:13
register.RLock()
defer register.RUnlock()
return register.confsLists[name]
}
Copy link
Contributor

Choose a reason for hiding this comment

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

@exekias Can it be possible to not make it a package variable and instead contains everything within the type/instance of the type? I don't know all the implementation details but as an information we are trying to remove any package variable and global variable from the core so when we see them we always ask do we need really need them?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's an option, but in practice, that would mean we need to pass an instance of the registry to all the running parts of the beat. Is there any plan to introduce something like pkg/context for this?

Copy link
Contributor

Choose a reason for hiding this comment

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

@exekias we want to introduce this, but we are a bit far from that, I am toying with the idea in beatless.

Let me clarify, when I first read your PR, I thought why not use the global feature registry to hold that and just wrap it in some function calls to do the type assertions, this is what I am doing in the beatless PR.

But after taking a step back I thought it's probably better to have this specific registry which deal with alive config changes. But I am not sure how you want to structure it with the application but I would expect some kind of listener that will trigger events? Maybe a bit of details or pointers of the usage would help me?

With that in mind I think what we can do without introducing a pkg/context is to make sure that the registry you create is instead a type that your Registry variable instantiate, this is what you almost do.

But instead of exposing the internals of the registry in the exposed function like the following you do like the next example which hide the implementation details but provide package accessable function. Will help if you add more logic to the class to be able to unit test it without having side effects on the running code.

func MustRegister(name string, r Reloadable) {
	register.Lock()
	defer register.Unlock()
	if r == nil {
		panic("Got a nil object")
	}
	if _, ok := register.confs[name]; ok {
		panic(fmt.Sprintf("%s configuration is already registered", name))
	}
	register.confs[name] = r
}

var Registry ...

func MustRegister(name string, r Reloadable) {
    err := Registry.Register(name, r)
    if err != nil {
     panic(".... something bad happened report the error.")
    }
}

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 think I got it, will do the changes! Thanks for your input 👍

MustRegister("name", nil)
})

assert.Panics(t, func() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Uh I didn't know that assert!

defer register.Unlock()

if r == nil {
panic("Got a nil object")
Copy link
Contributor

Choose a reason for hiding this comment

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

What about returning an error 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.

All the register calls should happen while initializing the beat, if an error happens here it means we did something really wrong, it would be interesting to know that early, I think we follow this pattern in many other registries?

Copy link
Contributor

Choose a reason for hiding this comment

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

ok, lets follow the common pattern.

Copy link
Contributor

Choose a reason for hiding this comment

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

It's good practice to offer both workflow.

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 added another commit to offer both in a non-global fashion

}

if _, ok := register.confs[name]; ok {
panic(fmt.Sprintf("%s configuration is already registered", name))
Copy link
Contributor

Choose a reason for hiding this comment

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

What about returning an error instead?

defer register.Unlock()

if list == nil {
panic("Got a nil object")
Copy link
Contributor

Choose a reason for hiding this comment

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

See above, same for next panic.

defer r.Unlock()

if r == nil {
return errors.New("Got a nil object")
Copy link
Member

Choose a reason for hiding this comment

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

Lowercase error messages

@exekias exekias force-pushed the reloadable-registry branch from 402ead9 to 0372531 Compare September 5, 2018 15:33
@exekias
Copy link
Contributor Author

exekias commented Sep 5, 2018

Thanks everyone for the reviews, I think this is ready for a second look!

"github.com/stretchr/testify/assert"
)

type nonreloadable struct{}
Copy link
Member

Choose a reason for hiding this comment

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

Not used?

func newRegistry() *registry {
return &registry{
confsLists: make(map[string]ReloadableList),
confs: make(map[string]Reloadable),
Copy link
Member

Choose a reason for hiding this comment

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

Btw, is there a reason to have two interfaces, one for a single entity and another one for lists of entities?
Methods could accept config ...*ConfigWithMeta and single entities could be stored as lists of one element.
Also, is it expected to have entities and list of entities with the same name?

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 don't expect entities sharing the name, these should map to a config key, like metricbeat.modules or output.

The problem with having a single definition is that then, the objects have to handle possible errors in the Reload call. Like for instance, having the output object receiving a list of configs. This split is semantic, as the two entities behave differently.

Copy link
Contributor

Choose a reason for hiding this comment

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

The suggestion from @jsoriano has some values, because currently we could call Register and RegisterList with the same name (same map key). Having a single interface for both concept would mean that we only have one map.

@exekias exekias merged commit 4d6d1eb into elastic:master Sep 6, 2018
exekias added a commit to exekias/beats that referenced this pull request Sep 6, 2018
* Add a reloadable objects registry

(cherry picked from commit 4d6d1eb)
@exekias exekias added v6.5.0 and removed needs_backport PR is waiting to be backported to other branches. labels Sep 6, 2018
exekias added a commit that referenced this pull request Sep 7, 2018
* Add a reloadable objects registry

(cherry picked from commit 4d6d1eb)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants