151 lines
4.8 KiB
Go
151 lines
4.8 KiB
Go
// Licensed to Elasticsearch B.V. under one or more contributor
|
|
// license agreements. See the NOTICE file distributed with
|
|
// this work for additional information regarding copyright
|
|
// ownership. Elasticsearch B.V. licenses this file to you under
|
|
// the Apache License, Version 2.0 (the "License"); you may
|
|
// not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing,
|
|
// software distributed under the License is distributed on an
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations
|
|
// under the License.
|
|
|
|
package eventlog
|
|
|
|
// This component of the eventlog package provides a cache for storing Handles
|
|
// to event message files.
|
|
|
|
import (
|
|
"expvar"
|
|
"time"
|
|
|
|
"github.com/elastic/beats/libbeat/common"
|
|
"github.com/elastic/beats/libbeat/logp"
|
|
"github.com/elastic/beats/winlogbeat/sys"
|
|
)
|
|
|
|
// Stats for the message file caches.
|
|
var (
|
|
cacheStats = expvar.NewMap("msg_file_cache")
|
|
)
|
|
|
|
// Constants that control the cache behavior.
|
|
const (
|
|
expirationTimeout time.Duration = 2 * time.Minute
|
|
janitorInterval time.Duration = 30 * time.Second
|
|
initialSize int = 10
|
|
)
|
|
|
|
// Function type for loading event message files associated with the given
|
|
// event log and source name.
|
|
type messageFileLoaderFunc func(eventLogName, sourceName string) sys.MessageFiles
|
|
|
|
// Function type for freeing Handles.
|
|
type freeHandleFunc func(handle uintptr) error
|
|
|
|
// handleCache provides a synchronized cache that holds MessageFiles.
|
|
type messageFilesCache struct {
|
|
cache *common.Cache
|
|
loader messageFileLoaderFunc
|
|
freer freeHandleFunc
|
|
eventLogName string
|
|
|
|
// Cache metrics.
|
|
hit func() // Increments number of cache hits.
|
|
miss func() // Increments number of cache misses.
|
|
size func() // Sets the current cache size.
|
|
}
|
|
|
|
// newHandleCache creates and returns a new handleCache that has been
|
|
// initialized (including starting a periodic janitor goroutine to purge
|
|
// expired Handles).
|
|
func newMessageFilesCache(eventLogName string, loader messageFileLoaderFunc,
|
|
freer freeHandleFunc) *messageFilesCache {
|
|
|
|
size := &expvar.Int{}
|
|
cacheStats.Set(eventLogName+"Size", size)
|
|
|
|
hc := &messageFilesCache{
|
|
loader: loader,
|
|
freer: freer,
|
|
eventLogName: eventLogName,
|
|
hit: func() { cacheStats.Add(eventLogName+"Hits", 1) },
|
|
miss: func() { cacheStats.Add(eventLogName+"Misses", 1) },
|
|
}
|
|
hc.cache = common.NewCacheWithRemovalListener(expirationTimeout,
|
|
initialSize, hc.evictionHandler)
|
|
hc.cache.StartJanitor(janitorInterval)
|
|
hc.size = func() {
|
|
s := hc.cache.Size()
|
|
size.Set(int64(s))
|
|
debugf("messageFilesCache[%s] size=%d", hc.eventLogName, s)
|
|
}
|
|
return hc
|
|
}
|
|
|
|
// get returns a cached MessageFiles for the given sourceName.
|
|
// If no item is cached, then one is loaded, stored, and returned.
|
|
// Callers should check the MessageFiles.Err value to see if an error occurred
|
|
// while loading the message files.
|
|
func (hc *messageFilesCache) get(sourceName string) sys.MessageFiles {
|
|
v := hc.cache.Get(sourceName)
|
|
if v == nil {
|
|
hc.miss()
|
|
|
|
// Handle to event message file for sourceName is not cached. Attempt
|
|
// to load the Handles into the cache.
|
|
v = hc.loader(hc.eventLogName, sourceName)
|
|
|
|
// Store the newly loaded value. Since this code does not lock we must
|
|
// check if a value was already loaded.
|
|
existing := hc.cache.PutIfAbsent(sourceName, v)
|
|
if existing != nil {
|
|
// A value was already loaded, so free the handles we just created.
|
|
messageFiles, _ := v.(sys.MessageFiles)
|
|
hc.freeHandles(messageFiles)
|
|
|
|
// Return the existing cached value.
|
|
messageFiles, _ = existing.(sys.MessageFiles)
|
|
return messageFiles
|
|
}
|
|
hc.size()
|
|
} else {
|
|
hc.hit()
|
|
}
|
|
|
|
messageFiles, _ := v.(sys.MessageFiles)
|
|
return messageFiles
|
|
}
|
|
|
|
// evictionHandler is the callback handler that receives notifications when
|
|
// a key-value pair is evicted from the messageFilesCache.
|
|
func (hc *messageFilesCache) evictionHandler(k common.Key, v common.Value) {
|
|
// Update the size on a different goroutine after the callback completes.
|
|
defer func() { go hc.size() }()
|
|
|
|
messageFiles, ok := v.(sys.MessageFiles)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
debugf("messageFilesCache[%s] Evicting messageFiles %+v for sourceName %v.",
|
|
hc.eventLogName, messageFiles, k)
|
|
hc.freeHandles(messageFiles)
|
|
}
|
|
|
|
// freeHandles free the event message file Handles so that the modules can
|
|
// be unloaded. The Handles are no longer valid after being freed.
|
|
func (hc *messageFilesCache) freeHandles(mf sys.MessageFiles) {
|
|
for _, fh := range mf.Handles {
|
|
err := hc.freer(fh.Handle)
|
|
if err != nil {
|
|
logp.Warn("messageFilesCache[%s] FreeLibrary error for handle %v",
|
|
hc.eventLogName, fh.Handle)
|
|
}
|
|
}
|
|
}
|