-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
Implement prospector reloading #3362
Conversation
24b5023
to
d156ea3
Compare
964a09e
to
1ba7710
Compare
|
||
[source,yaml] | ||
------------------------------------------------------------------------------ | ||
filebeat.reload.modules: |
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.
Hmm, this might be confusing. It's called reload.modules
but reloads prospectors config, not module configs.
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.
It is actually reload.prospectors
not modules. Copy / paste error from metricbeat ...
go func(pr *Prospector) { | ||
defer wg.Done() | ||
logp.Debug("reload", "stopping prospector: %v", pr.ID) | ||
// TODO: Stop prospectors in parallel |
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.
Isn't this already the case since a goroutine is started for each prospector?
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.
yes, the comment was a left over.
configReloads = expvar.NewInt("filebeat.config.reloads") | ||
prospectorStarts = expvar.NewInt("filebeat.config.prospector.starts") | ||
prospectorStops = expvar.NewInt("filebeat.config.prospector.stops") | ||
prospectorRunning = expvar.NewInt("filebeat.config.prospector.running") |
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.
This seems to only count dynamically configured prospectors, right? We should reflect that in the name.
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 changed it to filebeat.config.prospector.dyamic.*
case <-time.After(r.config.Period): | ||
|
||
debugr("Scan for new config files") | ||
configReloads.Add(1) |
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 this is incremented at each period regardless of whether any file has changed, the expvar name might be a little confusing.
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.
It would be easy to just move it after !updated
and then it's more useful, right?
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 moved it after !updated
|
||
path := r.config.Path | ||
if !filepath.IsAbs(path) { | ||
path = filepath.Join(cfgfile.GetPathConfig(), path) |
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.
Better use paths.Resolve(paths.Config, path)
.
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.
Ah, I see that GetPathConfig
, which was recently introduced, duplicates the logic of paths.Resolve
. Was there a reason for that? I'd prefer using paths.Resolve
everywhere.
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.
GetPathConfig
was around for quite some time I think, or at least the logic around it. I was not aware of paths.Resolve
that is why I used GetPathConfig
. I changed it now in the reloaders.
@tsg New version pushed with reviews implemented. |
7122a59
to
0e0bcf0
Compare
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.
LGTM
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.
Should we deprecate config_dir
now that we have this feature?
return nil, errors.New("No prospectors defined. What files do you want me to watch?") | ||
} | ||
|
||
if *once && config.ProspectorReload.Enabled() { | ||
return nil, errors.New("Once and prospector reloading cannot be used 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.
Consider changing s/Once/-once/
and/or swapping it around like "prospector reloading and -once cannot be used 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.
I swapped the two.
if reloaderConfig.Enabled() { | ||
logp.Warn("EXPERIMENTAL feature dynamic configuration reloading is enabled.") | ||
|
||
go func() { |
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.
How does this goroutine stop?
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.
Good you spotted that. It's missing :-( Will add it.
experimental[] | ||
|
||
Reload configuration allows to dynamically reload prospector configuration files. A glob can be defined which should be watched | ||
for prospector configuration changes. New prospectors will started / stopped accordingly. This is especially useful in |
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.
"will be started"
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.
changed
|
||
A path with a glob must be defined on which files should be checked for changes. A period is set on how often | ||
the files are checked for changes. Do not set period below 1s as the modification time of files is often stored in seconds. | ||
Setting it below 1s will have an unnecessary overhead. |
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/will have/will cause/
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.
changed
@andrewkroh I pushed a new commit with the changes. About deprecating I will open a seperate PR to deprecate |
logp.Warn("EXPERIMENTAL feature dynamic configuration reloading is enabled.") | ||
|
||
go func() { | ||
c.reloader = prospector.NewProspectorReloader(reloaderConfig, c.out, r) |
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 don't think anything bad will happen here, but doing the c.reloader
assignment outside of the goroutine would be less prone to race conditions.
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.
agreed, then code can become:
c.reloader = ...
go c.reloader.Run()
@andrewkroh Deprecation of config_dir: #3429 |
f1f94c7
to
8846cca
Compare
logp.Warn("EXPERIMENTAL feature dynamic configuration reloading is enabled.") | ||
|
||
go func() { | ||
c.reloader = prospector.NewProspectorReloader(reloaderConfig, c.out, r) |
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.
agreed, then code can become:
c.reloader = ...
go c.reloader.Run()
|
||
if c.reloader != nil { | ||
c.wg.Add(1) | ||
go c.reloader.Stop() |
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.
hmmm... where do you call c.wg.Done
asyncWaitStop := func(stop func()) {
c.wg.Add(1)
go func() {
defer c.wg.Done()
stop()
}
}
...
for _, p := range c.prospectors {
asyncWaitStop(p.Stop)
}
if c.reloader != nil {
asyncWaitStop(c.reloader.Stop)
}
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.
c.wg.Done is called on line 50. Current implementation works as expected.
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.
oh... I'm not really ok with calling it on line 50. When using waitGroups one must/should ensure Add
is always run before Done
, so the internal semaphore(counter) can not become negative at any time. What if Run
returns early (even if it doesn't). With Done
being called on line 50, let's move the add right before that line.
With Add/Done being somewhat symmetric, usage should clearly follow the simple pattern of: always increase right before starting the go-routine which is going to call Done. This leaves less room for errors and makes usage more clear for readers (we've already got quite a many wait groups and done channels in here).
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.
Agree. Changed.
This PR allows to dynamically reload prospectors. It works the same way as module reloading in metricbeat. **Refactoring** * LoadStates was separated from NewProspector. The reason is that after New only the ID is needed and setting up states requires more calculations. So this can be done in a second step when all the validations are done. * Only allow to start a prospector when all states are set to Finished. If not, LoadStates returns an error. This is to prevent a prospector starting before a harvester finished with a file. The prospector will be picked up again during the next reloading phase. * Extract ReloadConfig to libbeat **Limitations** This implementation currently has the some limitations. This are not new in filebeat but require more care as configurations change more often. * Two prospectors on one file: It is possible, that two prospectors pick up one file because they defined overlapping patterns. This can have the consequence that two harvesters on the same file are running which can lead to duplicates and unpredictable behaviour. The risk is minimized in that a prospector does not start as long as a state it takes care of is not finished. But it can still happen that a Finished state is picked up but it also managed by an other prospector. The user must ensure no prospector paths overlap. This problem can potentially be solved in the future with a global harvester registry. **Notes** * In a later PR, more refactoring and unification of the reloading should happen.
e8ae484
to
88a0ff1
Compare
Thanks so much guys for pushing this PR! Quick question we heavily use "config_dir" option. Does it mean if we have that option enabled reload will not work? |
@superwhykz No, currently config_dir and the reloading can be enabled together, you should just ensure that the directories they are fetching config files from are not overlapping. Let us know if you hit any issues with the reloading. |
@ruflin awesome! this feature should be in night build by now right? |
@superwhykz yes |
The plan is replace this with a better implementation in the future similar to prospector reloading. See elastic#3362.
In what version are you guys targeting this feature to be available? Any idea on a timeframe for that? Thanks! |
@jasonkylemorse It is available in 5.3 as an experimental feature. 5.3 should be coming out in the next weeks. If you want to test it already, you can also use the nightly builds: https://beats-nightlies.s3.amazonaws.com/index.html?prefix=filebeat/ |
This PR allows to dynamically reload prospectors. It works the same way as module reloading in metricbeat.
Refactoring
Limitations
This implementation currently has the some limitations. This are not new in filebeat but require more care as configurations change more often.
Notes