forked from elastic/beats
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Cursor input old state collection (elastic#19528)
The change provides the collection and deletion support for old states. All entries added to the store have a TTL configured. A key-value pair will be removed from the store once the most recent update timestamp + TTL < now. Pairs with pending updates will not be deleted yet. The collector currently only implements the logic for the clean_inactive setting. In comparison to the old registar, is the TTL not reset on startup. Old entries are still subject to collection, even if they've not been claimed. The TTL will be updated by the InputManager if an input with a different TTL is started for an already seen source.
- Loading branch information
Steffen Siering
committed
Jul 8, 2020
1 parent
623201a
commit 3d28fab
Showing
2 changed files
with
242 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
// 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 cursor | ||
|
||
import ( | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/elastic/beats/v7/libbeat/logp" | ||
) | ||
|
||
func TestGCStore(t *testing.T) { | ||
t.Run("empty store", func(t *testing.T) { | ||
started := time.Now() | ||
|
||
backend := createSampleStore(t, nil) | ||
store := testOpenStore(t, "test", backend) | ||
defer store.Release() | ||
|
||
gcStore(logp.NewLogger("test"), started, store) | ||
|
||
want := map[string]state{} | ||
checkEqualStoreState(t, want, backend.snapshot()) | ||
}) | ||
|
||
t.Run("state is still alive", func(t *testing.T) { | ||
started := time.Now() | ||
const ttl = 60 * time.Second | ||
|
||
initState := map[string]state{ | ||
"test::key": { | ||
TTL: ttl, | ||
Updated: started.Add(-ttl / 2), | ||
}, | ||
} | ||
|
||
backend := createSampleStore(t, initState) | ||
store := testOpenStore(t, "test", backend) | ||
defer store.Release() | ||
|
||
gcStore(logp.NewLogger("test"), started, store) | ||
|
||
checkEqualStoreState(t, initState, backend.snapshot()) | ||
}) | ||
|
||
t.Run("old state can be removed", func(t *testing.T) { | ||
const ttl = 60 * time.Second | ||
started := time.Now().Add(-5 * ttl) // cleanup process is running for a while already | ||
|
||
initState := map[string]state{ | ||
"test::key": { | ||
TTL: ttl, | ||
Updated: started.Add(-ttl), | ||
}, | ||
} | ||
|
||
backend := createSampleStore(t, initState) | ||
store := testOpenStore(t, "test", backend) | ||
defer store.Release() | ||
|
||
gcStore(logp.NewLogger("test"), started, store) | ||
|
||
want := map[string]state{} | ||
checkEqualStoreState(t, want, backend.snapshot()) | ||
}) | ||
|
||
t.Run("old state is not removed if cleanup is not active long enough", func(t *testing.T) { | ||
const ttl = 60 * time.Minute | ||
started := time.Now() | ||
|
||
initState := map[string]state{ | ||
"test::key": { | ||
TTL: ttl, | ||
Updated: started.Add(-2 * ttl), | ||
}, | ||
} | ||
|
||
backend := createSampleStore(t, initState) | ||
store := testOpenStore(t, "test", backend) | ||
defer store.Release() | ||
|
||
gcStore(logp.NewLogger("test"), started, store) | ||
|
||
checkEqualStoreState(t, initState, backend.snapshot()) | ||
}) | ||
|
||
t.Run("old state but resource is accessed", func(t *testing.T) { | ||
const ttl = 60 * time.Second | ||
started := time.Now().Add(-5 * ttl) // cleanup process is running for a while already | ||
|
||
initState := map[string]state{ | ||
"test::key": { | ||
TTL: ttl, | ||
Updated: started.Add(-ttl), | ||
}, | ||
} | ||
|
||
backend := createSampleStore(t, initState) | ||
store := testOpenStore(t, "test", backend) | ||
defer store.Release() | ||
|
||
// access resource and check it is not gc'ed | ||
res := store.Get("test::key") | ||
gcStore(logp.NewLogger("test"), started, store) | ||
checkEqualStoreState(t, initState, backend.snapshot()) | ||
|
||
// release resource and check it gets gc'ed | ||
res.Release() | ||
want := map[string]state{} | ||
gcStore(logp.NewLogger("test"), started, store) | ||
checkEqualStoreState(t, want, backend.snapshot()) | ||
}) | ||
|
||
t.Run("old state but resource has pending updates", func(t *testing.T) { | ||
const ttl = 60 * time.Second | ||
started := time.Now().Add(-5 * ttl) // cleanup process is running for a while already | ||
|
||
initState := map[string]state{ | ||
"test::key": { | ||
TTL: ttl, | ||
Updated: started.Add(-ttl), | ||
}, | ||
} | ||
|
||
backend := createSampleStore(t, initState) | ||
store := testOpenStore(t, "test", backend) | ||
defer store.Release() | ||
|
||
// create pending update operation | ||
res := store.Get("test::key") | ||
op, err := createUpdateOp(store, res, "test-state-update") | ||
require.NoError(t, err) | ||
res.Release() | ||
|
||
// cleanup fails | ||
gcStore(logp.NewLogger("test"), started, store) | ||
checkEqualStoreState(t, initState, backend.snapshot()) | ||
|
||
// cancel operation (no more pending operations) and try to gc again | ||
op.done(1) | ||
gcStore(logp.NewLogger("test"), started, store) | ||
want := map[string]state{} | ||
checkEqualStoreState(t, want, backend.snapshot()) | ||
}) | ||
} |