Repository: kubernetes-sigs/security-profiles-operator
Pull request #1061 (2022-07-28 10:44:52)

Switch to ttlcache v3

#### What type of PR is this?

/kind cleanup


#### What this PR does / why we need it:
Update ttlcache to the latest v3 release and adapt all code paths.
#### Which issue(s) this PR fixes:

<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.

Fixes #

or

None
-->
None
#### Does this PR have test?
None
<!--
If tests aren't applicable just write N/A.
-->

#### Special notes for your reviewer:
None
#### Does this PR introduce a user-facing change?

<!--
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".

For more information on release notes see: https://git.k8s.io/community/contributors/guide/release-notes.md
-->

```release-note
None
```


k8s-ci-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/security-profiles-operator/pull/1061#" title="Author self-approved">saschagrunert</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fsecurity-profiles-operator).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/kubernetes-sigs/security-profiles-operator/blob/main/OWNERS)~~ [saschagrunert]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->

Patch:
diff --git a/.golangci.yml b/.golangci.yml
index 16ff93e6b..8167a5649 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -37,7 +37,7 @@ linters:
     - gochecknoinits
     - gocognit
     - goconst
-    - gocritic
+    # - gocritic # TODO: re-enable when it supports go 1.18 and generics
     - gocyclo
     - godot
     - godox
diff --git a/dependencies.yaml b/dependencies.yaml
index 65d136996..605b9dbce 100644
--- a/dependencies.yaml
+++ b/dependencies.yaml
@@ -52,7 +52,7 @@ dependencies:
       match: OLM_VERSION
 
   - name: cert-manager
-    version: 1.8.0
+    version: 1.9.1
     refPaths:
     - path: test/e2e_test.go
       match: cert-manager/cert-manager
diff --git a/go.mod b/go.mod
index 32ec0188e..bcaf07c53 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,6 @@ module sigs.k8s.io/security-profiles-operator
 go 1.18
 
 require (
-	github.com/ReneKroon/ttlcache/v2 v2.11.0
 	github.com/acobaugh/osrelease v0.0.0-20181218015638-a93a0a55a249
 	github.com/aquasecurity/libbpfgo v0.3.0-libbpf-0.8.0
 	github.com/blang/semver/v4 v4.0.0
@@ -11,6 +10,7 @@ require (
 	github.com/containers/common v0.48.1-0.20220510094751-400832f41771
 	github.com/crossplane/crossplane-runtime v0.17.0
 	github.com/go-logr/logr v1.2.3
+	github.com/jellydator/ttlcache/v3 v3.0.0
 	github.com/maxbrunsfeld/counterfeiter/v6 v6.5.0
 	github.com/mogensen/kubernetes-split-yaml v0.3.0
 	github.com/nxadm/tail v1.4.8
diff --git a/go.sum b/go.sum
index 75800de87..7eb69e6d5 100644
--- a/go.sum
+++ b/go.sum
@@ -127,8 +127,6 @@ github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt
 github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
-github.com/ReneKroon/ttlcache/v2 v2.11.0 h1:OvlcYFYi941SBN3v9dsDcC2N8vRxyHcCmJb3Vl4QMoM=
-github.com/ReneKroon/ttlcache/v2 v2.11.0/go.mod h1:mBxvsNY+BT8qLLd6CuAJubbKo6r0jh3nb5et22bbfGY=
 github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
 github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
 github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
@@ -829,6 +827,8 @@ github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6t
 github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw=
 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
+github.com/jellydator/ttlcache/v3 v3.0.0 h1:zmFhqrB/4sKiEiJHhtseJsNRE32IMVmJSs4++4gaQO4=
+github.com/jellydator/ttlcache/v3 v3.0.0/go.mod h1:WwTaEmcXQ3MTjOm4bsZoDFiCu/hMvNWLO1w67RXz6h4=
 github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
 github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
 github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4=
@@ -1913,7 +1913,6 @@ golang.org/x/tools v0.0.0-20210104081019-d8d6ddbec6ee/go.mod h1:emZCQorbCU4vsT4f
 golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20210112230658-8b4aab62c064/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU=
 golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU=
diff --git a/installation-usage.md b/installation-usage.md
index 5fca34857..aff0acdb5 100644
--- a/installation-usage.md
+++ b/installation-usage.md
@@ -62,7 +62,7 @@ cert-manager via `kubectl`, if you're **not** running on
 [OpenShift](https://www.redhat.com/en/technologies/cloud-computing/openshift):
 
 ```sh
-$ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.8.0/cert-manager.yaml
+$ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.9.1/cert-manager.yaml
 $ kubectl --namespace cert-manager wait --for condition=ready pod -l app.kubernetes.io/instance=cert-manager
 ```
 
diff --git a/internal/pkg/daemon/bpfrecorder/bpfrecorder.go b/internal/pkg/daemon/bpfrecorder/bpfrecorder.go
index 797841aa5..3d4cfd7b9 100644
--- a/internal/pkg/daemon/bpfrecorder/bpfrecorder.go
+++ b/internal/pkg/daemon/bpfrecorder/bpfrecorder.go
@@ -34,10 +34,10 @@ import (
 	"syscall"
 	"time"
 
-	"github.com/ReneKroon/ttlcache/v2"
 	bpf "github.com/aquasecurity/libbpfgo"
 	"github.com/blang/semver/v4"
 	"github.com/go-logr/logr"
+	"github.com/jellydator/ttlcache/v3"
 	seccomp "github.com/seccomp/libseccomp-golang"
 	"google.golang.org/grpc"
 	"google.golang.org/grpc/credentials/insecure"
@@ -55,7 +55,7 @@ const (
 	defaultTimeout      time.Duration = time.Minute
 	maxMsgSize          int           = 16 * 1024 * 1024
 	defaultCacheTimeout time.Duration = time.Hour
-	maxCacheItems       int           = 1000
+	maxCacheItems       uint64        = 1000
 )
 
 // BpfRecorder is the main structure of this package.
@@ -67,8 +67,8 @@ type BpfRecorder struct {
 	syscalls                 *bpf.BPFMap
 	comms                    *bpf.BPFMap
 	btfPath                  string
-	syscallNamesForIDCache   *ttlcache.Cache
-	containerIDCache         *ttlcache.Cache
+	syscallNamesForIDCache   *ttlcache.Cache[string, string]
+	containerIDCache         *ttlcache.Cache[string, string]
 	nodeName                 string
 	clientset                *kubernetes.Clientset
 	profileForContainerIDs   sync.Map
@@ -89,10 +89,16 @@ type Pid struct {
 // New returns a new BpfRecorder instance.
 func New(logger logr.Logger) *BpfRecorder {
 	return &BpfRecorder{
-		impl:                     &defaultImpl{},
-		logger:                   logger,
-		syscallNamesForIDCache:   ttlcache.NewCache(),
-		containerIDCache:         ttlcache.NewCache(),
+		impl:   &defaultImpl{},
+		logger: logger,
+		syscallNamesForIDCache: ttlcache.New(
+			ttlcache.WithTTL[string, string](defaultCacheTimeout),
+			ttlcache.WithCapacity[string, string](maxCacheItems),
+		),
+		containerIDCache: ttlcache.New(
+			ttlcache.WithTTL[string, string](defaultCacheTimeout),
+			ttlcache.WithCapacity[string, string](maxCacheItems),
+		),
 		profileForContainerIDs:   sync.Map{},
 		pidsForProfiles:          sync.Map{},
 		pidLock:                  sync.Mutex{},
@@ -104,14 +110,10 @@ func New(logger logr.Logger) *BpfRecorder {
 // Run the BpfRecorder.
 func (b *BpfRecorder) Run() error {
 	b.logger.Info(fmt.Sprintf("Setting up caches with expiry of %v", defaultCacheTimeout))
-	for _, cache := range []*ttlcache.Cache{
+	for _, cache := range []*ttlcache.Cache[string, string]{
 		b.containerIDCache, b.syscallNamesForIDCache,
 	} {
-		if err := b.SetTTL(cache, defaultCacheTimeout); err != nil {
-			return fmt.Errorf("set cache timeout: %w", err)
-		}
-		cache.SetCacheSizeLimit(maxCacheItems)
-		defer cache.Close() // nolint:gocritic // this is intentional
+		go cache.Start()
 	}
 
 	b.nodeName = b.Getenv(config.NodeNameEnvKey)
@@ -758,14 +760,9 @@ func (b *BpfRecorder) unload() {
 func (b *BpfRecorder) syscallNameForID(id int) (string, error) {
 	// Check the cache first
 	key := strconv.Itoa(id)
-	if name, err := b.syscallNamesForIDCache.Get(key); !errors.Is(
-		err, ttlcache.ErrNotFound,
-	) {
-		nameString, ok := name.(string)
-		if !ok {
-			return "", errors.New("name is not a string")
-		}
-		return nameString, nil
+	item := b.syscallNamesForIDCache.Get(key)
+	if item != nil {
+		return item.Value(), nil
 	}
 
 	name, err := b.GetName(seccomp.ScmpSyscall(id))
@@ -773,8 +770,6 @@ func (b *BpfRecorder) syscallNameForID(id int) (string, error) {
 		return "", fmt.Errorf("get syscall name for ID %d: %w", id, err)
 	}
 
-	if err := b.syscallNamesForIDCache.Set(key, name); err != nil {
-		return "", fmt.Errorf("update cache: %w", err)
-	}
+	b.syscallNamesForIDCache.Set(key, name, ttlcache.DefaultTTL)
 	return name, nil
 }
diff --git a/internal/pkg/daemon/bpfrecorder/bpfrecorderfakes/fake_impl.go b/internal/pkg/daemon/bpfrecorder/bpfrecorderfakes/fake_impl.go
index 56a2a6d91..68a15516e 100644
--- a/internal/pkg/daemon/bpfrecorder/bpfrecorderfakes/fake_impl.go
+++ b/internal/pkg/daemon/bpfrecorder/bpfrecorderfakes/fake_impl.go
@@ -27,10 +27,9 @@ import (
 	"os"
 	"sync"
 	"syscall"
-	"time"
 
-	ttlcache "github.com/ReneKroon/ttlcache/v2"
 	"github.com/aquasecurity/libbpfgo"
+	ttlcache "github.com/jellydator/ttlcache/v3"
 	seccomp "github.com/seccomp/libseccomp-golang"
 	"google.golang.org/grpc"
 	v1 "k8s.io/api/core/v1"
@@ -121,10 +120,10 @@ type FakeImpl struct {
 	closeModuleArgsForCall []struct {
 		arg1 *libbpfgo.BPFMap
 	}
-	ContainerIDForPIDStub        func(ttlcache.SimpleCache, int) (string, error)
+	ContainerIDForPIDStub        func(*ttlcache.Cache[string, string], int) (string, error)
 	containerIDForPIDMutex       sync.RWMutex
 	containerIDForPIDArgsForCall []struct {
-		arg1 ttlcache.SimpleCache
+		arg1 *ttlcache.Cache[string, string]
 		arg2 int
 	}
 	containerIDForPIDReturns struct {
@@ -379,18 +378,6 @@ type FakeImpl struct {
 	serveReturnsOnCall map[int]struct {
 		result1 error
 	}
-	SetTTLStub        func(*ttlcache.Cache, time.Duration) error
-	setTTLMutex       sync.RWMutex
-	setTTLArgsForCall []struct {
-		arg1 *ttlcache.Cache
-		arg2 time.Duration
-	}
-	setTTLReturns struct {
-		result1 error
-	}
-	setTTLReturnsOnCall map[int]struct {
-		result1 error
-	}
 	StartRingBufferStub        func(*libbpfgo.RingBuffer)
 	startRingBufferMutex       sync.RWMutex
 	startRingBufferArgsForCall []struct {
@@ -875,11 +862,11 @@ func (fake *FakeImpl) CloseModuleArgsForCall(i int) *libbpfgo.BPFMap {
 	return argsForCall.arg1
 }
 
-func (fake *FakeImpl) ContainerIDForPID(arg1 ttlcache.SimpleCache, arg2 int) (string, error) {
+func (fake *FakeImpl) ContainerIDForPID(arg1 *ttlcache.Cache[string, string], arg2 int) (string, error) {
 	fake.containerIDForPIDMutex.Lock()
 	ret, specificReturn := fake.containerIDForPIDReturnsOnCall[len(fake.containerIDForPIDArgsForCall)]
 	fake.containerIDForPIDArgsForCall = append(fake.containerIDForPIDArgsForCall, struct {
-		arg1 ttlcache.SimpleCache
+		arg1 *ttlcache.Cache[string, string]
 		arg2 int
 	}{arg1, arg2})
 	stub := fake.ContainerIDForPIDStub
@@ -901,13 +888,13 @@ func (fake *FakeImpl) ContainerIDForPIDCallCount() int {
 	return len(fake.containerIDForPIDArgsForCall)
 }
 
-func (fake *FakeImpl) ContainerIDForPIDCalls(stub func(ttlcache.SimpleCache, int) (string, error)) {
+func (fake *FakeImpl) ContainerIDForPIDCalls(stub func(*ttlcache.Cache[string, string], int) (string, error)) {
 	fake.containerIDForPIDMutex.Lock()
 	defer fake.containerIDForPIDMutex.Unlock()
 	fake.ContainerIDForPIDStub = stub
 }
 
-func (fake *FakeImpl) ContainerIDForPIDArgsForCall(i int) (ttlcache.SimpleCache, int) {
+func (fake *FakeImpl) ContainerIDForPIDArgsForCall(i int) (*ttlcache.Cache[string, string], int) {
 	fake.containerIDForPIDMutex.RLock()
 	defer fake.containerIDForPIDMutex.RUnlock()
 	argsForCall := fake.containerIDForPIDArgsForCall[i]
@@ -2120,68 +2107,6 @@ func (fake *FakeImpl) ServeReturnsOnCall(i int, result1 error) {
 	}{result1}
 }
 
-func (fake *FakeImpl) SetTTL(arg1 *ttlcache.Cache, arg2 time.Duration) error {
-	fake.setTTLMutex.Lock()
-	ret, specificReturn := fake.setTTLReturnsOnCall[len(fake.setTTLArgsForCall)]
-	fake.setTTLArgsForCall = append(fake.setTTLArgsForCall, struct {
-		arg1 *ttlcache.Cache
-		arg2 time.Duration
-	}{arg1, arg2})
-	stub := fake.SetTTLStub
-	fakeReturns := fake.setTTLReturns
-	fake.recordInvocation("SetTTL", []interface{}{arg1, arg2})
-	fake.setTTLMutex.Unlock()
-	if stub != nil {
-		return stub(arg1, arg2)
-	}
-	if specificReturn {
-		return ret.result1
-	}
-	return fakeReturns.result1
-}
-
-func (fake *FakeImpl) SetTTLCallCount() int {
-	fake.setTTLMutex.RLock()
-	defer fake.setTTLMutex.RUnlock()
-	return len(fake.setTTLArgsForCall)
-}
-
-func (fake *FakeImpl) SetTTLCalls(stub func(*ttlcache.Cache, time.Duration) error) {
-	fake.setTTLMutex.Lock()
-	defer fake.setTTLMutex.Unlock()
-	fake.SetTTLStub = stub
-}
-
-func (fake *FakeImpl) SetTTLArgsForCall(i int) (*ttlcache.Cache, time.Duration) {
-	fake.setTTLMutex.RLock()
-	defer fake.setTTLMutex.RUnlock()
-	argsForCall := fake.setTTLArgsForCall[i]
-	return argsForCall.arg1, argsForCall.arg2
-}
-
-func (fake *FakeImpl) SetTTLReturns(result1 error) {
-	fake.setTTLMutex.Lock()
-	defer fake.setTTLMutex.Unlock()
-	fake.SetTTLStub = nil
-	fake.setTTLReturns = struct {
-		result1 error
-	}{result1}
-}
-
-func (fake *FakeImpl) SetTTLReturnsOnCall(i int, result1 error) {
-	fake.setTTLMutex.Lock()
-	defer fake.setTTLMutex.Unlock()
-	fake.SetTTLStub = nil
-	if fake.setTTLReturnsOnCall == nil {
-		fake.setTTLReturnsOnCall = make(map[int]struct {
-			result1 error
-		})
-	}
-	fake.setTTLReturnsOnCall[i] = struct {
-		result1 error
-	}{result1}
-}
-
 func (fake *FakeImpl) StartRingBuffer(arg1 *libbpfgo.RingBuffer) {
 	fake.startRingBufferMutex.Lock()
 	fake.startRingBufferArgsForCall = append(fake.startRingBufferArgsForCall, struct {
@@ -2598,8 +2523,6 @@ func (fake *FakeImpl) Invocations() map[string][][]interface{} {
 	defer fake.sendMetricMutex.RUnlock()
 	fake.serveMutex.RLock()
 	defer fake.serveMutex.RUnlock()
-	fake.setTTLMutex.RLock()
-	defer fake.setTTLMutex.RUnlock()
 	fake.startRingBufferMutex.RLock()
 	defer fake.startRingBufferMutex.RUnlock()
 	fake.statMutex.RLock()
diff --git a/internal/pkg/daemon/bpfrecorder/impl.go b/internal/pkg/daemon/bpfrecorder/impl.go
index 36578a7b3..5a46442fb 100644
--- a/internal/pkg/daemon/bpfrecorder/impl.go
+++ b/internal/pkg/daemon/bpfrecorder/impl.go
@@ -28,12 +28,11 @@ import (
 	"runtime"
 	"strconv"
 	"syscall"
-	"time"
 	"unsafe"
 
-	"github.com/ReneKroon/ttlcache/v2"
 	"github.com/acobaugh/osrelease"
 	bpf "github.com/aquasecurity/libbpfgo"
+	"github.com/jellydator/ttlcache/v3"
 	seccomp "github.com/seccomp/libseccomp-golang"
 	"google.golang.org/grpc"
 	v1 "k8s.io/api/core/v1"
@@ -51,7 +50,6 @@ type defaultImpl struct{}
 //go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -generate
 //counterfeiter:generate . impl
 type impl interface {
-	SetTTL(*ttlcache.Cache, time.Duration) error
 	Getenv(string) string
 	InClusterConfig() (*rest.Config, error)
 	NewForConfig(*rest.Config) (*kubernetes.Clientset, error)
@@ -69,7 +67,7 @@ type impl interface {
 	Uname(*syscall.Utsname) error
 	TempFile(string, string) (*os.File, error)
 	Write(*os.File, []byte) (int, error)
-	ContainerIDForPID(ttlcache.SimpleCache, int) (string, error)
+	ContainerIDForPID(*ttlcache.Cache[string, string], int) (string, error)
 	GetValue(*bpf.BPFMap, uint32) ([]byte, error)
 	DeleteKey(*bpf.BPFMap, uint32) error
 	ListPods(context.Context, *kubernetes.Clientset, string) (*v1.PodList, error)
@@ -87,10 +85,6 @@ type impl interface {
 	SendMetric(apimetrics.Metrics_BpfIncClient, *apimetrics.BpfRequest) error
 }
 
-func (d *defaultImpl) SetTTL(cache *ttlcache.Cache, ttl time.Duration) error {
-	return cache.SetTTL(ttl)
-}
-
 func (d *defaultImpl) Getenv(key string) string {
 	return os.Getenv(key)
 }
@@ -161,7 +155,7 @@ func (d *defaultImpl) Write(file *os.File, b []byte) (n int, err error) {
 	return file.Write(b)
 }
 
-func (d *defaultImpl) ContainerIDForPID(cache ttlcache.SimpleCache, pid int) (string, error) {
+func (d *defaultImpl) ContainerIDForPID(cache *ttlcache.Cache[string, string], pid int) (string, error) {
 	return util.ContainerIDForPID(cache, pid)
 }
 
diff --git a/internal/pkg/daemon/enricher/audit.go b/internal/pkg/daemon/enricher/audit.go
index 5f6406cd1..7cb28e955 100644
--- a/internal/pkg/daemon/enricher/audit.go
+++ b/internal/pkg/daemon/enricher/audit.go
@@ -20,6 +20,8 @@ import (
 	"fmt"
 	"regexp"
 	"strconv"
+
+	"sigs.k8s.io/security-profiles-operator/internal/pkg/daemon/enricher/types"
 )
 
 // type IDs are defined at https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/audit.h
@@ -49,7 +51,7 @@ func isAuditLine(logLine string) bool {
 }
 
 // extractAuditLine extracts an auditline from logLine.
-func extractAuditLine(logLine string) (*auditLine, error) {
+func extractAuditLine(logLine string) (*types.AuditLine, error) {
 	if seccomp := extractSeccompLine(logLine); seccomp != nil {
 		return seccomp, nil
 	}
@@ -61,18 +63,18 @@ func extractAuditLine(logLine string) (*auditLine, error) {
 	return nil, fmt.Errorf("unsupported log line: %s", logLine)
 }
 
-func extractSeccompLine(logLine string) *auditLine {
+func extractSeccompLine(logLine string) *types.AuditLine {
 	captures := seccompLineRegex.FindStringSubmatch(logLine)
 	if len(captures) < minSeccompCapturesExpected {
 		return nil
 	}
 
-	line := auditLine{}
-	line.auditType = auditTypeSeccomp
-	line.timestampID = captures[2]
-	line.executable = captures[4]
+	line := types.AuditLine{}
+	line.AuditType = types.AuditTypeSeccomp
+	line.TimestampID = captures[2]
+	line.Executable = captures[4]
 	if v, err := strconv.Atoi(captures[3]); err == nil {
-		line.processID = v
+		line.ProcessID = v
 	}
 
 	const (
@@ -80,28 +82,28 @@ func extractSeccompLine(logLine string) *auditLine {
 		bitSize = 32
 	)
 	if v, err := strconv.ParseInt(captures[5], base, bitSize); err == nil {
-		line.systemCallID = int32(v)
+		line.SystemCallID = int32(v)
 	}
 
 	return &line
 }
 
-func extractSelinuxLine(logLine string) *auditLine {
+func extractSelinuxLine(logLine string) *types.AuditLine {
 	captures := selinuxLineRegex.FindStringSubmatch(logLine)
 	if len(captures) < minSelinuxCapturesExpected {
 		return nil
 	}
 
-	line := auditLine{}
-	line.auditType = auditTypeSelinux
-	line.timestampID = captures[1]
-	line.perm = captures[2]
+	line := types.AuditLine{}
+	line.AuditType = types.AuditTypeSelinux
+	line.TimestampID = captures[1]
+	line.Perm = captures[2]
 	if v, err := strconv.Atoi(captures[3]); err == nil {
-		line.processID = v
+		line.ProcessID = v
 	}
-	line.scontext = captures[4]
-	line.tcontext = captures[5]
-	line.tclass = captures[6]
+	line.Scontext = captures[4]
+	line.Tcontext = captures[5]
+	line.Tclass = captures[6]
 
 	return &line
 }
diff --git a/internal/pkg/daemon/enricher/container.go b/internal/pkg/daemon/enricher/container.go
index 78fcdf81f..e2d7de4bf 100644
--- a/internal/pkg/daemon/enricher/container.go
+++ b/internal/pkg/daemon/enricher/container.go
@@ -21,8 +21,8 @@ import (
 	"fmt"
 	"time"
 
-	"github.com/ReneKroon/ttlcache/v2"
 	"github.com/go-logr/logr"
+	"github.com/jellydator/ttlcache/v3"
 	"golang.org/x/net/context"
 	"golang.org/x/sync/errgroup"
 	v1 "k8s.io/api/core/v1"
@@ -30,6 +30,7 @@ import (
 	"k8s.io/apimachinery/pkg/util/wait"
 
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/config"
+	"sigs.k8s.io/security-profiles-operator/internal/pkg/daemon/enricher/types"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/util"
 )
 
@@ -52,30 +53,20 @@ var errContainerIDEmpty = errors.New("container ID is empty")
 
 func (e *Enricher) getContainerInfo(
 	nodeName, targetContainerID string,
-) (*containerInfo, error) {
+) (*types.ContainerInfo, error) {
 	// Check the cache first
-	if info, err := e.infoCache.Get(
-		targetContainerID,
-	); !errors.Is(err, ttlcache.ErrNotFound) {
-		cInfo, ok := info.(*containerInfo)
-		if !ok {
-			return nil, errors.New("cache value is not container info")
-		}
-		return cInfo, nil
+	item := e.infoCache.Get(targetContainerID)
+	if item != nil {
+		return item.Value(), nil
 	}
 
 	if err := e.populateContainerPodCache(nodeName, targetContainerID); err != nil {
 		return nil, fmt.Errorf("get container info for pods: %w", err)
 	}
 
-	if info, err := e.infoCache.Get(
-		targetContainerID,
-	); !errors.Is(err, ttlcache.ErrNotFound) {
-		cInfo, ok := info.(*containerInfo)
-		if !ok {
-			return nil, errors.New("cache value is not container info")
-		}
-		return cInfo, nil
+	item = e.infoCache.Get(targetContainerID)
+	if item != nil {
+		return item.Value(), nil
 	}
 
 	return nil, errors.New("no container info for container ID")
@@ -157,24 +148,22 @@ func (e *Enricher) populateCacheEntryForContainer(
 			if !ok {
 				recordProfile = pod.Annotations[config.SelinuxProfileRecordLogsAnnotationKey+containerName]
 			}
-			info := &containerInfo{
-				podName:       pod.Name,
-				containerName: containerStatus.Name,
-				namespace:     pod.Namespace,
-				containerID:   rawContainerID,
-				recordProfile: recordProfile,
+			info := &types.ContainerInfo{
+				PodName:       pod.Name,
+				ContainerName: containerStatus.Name,
+				Namespace:     pod.Namespace,
+				ContainerID:   rawContainerID,
+				RecordProfile: recordProfile,
 			}
 
 			if e.labelDenials && rawContainerID == targetContainerID {
 				e.logger.Info("labeling problematic container", "containerID", containerID,
 					"podNamespace", pod.Namespace, "podName", pod.Name)
-				e.labelPodDenials(context.TODO(), info.containerName, pod.DeepCopy(), e.logger)
+				e.labelPodDenials(context.TODO(), info.ContainerName, pod.DeepCopy(), e.logger)
 			}
 
 			// Update the cache
-			if err := e.infoCache.Set(rawContainerID, info); err != nil {
-				return fmt.Errorf("update cache: %w", err)
-			}
+			e.infoCache.Set(rawContainerID, info, ttlcache.DefaultTTL)
 		}
 
 		return errorToRetry
diff --git a/internal/pkg/daemon/enricher/enricher.go b/internal/pkg/daemon/enricher/enricher.go
index 3ce1ebd36..ea5002d4b 100644
--- a/internal/pkg/daemon/enricher/enricher.go
+++ b/internal/pkg/daemon/enricher/enricher.go
@@ -25,8 +25,8 @@ import (
 	"sync"
 	"time"
 
-	"github.com/ReneKroon/ttlcache/v2"
 	"github.com/go-logr/logr"
+	"github.com/jellydator/ttlcache/v3"
 	"github.com/nxadm/tail"
 	"google.golang.org/grpc"
 	"google.golang.org/grpc/credentials/insecure"
@@ -38,6 +38,7 @@ import (
 	apienricher "sigs.k8s.io/security-profiles-operator/api/grpc/enricher"
 	apimetrics "sigs.k8s.io/security-profiles-operator/api/grpc/metrics"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/config"
+	"sigs.k8s.io/security-profiles-operator/internal/pkg/daemon/enricher/types"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/util"
 )
 
@@ -49,7 +50,7 @@ const (
 
 	defaultTimeout time.Duration = time.Minute
 	maxMsgSize     int           = 16 * 1024 * 1024
-	maxCacheItems  int           = 1000
+	maxCacheItems  uint64        = 1000
 )
 
 // Enricher is the main structure of this package.
@@ -57,11 +58,11 @@ type Enricher struct {
 	apienricher.UnimplementedEnricherServer
 	impl             impl
 	logger           logr.Logger
-	containerIDCache *ttlcache.Cache
-	infoCache        *ttlcache.Cache
+	containerIDCache *ttlcache.Cache[string, string]
+	infoCache        *ttlcache.Cache[string, *types.ContainerInfo]
 	syscalls         sync.Map
 	avcs             sync.Map
-	auditLineCache   *ttlcache.Cache
+	auditLineCache   *ttlcache.Cache[string, []*types.AuditLine]
 	clientset        kubernetes.Interface
 	labelDenials     bool
 }
@@ -85,15 +86,28 @@ func New(logger logr.Logger, labelDenials bool, impls ...impl) (*Enricher, error
 	}
 
 	return &Enricher{
-		impl:             effectiveimpl,
-		logger:           logger,
-		containerIDCache: ttlcache.NewCache(),
-		infoCache:        ttlcache.NewCache(),
-		syscalls:         sync.Map{},
-		avcs:             sync.Map{},
-		auditLineCache:   ttlcache.NewCache(),
-		labelDenials:     labelDenials,
-		clientset:        clientset,
+		impl:   effectiveimpl,
+		logger: logger,
+		containerIDCache: ttlcache.New(
+			ttlcache.WithTTL[string, string](defaultCacheTimeout),
+			ttlcache.WithCapacity[string, string](maxCacheItems),
+		),
+		infoCache: ttlcache.New(
+			ttlcache.WithTTL[string, *types.ContainerInfo](defaultCacheTimeout),
+			ttlcache.WithCapacity[string, *types.ContainerInfo](maxCacheItems),
+		),
+		syscalls: sync.Map{},
+		avcs:     sync.Map{},
+		auditLineCache: ttlcache.New(
+			ttlcache.WithTTL[string, []*types.AuditLine](defaultCacheTimeout),
+			ttlcache.WithCapacity[string, []*types.AuditLine](maxCacheItems),
+			// For the audit line cache we don't want to increase the TTL on
+			// Get calls because we want the TTLs just to quietly expire
+			// if/when the cache is full.
+			ttlcache.WithDisableTouchOnHit[string, []*types.AuditLine](),
+		),
+		labelDenials: labelDenials,
+		clientset:    clientset,
 	}, nil
 }
 
@@ -105,22 +119,9 @@ func (e *Enricher) Run() error {
 	}
 
 	e.logger.Info(fmt.Sprintf("Setting up caches with expiry of %v", defaultCacheTimeout))
-	for _, cache := range []*ttlcache.Cache{
-		e.containerIDCache,
-		e.infoCache,
-		e.auditLineCache,
-	} {
-		if err := e.impl.SetTTL(cache, defaultCacheTimeout); err != nil {
-			return fmt.Errorf("set cache timeout: %w", err)
-		}
-		cache.SetCacheSizeLimit(maxCacheItems)
-		defer cache.Close() // nolint:gocritic // this is intentional
-	}
-
-	// For the audit line cache we don't want to increase the TTL on Gets
-	// because we want the TTLs just to quietly expire if/when the cache
-	// is full
-	e.auditLineCache.SkipTTLExtensionOnHit(true)
+	go e.containerIDCache.Start()
+	go e.infoCache.Start()
+	go e.auditLineCache.Start()
 
 	nodeName := e.impl.Getenv(config.NodeNameEnvKey)
 	if nodeName == "" {
@@ -202,8 +203,8 @@ func (e *Enricher) Run() error {
 			continue
 		}
 
-		e.logger.V(config.VerboseLevel).Info(fmt.Sprintf("Get container ID for PID: %d", auditLine.processID))
-		cID, err := e.impl.ContainerIDForPID(e.containerIDCache, auditLine.processID)
+		e.logger.V(config.VerboseLevel).Info(fmt.Sprintf("Get container ID for PID: %d", auditLine.ProcessID))
+		cID, err := e.impl.ContainerIDForPID(e.containerIDCache, auditLine.ProcessID)
 		if errors.Is(err, os.ErrNotExist) {
 			// We're probably in container creation or removal
 			if backlogErr := e.addToBacklog(auditLine); backlogErr != nil {
@@ -214,7 +215,7 @@ func (e *Enricher) Run() error {
 		if err != nil {
 			e.logger.Error(
 				err, "unable to get container ID",
-				"processID", auditLine.processID,
+				"processID", auditLine.ProcessID,
 			)
 			if backlogErr := e.addToBacklog(auditLine); backlogErr != nil {
 				e.logger.Error(backlogErr, "adding line to backlog")
@@ -227,7 +228,7 @@ func (e *Enricher) Run() error {
 		if err != nil {
 			e.logger.Error(
 				err, "container ID not found in cluster",
-				"processID", auditLine.processID,
+				"processID", auditLine.ProcessID,
 				"containerID", cID,
 			)
 			if backlogErr := e.addToBacklog(auditLine); backlogErr != nil {
@@ -244,7 +245,7 @@ func (e *Enricher) Run() error {
 		}
 
 		// check if there's anything in the cache for this processID
-		err = e.dispatchBacklog(metricsClient, nodeName, info, auditLine.processID)
+		err = e.dispatchBacklog(metricsClient, nodeName, info, auditLine.ProcessID)
 		if err != nil {
 			e.logger.Error(
 				err, "process backlog")
@@ -308,23 +309,16 @@ func Dial() (*grpc.ClientConn, context.CancelFunc, error) {
 	return conn, cancel, nil
 }
 
-func (e *Enricher) addToBacklog(line *auditLine) error {
-	strPid := strconv.Itoa(line.processID)
+func (e *Enricher) addToBacklog(line *types.AuditLine) error {
+	strPid := strconv.Itoa(line.ProcessID)
 
-	backlog, err := e.auditLineCache.Get(strPid)
-	if errors.Is(err, ttlcache.ErrNotFound) {
-		if setErr := e.impl.AddToBacklog(e.auditLineCache, strPid, []*auditLine{line}); setErr != nil {
-			return fmt.Errorf("adding the first line to the backlog: %w", setErr)
-		}
+	item := e.auditLineCache.Get(strPid)
+	if item == nil {
+		e.impl.AddToBacklog(e.auditLineCache, strPid, []*types.AuditLine{line})
 		return nil
-	} else if err != nil {
-		return fmt.Errorf("retrieving an item from the cache: %w", err)
 	}
 
-	auditBacklog, ok := backlog.([]*auditLine)
-	if !ok {
-		return errors.New("wrong type")
-	}
+	auditBacklog := item.Value()
 
 	if auditBacklog == nil {
 		// this should not happen, but let's be paranoid
@@ -339,32 +333,22 @@ func (e *Enricher) addToBacklog(line *auditLine) error {
 		return nil
 	}
 
-	if setErr := e.impl.AddToBacklog(e.auditLineCache, strPid, append(auditBacklog, line)); setErr != nil {
-		return fmt.Errorf("adding a line to the backlog: %w", setErr)
-	}
-
+	e.impl.AddToBacklog(e.auditLineCache, strPid, append(auditBacklog, line))
 	return nil
 }
 
 func (e *Enricher) dispatchBacklog(
 	metricsClient apimetrics.Metrics_AuditIncClient,
 	nodeName string,
-	info *containerInfo,
+	info *types.ContainerInfo,
 	processID int,
 ) error {
 	strPid := strconv.Itoa(processID)
 
-	backlog, err := e.impl.GetFromBacklog(e.auditLineCache, strPid)
-	if errors.Is(err, ttlcache.ErrNotFound) {
+	auditBacklog := e.impl.GetFromBacklog(e.auditLineCache, strPid)
+	if auditBacklog == nil {
 		// nothing in the cache
 		return nil
-	} else if err != nil {
-		return fmt.Errorf("retrieving backlog: %w", err)
-	}
-
-	auditBacklog, ok := backlog.([]*auditLine)
-	if !ok {
-		return errors.New("wrong type")
 	}
 
 	for i := range auditBacklog {
@@ -376,26 +360,23 @@ func (e *Enricher) dispatchBacklog(
 		}
 	}
 
-	if err := e.impl.FlushBacklog(e.auditLineCache, strPid); err != nil {
-		return fmt.Errorf("flushing backlog: %w", err)
-	}
-
+	e.impl.FlushBacklog(e.auditLineCache, strPid)
 	return nil
 }
 
 func (e *Enricher) dispatchAuditLine(
 	metricsClient apimetrics.Metrics_AuditIncClient,
 	nodeName string,
-	auditLine *auditLine,
-	info *containerInfo,
+	auditLine *types.AuditLine,
+	info *types.ContainerInfo,
 ) error {
-	switch auditLine.auditType {
-	case auditTypeSelinux:
+	switch auditLine.AuditType {
+	case types.AuditTypeSelinux:
 		e.dispatchSelinuxLine(metricsClient, nodeName, auditLine, info)
-	case auditTypeSeccomp:
+	case types.AuditTypeSeccomp:
 		e.dispatchSeccompLine(metricsClient, nodeName, auditLine, info)
 	default:
-		return fmt.Errorf("unknown audit line type %s", auditLine.auditType)
+		return fmt.Errorf("unknown audit line type %s", auditLine.AuditType)
 	}
 
 	return nil
@@ -404,53 +385,53 @@ func (e *Enricher) dispatchAuditLine(
 func (e *Enricher) dispatchSelinuxLine(
 	metricsClient apimetrics.Metrics_AuditIncClient,
 	nodeName string,
-	auditLine *auditLine,
-	info *containerInfo,
+	auditLine *types.AuditLine,
+	info *types.ContainerInfo,
 ) {
 	e.logger.Info("audit",
-		"timestamp", auditLine.timestampID,
-		"type", auditLine.auditType,
-		"profile", info.recordProfile,
+		"timestamp", auditLine.TimestampID,
+		"type", auditLine.AuditType,
+		"profile", info.RecordProfile,
 		"node", nodeName,
-		"namespace", info.namespace,
-		"pod", info.podName,
-		"container", info.containerName,
-		"perm", auditLine.perm,
-		"scontext", auditLine.scontext,
-		"tcontext", auditLine.tcontext,
-		"tclass", auditLine.tclass,
+		"namespace", info.Namespace,
+		"pod", info.PodName,
+		"container", info.ContainerName,
+		"perm", auditLine.Perm,
+		"scontext", auditLine.Scontext,
+		"tcontext", auditLine.Tcontext,
+		"tclass", auditLine.Tclass,
 	)
 
 	if err := e.impl.SendMetric(
 		metricsClient,
 		&apimetrics.AuditRequest{
 			Node:       nodeName,
-			Namespace:  info.namespace,
-			Pod:        info.podName,
-			Container:  info.containerName,
-			Executable: auditLine.executable,
+			Namespace:  info.Namespace,
+			Pod:        info.PodName,
+			Container:  info.ContainerName,
+			Executable: auditLine.Executable,
 			SelinuxReq: &apimetrics.AuditRequest_SelinuxAuditReq{
-				Scontext: auditLine.scontext,
-				Tcontext: auditLine.tcontext,
+				Scontext: auditLine.Scontext,
+				Tcontext: auditLine.Tcontext,
 			},
 		},
 	); err != nil {
 		e.logger.Error(err, "unable to update metrics")
 	}
 
-	if info.recordProfile != "" {
+	if info.RecordProfile != "" {
 		avc := &apienricher.AvcResponse_SelinuxAvc{
-			Perm:     auditLine.perm,
-			Scontext: auditLine.scontext,
-			Tcontext: auditLine.tcontext,
-			Tclass:   auditLine.tclass,
+			Perm:     auditLine.Perm,
+			Scontext: auditLine.Scontext,
+			Tcontext: auditLine.Tcontext,
+			Tclass:   auditLine.Tclass,
 		}
 		jsonBytes, err := protojson.Marshal(avc)
 		if err != nil {
 			e.logger.Error(err, "marshall protobuf")
 		}
 
-		a, _ := e.avcs.LoadOrStore(info.recordProfile, sets.NewString())
+		a, _ := e.avcs.LoadOrStore(info.RecordProfile, sets.NewString())
 		stringSet, ok := a.(sets.String)
 		if ok {
 			stringSet.Insert(string(jsonBytes))
@@ -461,29 +442,29 @@ func (e *Enricher) dispatchSelinuxLine(
 func (e *Enricher) dispatchSeccompLine(
 	metricsClient apimetrics.Metrics_AuditIncClient,
 	nodeName string,
-	auditLine *auditLine,
-	info *containerInfo,
+	auditLine *types.AuditLine,
+	info *types.ContainerInfo,
 ) {
-	syscallName, err := syscallName(auditLine.systemCallID)
+	syscallName, err := syscallName(auditLine.SystemCallID)
 	if err != nil {
 		e.logger.Info(
 			"no syscall name found for ID",
-			"syscallID", auditLine.systemCallID,
+			"syscallID", auditLine.SystemCallID,
 			"err", err.Error(),
 		)
 		return
 	}
 
 	e.logger.Info("audit",
-		"timestamp", auditLine.timestampID,
-		"type", auditLine.auditType,
+		"timestamp", auditLine.TimestampID,
+		"type", auditLine.AuditType,
 		"node", nodeName,
-		"namespace", info.namespace,
-		"pod", info.podName,
-		"container", info.containerName,
-		"executable", auditLine.executable,
-		"pid", auditLine.processID,
-		"syscallID", auditLine.systemCallID,
+		"namespace", info.Namespace,
+		"pod", info.PodName,
+		"container", info.ContainerName,
+		"executable", auditLine.Executable,
+		"pid", auditLine.ProcessID,
+		"syscallID", auditLine.SystemCallID,
 		"syscallName", syscallName,
 	)
 
@@ -491,10 +472,10 @@ func (e *Enricher) dispatchSeccompLine(
 		metricsClient,
 		&apimetrics.AuditRequest{
 			Node:       nodeName,
-			Namespace:  info.namespace,
-			Pod:        info.podName,
-			Container:  info.containerName,
-			Executable: auditLine.executable,
+			Namespace:  info.Namespace,
+			Pod:        info.PodName,
+			Container:  info.ContainerName,
+			Executable: auditLine.Executable,
 			SeccompReq: &apimetrics.AuditRequest_SeccompAuditReq{
 				Syscall: syscallName,
 			},
@@ -503,8 +484,8 @@ func (e *Enricher) dispatchSeccompLine(
 		e.logger.Error(err, "unable to update metrics")
 	}
 
-	if info.recordProfile != "" {
-		s, _ := e.syscalls.LoadOrStore(info.recordProfile, sets.NewString())
+	if info.RecordProfile != "" {
+		s, _ := e.syscalls.LoadOrStore(info.RecordProfile, sets.NewString())
 		stringSet, ok := s.(sets.String)
 		if ok {
 			stringSet.Insert(syscallName)
diff --git a/internal/pkg/daemon/enricher/enricherfakes/fake_impl.go b/internal/pkg/daemon/enricher/enricherfakes/fake_impl.go
index 61953b488..f91924c22 100644
--- a/internal/pkg/daemon/enricher/enricherfakes/fake_impl.go
+++ b/internal/pkg/daemon/enricher/enricherfakes/fake_impl.go
@@ -22,30 +22,24 @@ import (
 	"io/fs"
 	"net"
 	"sync"
-	"time"
 
-	ttlcache "github.com/ReneKroon/ttlcache/v2"
+	ttlcache "github.com/jellydator/ttlcache/v3"
 	"github.com/nxadm/tail"
 	"google.golang.org/grpc"
 	v1 "k8s.io/api/core/v1"
 	"k8s.io/client-go/kubernetes"
 	"k8s.io/client-go/rest"
 	api_metrics "sigs.k8s.io/security-profiles-operator/api/grpc/metrics"
+	"sigs.k8s.io/security-profiles-operator/internal/pkg/daemon/enricher/types"
 )
 
 type FakeImpl struct {
-	AddToBacklogStub        func(*ttlcache.Cache, string, interface{}) error
+	AddToBacklogStub        func(*ttlcache.Cache[string, []*types.AuditLine], string, []*types.AuditLine)
 	addToBacklogMutex       sync.RWMutex
 	addToBacklogArgsForCall []struct {
-		arg1 *ttlcache.Cache
+		arg1 *ttlcache.Cache[string, []*types.AuditLine]
 		arg2 string
-		arg3 interface{}
-	}
-	addToBacklogReturns struct {
-		result1 error
-	}
-	addToBacklogReturnsOnCall map[int]struct {
-		result1 error
+		arg3 []*types.AuditLine
 	}
 	AuditIncStub        func(api_metrics.MetricsClient) (api_metrics.Metrics_AuditIncClient, error)
 	auditIncMutex       sync.RWMutex
@@ -84,10 +78,10 @@ type FakeImpl struct {
 	closeReturnsOnCall map[int]struct {
 		result1 error
 	}
-	ContainerIDForPIDStub        func(*ttlcache.Cache, int) (string, error)
+	ContainerIDForPIDStub        func(*ttlcache.Cache[string, string], int) (string, error)
 	containerIDForPIDMutex       sync.RWMutex
 	containerIDForPIDArgsForCall []struct {
-		arg1 *ttlcache.Cache
+		arg1 *ttlcache.Cache[string, string]
 		arg2 int
 	}
 	containerIDForPIDReturns struct {
@@ -112,31 +106,23 @@ type FakeImpl struct {
 		result2 context.CancelFunc
 		result3 error
 	}
-	FlushBacklogStub        func(*ttlcache.Cache, string) error
+	FlushBacklogStub        func(*ttlcache.Cache[string, []*types.AuditLine], string)
 	flushBacklogMutex       sync.RWMutex
 	flushBacklogArgsForCall []struct {
-		arg1 *ttlcache.Cache
+		arg1 *ttlcache.Cache[string, []*types.AuditLine]
 		arg2 string
 	}
-	flushBacklogReturns struct {
-		result1 error
-	}
-	flushBacklogReturnsOnCall map[int]struct {
-		result1 error
-	}
-	GetFromBacklogStub        func(*ttlcache.Cache, string) (interface{}, error)
+	GetFromBacklogStub        func(*ttlcache.Cache[string, []*types.AuditLine], string) []*types.AuditLine
 	getFromBacklogMutex       sync.RWMutex
 	getFromBacklogArgsForCall []struct {
-		arg1 *ttlcache.Cache
+		arg1 *ttlcache.Cache[string, []*types.AuditLine]
 		arg2 string
 	}
 	getFromBacklogReturns struct {
-		result1 interface{}
-		result2 error
+		result1 []*types.AuditLine
 	}
 	getFromBacklogReturnsOnCall map[int]struct {
-		result1 interface{}
-		result2 error
+		result1 []*types.AuditLine
 	}
 	GetenvStub        func(string) string
 	getenvMutex       sync.RWMutex
@@ -273,18 +259,6 @@ type FakeImpl struct {
 	serveReturnsOnCall map[int]struct {
 		result1 error
 	}
-	SetTTLStub        func(*ttlcache.Cache, time.Duration) error
-	setTTLMutex       sync.RWMutex
-	setTTLArgsForCall []struct {
-		arg1 *ttlcache.Cache
-		arg2 time.Duration
-	}
-	setTTLReturns struct {
-		result1 error
-	}
-	setTTLReturnsOnCall map[int]struct {
-		result1 error
-	}
 	StatStub        func(string) (fs.FileInfo, error)
 	statMutex       sync.RWMutex
 	statArgsForCall []struct {
@@ -316,25 +290,24 @@ type FakeImpl struct {
 	invocationsMutex sync.RWMutex
 }
 
-func (fake *FakeImpl) AddToBacklog(arg1 *ttlcache.Cache, arg2 string, arg3 interface{}) error {
+func (fake *FakeImpl) AddToBacklog(arg1 *ttlcache.Cache[string, []*types.AuditLine], arg2 string, arg3 []*types.AuditLine) {
+	var arg3Copy []*types.AuditLine
+	if arg3 != nil {
+		arg3Copy = make([]*types.AuditLine, len(arg3))
+		copy(arg3Copy, arg3)
+	}
 	fake.addToBacklogMutex.Lock()
-	ret, specificReturn := fake.addToBacklogReturnsOnCall[len(fake.addToBacklogArgsForCall)]
 	fake.addToBacklogArgsForCall = append(fake.addToBacklogArgsForCall, struct {
-		arg1 *ttlcache.Cache
+		arg1 *ttlcache.Cache[string, []*types.AuditLine]
 		arg2 string
-		arg3 interface{}
-	}{arg1, arg2, arg3})
+		arg3 []*types.AuditLine
+	}{arg1, arg2, arg3Copy})
 	stub := fake.AddToBacklogStub
-	fakeReturns := fake.addToBacklogReturns
-	fake.recordInvocation("AddToBacklog", []interface{}{arg1, arg2, arg3})
+	fake.recordInvocation("AddToBacklog", []interface{}{arg1, arg2, arg3Copy})
 	fake.addToBacklogMutex.Unlock()
 	if stub != nil {
-		return stub(arg1, arg2, arg3)
+		fake.AddToBacklogStub(arg1, arg2, arg3)
 	}
-	if specificReturn {
-		return ret.result1
-	}
-	return fakeReturns.result1
 }
 
 func (fake *FakeImpl) AddToBacklogCallCount() int {
@@ -343,42 +316,19 @@ func (fake *FakeImpl) AddToBacklogCallCount() int {
 	return len(fake.addToBacklogArgsForCall)
 }
 
-func (fake *FakeImpl) AddToBacklogCalls(stub func(*ttlcache.Cache, string, interface{}) error) {
+func (fake *FakeImpl) AddToBacklogCalls(stub func(*ttlcache.Cache[string, []*types.AuditLine], string, []*types.AuditLine)) {
 	fake.addToBacklogMutex.Lock()
 	defer fake.addToBacklogMutex.Unlock()
 	fake.AddToBacklogStub = stub
 }
 
-func (fake *FakeImpl) AddToBacklogArgsForCall(i int) (*ttlcache.Cache, string, interface{}) {
+func (fake *FakeImpl) AddToBacklogArgsForCall(i int) (*ttlcache.Cache[string, []*types.AuditLine], string, []*types.AuditLine) {
 	fake.addToBacklogMutex.RLock()
 	defer fake.addToBacklogMutex.RUnlock()
 	argsForCall := fake.addToBacklogArgsForCall[i]
 	return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
 }
 
-func (fake *FakeImpl) AddToBacklogReturns(result1 error) {
-	fake.addToBacklogMutex.Lock()
-	defer fake.addToBacklogMutex.Unlock()
-	fake.AddToBacklogStub = nil
-	fake.addToBacklogReturns = struct {
-		result1 error
-	}{result1}
-}
-
-func (fake *FakeImpl) AddToBacklogReturnsOnCall(i int, result1 error) {
-	fake.addToBacklogMutex.Lock()
-	defer fake.addToBacklogMutex.Unlock()
-	fake.AddToBacklogStub = nil
-	if fake.addToBacklogReturnsOnCall == nil {
-		fake.addToBacklogReturnsOnCall = make(map[int]struct {
-			result1 error
-		})
-	}
-	fake.addToBacklogReturnsOnCall[i] = struct {
-		result1 error
-	}{result1}
-}
-
 func (fake *FakeImpl) AuditInc(arg1 api_metrics.MetricsClient) (api_metrics.Metrics_AuditIncClient, error) {
 	fake.auditIncMutex.Lock()
 	ret, specificReturn := fake.auditIncReturnsOnCall[len(fake.auditIncArgsForCall)]
@@ -567,11 +517,11 @@ func (fake *FakeImpl) CloseReturnsOnCall(i int, result1 error) {
 	}{result1}
 }
 
-func (fake *FakeImpl) ContainerIDForPID(arg1 *ttlcache.Cache, arg2 int) (string, error) {
+func (fake *FakeImpl) ContainerIDForPID(arg1 *ttlcache.Cache[string, string], arg2 int) (string, error) {
 	fake.containerIDForPIDMutex.Lock()
 	ret, specificReturn := fake.containerIDForPIDReturnsOnCall[len(fake.containerIDForPIDArgsForCall)]
 	fake.containerIDForPIDArgsForCall = append(fake.containerIDForPIDArgsForCall, struct {
-		arg1 *ttlcache.Cache
+		arg1 *ttlcache.Cache[string, string]
 		arg2 int
 	}{arg1, arg2})
 	stub := fake.ContainerIDForPIDStub
@@ -593,13 +543,13 @@ func (fake *FakeImpl) ContainerIDForPIDCallCount() int {
 	return len(fake.containerIDForPIDArgsForCall)
 }
 
-func (fake *FakeImpl) ContainerIDForPIDCalls(stub func(*ttlcache.Cache, int) (string, error)) {
+func (fake *FakeImpl) ContainerIDForPIDCalls(stub func(*ttlcache.Cache[string, string], int) (string, error)) {
 	fake.containerIDForPIDMutex.Lock()
 	defer fake.containerIDForPIDMutex.Unlock()
 	fake.ContainerIDForPIDStub = stub
 }
 
-func (fake *FakeImpl) ContainerIDForPIDArgsForCall(i int) (*ttlcache.Cache, int) {
+func (fake *FakeImpl) ContainerIDForPIDArgsForCall(i int) (*ttlcache.Cache[string, string], int) {
 	fake.containerIDForPIDMutex.RLock()
 	defer fake.containerIDForPIDMutex.RUnlock()
 	argsForCall := fake.containerIDForPIDArgsForCall[i]
@@ -691,24 +641,18 @@ func (fake *FakeImpl) DialReturnsOnCall(i int, result1 *grpc.ClientConn, result2
 	}{result1, result2, result3}
 }
 
-func (fake *FakeImpl) FlushBacklog(arg1 *ttlcache.Cache, arg2 string) error {
+func (fake *FakeImpl) FlushBacklog(arg1 *ttlcache.Cache[string, []*types.AuditLine], arg2 string) {
 	fake.flushBacklogMutex.Lock()
-	ret, specificReturn := fake.flushBacklogReturnsOnCall[len(fake.flushBacklogArgsForCall)]
 	fake.flushBacklogArgsForCall = append(fake.flushBacklogArgsForCall, struct {
-		arg1 *ttlcache.Cache
+		arg1 *ttlcache.Cache[string, []*types.AuditLine]
 		arg2 string
 	}{arg1, arg2})
 	stub := fake.FlushBacklogStub
-	fakeReturns := fake.flushBacklogReturns
 	fake.recordInvocation("FlushBacklog", []interface{}{arg1, arg2})
 	fake.flushBacklogMutex.Unlock()
 	if stub != nil {
-		return stub(arg1, arg2)
-	}
-	if specificReturn {
-		return ret.result1
+		fake.FlushBacklogStub(arg1, arg2)
 	}
-	return fakeReturns.result1
 }
 
 func (fake *FakeImpl) FlushBacklogCallCount() int {
@@ -717,47 +661,24 @@ func (fake *FakeImpl) FlushBacklogCallCount() int {
 	return len(fake.flushBacklogArgsForCall)
 }
 
-func (fake *FakeImpl) FlushBacklogCalls(stub func(*ttlcache.Cache, string) error) {
+func (fake *FakeImpl) FlushBacklogCalls(stub func(*ttlcache.Cache[string, []*types.AuditLine], string)) {
 	fake.flushBacklogMutex.Lock()
 	defer fake.flushBacklogMutex.Unlock()
 	fake.FlushBacklogStub = stub
 }
 
-func (fake *FakeImpl) FlushBacklogArgsForCall(i int) (*ttlcache.Cache, string) {
+func (fake *FakeImpl) FlushBacklogArgsForCall(i int) (*ttlcache.Cache[string, []*types.AuditLine], string) {
 	fake.flushBacklogMutex.RLock()
 	defer fake.flushBacklogMutex.RUnlock()
 	argsForCall := fake.flushBacklogArgsForCall[i]
 	return argsForCall.arg1, argsForCall.arg2
 }
 
-func (fake *FakeImpl) FlushBacklogReturns(result1 error) {
-	fake.flushBacklogMutex.Lock()
-	defer fake.flushBacklogMutex.Unlock()
-	fake.FlushBacklogStub = nil
-	fake.flushBacklogReturns = struct {
-		result1 error
-	}{result1}
-}
-
-func (fake *FakeImpl) FlushBacklogReturnsOnCall(i int, result1 error) {
-	fake.flushBacklogMutex.Lock()
-	defer fake.flushBacklogMutex.Unlock()
-	fake.FlushBacklogStub = nil
-	if fake.flushBacklogReturnsOnCall == nil {
-		fake.flushBacklogReturnsOnCall = make(map[int]struct {
-			result1 error
-		})
-	}
-	fake.flushBacklogReturnsOnCall[i] = struct {
-		result1 error
-	}{result1}
-}
-
-func (fake *FakeImpl) GetFromBacklog(arg1 *ttlcache.Cache, arg2 string) (interface{}, error) {
+func (fake *FakeImpl) GetFromBacklog(arg1 *ttlcache.Cache[string, []*types.AuditLine], arg2 string) []*types.AuditLine {
 	fake.getFromBacklogMutex.Lock()
 	ret, specificReturn := fake.getFromBacklogReturnsOnCall[len(fake.getFromBacklogArgsForCall)]
 	fake.getFromBacklogArgsForCall = append(fake.getFromBacklogArgsForCall, struct {
-		arg1 *ttlcache.Cache
+		arg1 *ttlcache.Cache[string, []*types.AuditLine]
 		arg2 string
 	}{arg1, arg2})
 	stub := fake.GetFromBacklogStub
@@ -768,9 +689,9 @@ func (fake *FakeImpl) GetFromBacklog(arg1 *ttlcache.Cache, arg2 string) (interfa
 		return stub(arg1, arg2)
 	}
 	if specificReturn {
-		return ret.result1, ret.result2
+		return ret.result1
 	}
-	return fakeReturns.result1, fakeReturns.result2
+	return fakeReturns.result1
 }
 
 func (fake *FakeImpl) GetFromBacklogCallCount() int {
@@ -779,43 +700,40 @@ func (fake *FakeImpl) GetFromBacklogCallCount() int {
 	return len(fake.getFromBacklogArgsForCall)
 }
 
-func (fake *FakeImpl) GetFromBacklogCalls(stub func(*ttlcache.Cache, string) (interface{}, error)) {
+func (fake *FakeImpl) GetFromBacklogCalls(stub func(*ttlcache.Cache[string, []*types.AuditLine], string) []*types.AuditLine) {
 	fake.getFromBacklogMutex.Lock()
 	defer fake.getFromBacklogMutex.Unlock()
 	fake.GetFromBacklogStub = stub
 }
 
-func (fake *FakeImpl) GetFromBacklogArgsForCall(i int) (*ttlcache.Cache, string) {
+func (fake *FakeImpl) GetFromBacklogArgsForCall(i int) (*ttlcache.Cache[string, []*types.AuditLine], string) {
 	fake.getFromBacklogMutex.RLock()
 	defer fake.getFromBacklogMutex.RUnlock()
 	argsForCall := fake.getFromBacklogArgsForCall[i]
 	return argsForCall.arg1, argsForCall.arg2
 }
 
-func (fake *FakeImpl) GetFromBacklogReturns(result1 interface{}, result2 error) {
+func (fake *FakeImpl) GetFromBacklogReturns(result1 []*types.AuditLine) {
 	fake.getFromBacklogMutex.Lock()
 	defer fake.getFromBacklogMutex.Unlock()
 	fake.GetFromBacklogStub = nil
 	fake.getFromBacklogReturns = struct {
-		result1 interface{}
-		result2 error
-	}{result1, result2}
+		result1 []*types.AuditLine
+	}{result1}
 }
 
-func (fake *FakeImpl) GetFromBacklogReturnsOnCall(i int, result1 interface{}, result2 error) {
+func (fake *FakeImpl) GetFromBacklogReturnsOnCall(i int, result1 []*types.AuditLine) {
 	fake.getFromBacklogMutex.Lock()
 	defer fake.getFromBacklogMutex.Unlock()
 	fake.GetFromBacklogStub = nil
 	if fake.getFromBacklogReturnsOnCall == nil {
 		fake.getFromBacklogReturnsOnCall = make(map[int]struct {
-			result1 interface{}
-			result2 error
+			result1 []*types.AuditLine
 		})
 	}
 	fake.getFromBacklogReturnsOnCall[i] = struct {
-		result1 interface{}
-		result2 error
-	}{result1, result2}
+		result1 []*types.AuditLine
+	}{result1}
 }
 
 func (fake *FakeImpl) Getenv(arg1 string) string {
@@ -1500,68 +1418,6 @@ func (fake *FakeImpl) ServeReturnsOnCall(i int, result1 error) {
 	}{result1}
 }
 
-func (fake *FakeImpl) SetTTL(arg1 *ttlcache.Cache, arg2 time.Duration) error {
-	fake.setTTLMutex.Lock()
-	ret, specificReturn := fake.setTTLReturnsOnCall[len(fake.setTTLArgsForCall)]
-	fake.setTTLArgsForCall = append(fake.setTTLArgsForCall, struct {
-		arg1 *ttlcache.Cache
-		arg2 time.Duration
-	}{arg1, arg2})
-	stub := fake.SetTTLStub
-	fakeReturns := fake.setTTLReturns
-	fake.recordInvocation("SetTTL", []interface{}{arg1, arg2})
-	fake.setTTLMutex.Unlock()
-	if stub != nil {
-		return stub(arg1, arg2)
-	}
-	if specificReturn {
-		return ret.result1
-	}
-	return fakeReturns.result1
-}
-
-func (fake *FakeImpl) SetTTLCallCount() int {
-	fake.setTTLMutex.RLock()
-	defer fake.setTTLMutex.RUnlock()
-	return len(fake.setTTLArgsForCall)
-}
-
-func (fake *FakeImpl) SetTTLCalls(stub func(*ttlcache.Cache, time.Duration) error) {
-	fake.setTTLMutex.Lock()
-	defer fake.setTTLMutex.Unlock()
-	fake.SetTTLStub = stub
-}
-
-func (fake *FakeImpl) SetTTLArgsForCall(i int) (*ttlcache.Cache, time.Duration) {
-	fake.setTTLMutex.RLock()
-	defer fake.setTTLMutex.RUnlock()
-	argsForCall := fake.setTTLArgsForCall[i]
-	return argsForCall.arg1, argsForCall.arg2
-}
-
-func (fake *FakeImpl) SetTTLReturns(result1 error) {
-	fake.setTTLMutex.Lock()
-	defer fake.setTTLMutex.Unlock()
-	fake.SetTTLStub = nil
-	fake.setTTLReturns = struct {
-		result1 error
-	}{result1}
-}
-
-func (fake *FakeImpl) SetTTLReturnsOnCall(i int, result1 error) {
-	fake.setTTLMutex.Lock()
-	defer fake.setTTLMutex.Unlock()
-	fake.SetTTLStub = nil
-	if fake.setTTLReturnsOnCall == nil {
-		fake.setTTLReturnsOnCall = make(map[int]struct {
-			result1 error
-		})
-	}
-	fake.setTTLReturnsOnCall[i] = struct {
-		result1 error
-	}{result1}
-}
-
 func (fake *FakeImpl) Stat(arg1 string) (fs.FileInfo, error) {
 	fake.statMutex.Lock()
 	ret, specificReturn := fake.statReturnsOnCall[len(fake.statArgsForCall)]
@@ -1732,8 +1588,6 @@ func (fake *FakeImpl) Invocations() map[string][][]interface{} {
 	defer fake.sendMetricMutex.RUnlock()
 	fake.serveMutex.RLock()
 	defer fake.serveMutex.RUnlock()
-	fake.setTTLMutex.RLock()
-	defer fake.setTTLMutex.RUnlock()
 	fake.statMutex.RLock()
 	defer fake.statMutex.RUnlock()
 	fake.tailFileMutex.RLock()
diff --git a/internal/pkg/daemon/enricher/impl.go b/internal/pkg/daemon/enricher/impl.go
index 9478b89c0..1b6aad88e 100644
--- a/internal/pkg/daemon/enricher/impl.go
+++ b/internal/pkg/daemon/enricher/impl.go
@@ -20,9 +20,8 @@ import (
 	"context"
 	"net"
 	"os"
-	"time"
 
-	"github.com/ReneKroon/ttlcache/v2"
+	"github.com/jellydator/ttlcache/v3"
 	"github.com/nxadm/tail"
 	"google.golang.org/grpc"
 	v1 "k8s.io/api/core/v1"
@@ -31,6 +30,7 @@ import (
 	"k8s.io/client-go/rest"
 
 	api "sigs.k8s.io/security-profiles-operator/api/grpc/metrics"
+	"sigs.k8s.io/security-profiles-operator/internal/pkg/daemon/enricher/types"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/daemon/metrics"
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/util"
 )
@@ -40,14 +40,13 @@ type defaultImpl struct{}
 //go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -generate
 //counterfeiter:generate . impl
 type impl interface {
-	SetTTL(cache *ttlcache.Cache, ttl time.Duration) error
 	Getenv(key string) string
 	Dial() (*grpc.ClientConn, context.CancelFunc, error)
 	Close(*grpc.ClientConn) error
 	TailFile(filename string, config tail.Config) (*tail.Tail, error)
 	Lines(tailFile *tail.Tail) chan *tail.Line
 	Reason(tailFile *tail.Tail) error
-	ContainerIDForPID(cache *ttlcache.Cache, pid int) (string, error)
+	ContainerIDForPID(cache *ttlcache.Cache[string, string], pid int) (string, error)
 	InClusterConfig() (*rest.Config, error)
 	NewForConfig(c *rest.Config) (*kubernetes.Clientset, error)
 	ListPods(ctx context.Context, c kubernetes.Interface, nodeName string) (*v1.PodList, error)
@@ -56,18 +55,14 @@ type impl interface {
 	SendMetric(client api.Metrics_AuditIncClient, in *api.AuditRequest) error
 	Listen(string, string) (net.Listener, error)
 	Serve(*grpc.Server, net.Listener) error
-	AddToBacklog(cache *ttlcache.Cache, key string, data interface{}) error
-	GetFromBacklog(cache *ttlcache.Cache, key string) (interface{}, error)
-	FlushBacklog(cache *ttlcache.Cache, key string) error
+	AddToBacklog(cache *ttlcache.Cache[string, []*types.AuditLine], key string, value []*types.AuditLine)
+	GetFromBacklog(cache *ttlcache.Cache[string, []*types.AuditLine], key string) []*types.AuditLine
+	FlushBacklog(cache *ttlcache.Cache[string, []*types.AuditLine], key string)
 	Chown(string, int, int) error
 	Stat(string) (os.FileInfo, error)
 	RemoveAll(string) error
 }
 
-func (d *defaultImpl) SetTTL(cache *ttlcache.Cache, ttl time.Duration) error {
-	return cache.SetTTL(ttl)
-}
-
 func (d *defaultImpl) Getenv(key string) string {
 	return os.Getenv(key)
 }
@@ -94,7 +89,7 @@ func (d *defaultImpl) Reason(tailFile *tail.Tail) error {
 	return tailFile.Err()
 }
 
-func (d *defaultImpl) ContainerIDForPID(cache *ttlcache.Cache, pid int) (string, error) {
+func (d *defaultImpl) ContainerIDForPID(cache *ttlcache.Cache[string, string], pid int) (string, error) {
 	return util.ContainerIDForPID(cache, pid)
 }
 
@@ -138,21 +133,25 @@ func (d *defaultImpl) AuditInc(
 }
 
 func (d *defaultImpl) AddToBacklog(
-	cache *ttlcache.Cache, key string, value interface{},
-) error {
-	return cache.Set(key, value)
+	cache *ttlcache.Cache[string, []*types.AuditLine], key string, value []*types.AuditLine,
+) {
+	cache.Set(key, value, ttlcache.DefaultTTL)
 }
 
 func (d *defaultImpl) GetFromBacklog(
-	cache *ttlcache.Cache, key string,
-) (interface{}, error) {
-	return cache.Get(key)
+	cache *ttlcache.Cache[string, []*types.AuditLine], key string,
+) []*types.AuditLine {
+	item := cache.Get(key)
+	if item == nil {
+		return nil
+	}
+	return item.Value()
 }
 
 func (d *defaultImpl) FlushBacklog(
-	cache *ttlcache.Cache, key string,
-) error {
-	return cache.Remove(key)
+	cache *ttlcache.Cache[string, []*types.AuditLine], key string,
+) {
+	cache.Delete(key)
 }
 
 func (d *defaultImpl) SendMetric(
diff --git a/internal/pkg/daemon/enricher/types.go b/internal/pkg/daemon/enricher/types/types.go
similarity index 57%
rename from internal/pkg/daemon/enricher/types.go
rename to internal/pkg/daemon/enricher/types/types.go
index 8a054fa5a..949b9793b 100644
--- a/internal/pkg/daemon/enricher/types.go
+++ b/internal/pkg/daemon/enricher/types/types.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2020 The Kubernetes Authors.
+Copyright 2022 The Kubernetes Authors.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -14,35 +14,35 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package enricher
+package types
 
 const (
-	auditTypeSeccomp = "seccomp"
-	auditTypeSelinux = "selinux"
+	AuditTypeSeccomp = "seccomp"
+	AuditTypeSelinux = "selinux"
 )
 
-type auditLine struct {
-	auditType string
+type AuditLine struct {
+	AuditType string
 
 	// common
-	processID   int
-	timestampID string
+	ProcessID   int
+	TimestampID string
 
 	// seccomp
-	systemCallID int32
-	executable   string
+	SystemCallID int32
+	Executable   string
 
 	// selinux
-	scontext string
-	tcontext string
-	tclass   string
-	perm     string
+	Scontext string
+	Tcontext string
+	Tclass   string
+	Perm     string
 }
 
-type containerInfo struct {
-	podName       string
-	containerName string
-	namespace     string
-	containerID   string
-	recordProfile string
+type ContainerInfo struct {
+	PodName       string
+	ContainerName string
+	Namespace     string
+	ContainerID   string
+	RecordProfile string
 }
diff --git a/internal/pkg/util/containers.go b/internal/pkg/util/containers.go
index f7d767ee5..b6cd386f3 100644
--- a/internal/pkg/util/containers.go
+++ b/internal/pkg/util/containers.go
@@ -25,7 +25,7 @@ import (
 	"regexp"
 	"strconv"
 
-	"github.com/ReneKroon/ttlcache/v2"
+	"github.com/jellydator/ttlcache/v3"
 )
 
 var (
@@ -44,16 +44,11 @@ var (
 
 // ContainerIDForPID tries to find the 64 digit container ID for the provided
 // PID by using its cgroup. It supports caching via the cache argument.
-func ContainerIDForPID(cache ttlcache.SimpleCache, pid int) (string, error) {
+func ContainerIDForPID(cache *ttlcache.Cache[string, string], pid int) (string, error) {
 	// Check the cache first
-	if id, err := cache.Get(
-		strconv.Itoa(pid),
-	); !errors.Is(err, ttlcache.ErrNotFound) {
-		idString, ok := id.(string)
-		if !ok {
-			return "", errors.New("id is not a string")
-		}
-		return idString, nil
+	item := cache.Get(strconv.Itoa(pid))
+	if item != nil {
+		return item.Value(), nil
 	}
 
 	cgroupPath := fmt.Sprintf("/proc/%d/cgroup", pid)
@@ -76,12 +71,7 @@ func ContainerIDForPID(cache ttlcache.SimpleCache, pid int) (string, error) {
 
 		if containerID := ContainerIDRegex.FindString(text); containerID != "" {
 			// Update the cache
-			if err := cache.Set(
-				strconv.Itoa(pid), containerID,
-			); err != nil {
-				return "", fmt.Errorf("update cache: %w", err)
-			}
-
+			cache.Set(strconv.Itoa(pid), containerID, ttlcache.DefaultTTL)
 			return containerID, nil
 		}
 	}
diff --git a/vendor/github.com/ReneKroon/ttlcache/v2/.travis.yml b/vendor/github.com/ReneKroon/ttlcache/v2/.travis.yml
deleted file mode 100644
index 06007d369..000000000
--- a/vendor/github.com/ReneKroon/ttlcache/v2/.travis.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-language: go
-
-go:
-  - "1.17.x"
-  - "1.16.x"
-
-git:
-  depth: 1
-
-install:
-  - go install -race std
-  - go install golang.org/x/tools/cmd/cover
-  - go install golang.org/x/lint/golint
-  - export PATH=$HOME/gopath/bin:$PATH
-  - go get golang.org/x/tools/cmd/cover
-  - go get github.com/mattn/goveralls
-
-script:
-  - golint .
-  - go test -cover -race -count=1 -timeout=30s -run .
-  - go test -covermode=count -coverprofile=coverage.out -timeout=90s -run .
-  - if test ! -z "$COVERALLS_TOKEN"; then $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci -repotoken $COVERALLS_TOKEN; fi
-  - cd bench; go test -run=Bench.* -bench=. -benchmem; cd ..
\ No newline at end of file
diff --git a/vendor/github.com/ReneKroon/ttlcache/v2/Readme.md b/vendor/github.com/ReneKroon/ttlcache/v2/Readme.md
deleted file mode 100644
index 6bc07b8e9..000000000
--- a/vendor/github.com/ReneKroon/ttlcache/v2/Readme.md
+++ /dev/null
@@ -1,151 +0,0 @@
-# TTLCache - an in-memory cache with expiration
-
-[![Documentation](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/ReneKroon/ttlcache/v2)
-[![Release](https://img.shields.io/github/release/ReneKroon/ttlcache.svg?label=Release)](https://github.com/ReneKroon/ttlcache/releases)
-
-TTLCache is a simple key/value cache in golang with the following functions:
-
-1. Expiration of items based on time, or custom function
-2. Loader function to retrieve missing keys can be provided. Additional `Get` calls on the same key block while fetching is in progress (groupcache style).
-3. Individual expiring time or global expiring time, you can choose
-4. Auto-Extending expiration on `Get` -or- DNS style TTL, see `SkipTTLExtensionOnHit(bool)`
-5. Can trigger callback on key expiration
-6. Cleanup resources by calling `Close()` at end of lifecycle.
-7. Thread-safe with comprehensive testing suite. This code is in production at bol.com on critical systems.
-
-Note (issue #25): by default, due to historic reasons, the TTL will be reset on each cache hit and you need to explicitly configure the cache to use a TTL that will not get extended.
-
-[![Build Status](https://www.travis-ci.com/ReneKroon/ttlcache.svg?branch=master)](https://travis-ci.com/ReneKroon/ttlcache)
-[![Go Report Card](https://goreportcard.com/badge/github.com/ReneKroon/ttlcache)](https://goreportcard.com/report/github.com/ReneKroon/ttlcache)
-[![Coverage Status](https://coveralls.io/repos/github/ReneKroon/ttlcache/badge.svg?branch=master)](https://coveralls.io/github/ReneKroon/ttlcache?branch=master)
-[![GitHub issues](https://img.shields.io/github/issues/ReneKroon/ttlcache.svg)](https://github.com/ReneKroon/ttlcache/issues)
-[![license](https://img.shields.io/github/license/ReneKroon/ttlcache.svg?maxAge=2592000)](https://github.com/ReneKroon/ttlcache/LICENSE)
-
-## Usage
-
-`go get github.com/ReneKroon/ttlcache/v2`
-
-You can copy it as a full standalone demo program. The first snippet is basic usage, where the second exploits more options in the cache.
-
-Basic:
-```go
-package main
-
-import (
-	"fmt"
-	"time"
-
-	"github.com/ReneKroon/ttlcache/v2"
-)
-
-var notFound = ttlcache.ErrNotFound
-
-func main() {
-	var cache ttlcache.SimpleCache = ttlcache.NewCache()
-
-	cache.SetTTL(time.Duration(10 * time.Second))
-	cache.Set("MyKey", "MyValue")
-	cache.Set("MyNumber", 1000)
-
-	if val, err := cache.Get("MyKey"); err != notFound {
-		fmt.Printf("Got it: %s\n", val)
-	}
-
-	cache.Remove("MyNumber")
-	cache.Purge()
-	cache.Close()
-}
-```
-
-Advanced:
-```go
-package main
-
-import (
-	"fmt"
-	"time"
-
-	"github.com/ReneKroon/ttlcache/v2"
-)
-
-var (
-	notFound = ttlcache.ErrNotFound
-	isClosed = ttlcache.ErrClosed
-)
-
-func main() {
-	newItemCallback := func(key string, value interface{}) {
-		fmt.Printf("New key(%s) added\n", key)
-	}
-	checkExpirationCallback := func(key string, value interface{}) bool {
-		if key == "key1" {
-			// if the key equals "key1", the value
-			// will not be allowed to expire
-			return false
-		}
-		// all other values are allowed to expire
-		return true
-	}
-
-	expirationCallback := func(key string, reason ttlcache.EvictionReason, value interface{}) {
-		fmt.Printf("This key(%s) has expired because of %s\n", key, reason)
-	}
-
-	loaderFunction := func(key string) (data interface{}, ttl time.Duration, err error) {
-		ttl = time.Second * 300
-		data, err = getFromNetwork(key)
-
-		return data, ttl, err
-	}
-
-	cache := ttlcache.NewCache()
-	cache.SetTTL(time.Duration(10 * time.Second))
-	cache.SetExpirationReasonCallback(expirationCallback)
-	cache.SetLoaderFunction(loaderFunction)
-	cache.SetNewItemCallback(newItemCallback)
-	cache.SetCheckExpirationCallback(checkExpirationCallback)
-	cache.SetCacheSizeLimit(2)
-
-	cache.Set("key", "value")
-	cache.SetWithTTL("keyWithTTL", "value", 10*time.Second)
-
-	if value, exists := cache.Get("key"); exists == nil {
-		fmt.Printf("Got value: %v\n", value)
-	}
-	count := cache.Count()
-	if result := cache.Remove("keyNNN"); result == notFound {
-		fmt.Printf("Not found, %d items left\n", count)
-	}
-
-	cache.Set("key6", "value")
-	cache.Set("key7", "value")
-	metrics := cache.GetMetrics()
-	fmt.Printf("Total inserted: %d\n", metrics.Inserted)
-
-	cache.Close()
-
-}
-
-func getFromNetwork(key string) (string, error) {
-	time.Sleep(time.Millisecond * 30)
-	return "value", nil
-}
-```
-
-### TTLCache - Some design considerations
-
-1. The complexity of the current cache is already quite high. Therefore not all requests can be implemented in a straight-forward manner.
-2. The locking should be done only in the exported functions and `startExpirationProcessing` of the Cache struct. Else data races can occur or recursive locks are needed, which are both unwanted.
-3. I prefer correct functionality over fast tests. It's ok for new tests to take seconds to proof something.
-
-### Original Project
-
-TTLCache was forked from [wunderlist/ttlcache](https://github.com/wunderlist/ttlcache) to add extra functions not avaiable in the original scope.
-The main differences are:
-
-1. A item can store any kind of object, previously, only strings could be saved
-2. Optionally, you can add callbacks too: check if a value should expire, be notified if a value expires, and be notified when new values are added to the cache
-3. The expiration can be either global or per item
-4. Items can exist without expiration time (time.Zero)
-5. Expirations and callbacks are realtime. Don't have a pooling time to check anymore, now it's done with a heap.
-6. A cache count limiter
diff --git a/vendor/github.com/ReneKroon/ttlcache/v2/cache.go b/vendor/github.com/ReneKroon/ttlcache/v2/cache.go
deleted file mode 100644
index 4b2f8201f..000000000
--- a/vendor/github.com/ReneKroon/ttlcache/v2/cache.go
+++ /dev/null
@@ -1,605 +0,0 @@
-package ttlcache
-
-import (
-	"sync"
-	"time"
-
-	"golang.org/x/sync/singleflight"
-)
-
-// CheckExpireCallback is used as a callback for an external check on item expiration
-type CheckExpireCallback func(key string, value interface{}) bool
-
-// ExpireCallback is used as a callback on item expiration or when notifying of an item new to the cache
-// Note that ExpireReasonCallback will be the successor of this function in the next major release.
-type ExpireCallback func(key string, value interface{})
-
-// ExpireReasonCallback is used as a callback on item expiration with extra information why the item expired.
-type ExpireReasonCallback func(key string, reason EvictionReason, value interface{})
-
-// LoaderFunction can be supplied to retrieve an item where a cache miss occurs. Supply an item specific ttl or Duration.Zero
-type LoaderFunction func(key string) (data interface{}, ttl time.Duration, err error)
-
-// SimpleCache interface enables a quick-start. Interface for basic usage.
-type SimpleCache interface {
-	Get(key string) (interface{}, error)
-	GetWithTTL(key string) (interface{}, time.Duration, error)
-	Set(key string, data interface{}) error
-	SetTTL(ttl time.Duration) error
-	SetWithTTL(key string, data interface{}, ttl time.Duration) error
-	Remove(key string) error
-	Close() error
-	Purge() error
-}
-
-// Cache is a synchronized map of items that can auto-expire once stale
-type Cache struct {
-	// mutex is shared for all operations that need to be safe
-	mutex sync.Mutex
-	// ttl is the global ttl for the cache, can be zero (is infinite)
-	ttl time.Duration
-	// actual item storage
-	items map[string]*item
-	// lock used to avoid fetching a remote item multiple times
-	loaderLock           *singleflight.Group
-	expireCallback       ExpireCallback
-	expireReasonCallback ExpireReasonCallback
-	checkExpireCallback  CheckExpireCallback
-	newItemCallback      ExpireCallback
-	// the queue is used to have an ordered structure to use for expiration and cleanup.
-	priorityQueue          *priorityQueue
-	expirationNotification chan bool
-	// hasNotified is used to not schedule new expiration processing when an request is already pending.
-	hasNotified      bool
-	expirationTime   time.Time
-	skipTTLExtension bool
-	shutdownSignal   chan (chan struct{})
-	isShutDown       bool
-	loaderFunction   LoaderFunction
-	sizeLimit        int
-	metrics          Metrics
-}
-
-// EvictionReason is an enum that explains why an item was evicted
-type EvictionReason int
-
-const (
-	// Removed : explicitly removed from cache via API call
-	Removed EvictionReason = iota
-	// EvictedSize : evicted due to exceeding the cache size
-	EvictedSize
-	// Expired : the time to live is zero and therefore the item is removed
-	Expired
-	// Closed : the cache was closed
-	Closed
-)
-
-const (
-	// ErrClosed is raised when operating on a cache where Close() has already been called.
-	ErrClosed = constError("cache already closed")
-	// ErrNotFound indicates that the requested key is not present in the cache
-	ErrNotFound = constError("key not found")
-)
-
-type constError string
-
-func (err constError) Error() string {
-	return string(err)
-}
-
-func (cache *Cache) getItem(key string) (*item, bool, bool) {
-	item, exists := cache.items[key]
-	if !exists || item.expired() {
-		return nil, false, false
-	}
-
-	// no need to change priority queue when skipTTLExtension is true or the item will not expire
-	if cache.skipTTLExtension || (item.ttl == 0 && cache.ttl == 0) {
-		return item, true, false
-	}
-
-	if item.ttl == 0 {
-		item.ttl = cache.ttl
-	}
-
-	item.touch()
-
-	oldExpireTime := cache.priorityQueue.root().expireAt
-	cache.priorityQueue.update(item)
-	nowExpireTime := cache.priorityQueue.root().expireAt
-
-	expirationNotification := false
-
-	// notify expiration only if the latest expire time is changed
-	if (oldExpireTime.IsZero() && !nowExpireTime.IsZero()) || oldExpireTime.After(nowExpireTime) {
-		expirationNotification = true
-	}
-	return item, exists, expirationNotification
-}
-
-func (cache *Cache) startExpirationProcessing() {
-	timer := time.NewTimer(time.Hour)
-	for {
-		var sleepTime time.Duration
-		cache.mutex.Lock()
-		cache.hasNotified = false
-		if cache.priorityQueue.Len() > 0 {
-			sleepTime = time.Until(cache.priorityQueue.root().expireAt)
-			if sleepTime < 0 && cache.priorityQueue.root().expireAt.IsZero() {
-				sleepTime = time.Hour
-			} else if sleepTime < 0 {
-				sleepTime = time.Microsecond
-			}
-			if cache.ttl > 0 {
-				sleepTime = min(sleepTime, cache.ttl)
-			}
-
-		} else if cache.ttl > 0 {
-			sleepTime = cache.ttl
-		} else {
-			sleepTime = time.Hour
-		}
-
-		cache.expirationTime = time.Now().Add(sleepTime)
-		cache.mutex.Unlock()
-
-		timer.Reset(sleepTime)
-		select {
-		case shutdownFeedback := <-cache.shutdownSignal:
-			timer.Stop()
-			cache.mutex.Lock()
-			if cache.priorityQueue.Len() > 0 {
-				cache.evictjob(Closed)
-			}
-			cache.mutex.Unlock()
-			shutdownFeedback <- struct{}{}
-			return
-		case <-timer.C:
-			timer.Stop()
-			cache.mutex.Lock()
-			if cache.priorityQueue.Len() == 0 {
-				cache.mutex.Unlock()
-				continue
-			}
-
-			cache.cleanjob()
-			cache.mutex.Unlock()
-
-		case <-cache.expirationNotification:
-			timer.Stop()
-			continue
-		}
-	}
-}
-
-func (cache *Cache) checkExpirationCallback(item *item, reason EvictionReason) {
-	if cache.expireCallback != nil {
-		go cache.expireCallback(item.key, item.data)
-	}
-	if cache.expireReasonCallback != nil {
-		go cache.expireReasonCallback(item.key, reason, item.data)
-	}
-}
-
-func (cache *Cache) removeItem(item *item, reason EvictionReason) {
-	cache.metrics.Evicted++
-	cache.checkExpirationCallback(item, reason)
-	cache.priorityQueue.remove(item)
-	delete(cache.items, item.key)
-}
-
-func (cache *Cache) evictjob(reason EvictionReason) {
-	// index will only be advanced if the current entry will not be evicted
-	i := 0
-	for item := cache.priorityQueue.items[i]; ; item = cache.priorityQueue.items[i] {
-
-		cache.removeItem(item, reason)
-		if cache.priorityQueue.Len() == 0 {
-			return
-		}
-	}
-}
-
-func (cache *Cache) cleanjob() {
-	// index will only be advanced if the current entry will not be evicted
-	i := 0
-	for item := cache.priorityQueue.items[i]; item.expired(); item = cache.priorityQueue.items[i] {
-
-		if cache.checkExpireCallback != nil {
-			if !cache.checkExpireCallback(item.key, item.data) {
-				item.touch()
-				cache.priorityQueue.update(item)
-				i++
-				if i == cache.priorityQueue.Len() {
-					break
-				}
-				continue
-			}
-		}
-
-		cache.removeItem(item, Expired)
-		if cache.priorityQueue.Len() == 0 {
-			return
-		}
-	}
-}
-
-// Close calls Purge after stopping the goroutine that does ttl checking, for a clean shutdown.
-// The cache is no longer cleaning up after the first call to Close, repeated calls are safe and return ErrClosed.
-func (cache *Cache) Close() error {
-	cache.mutex.Lock()
-	if !cache.isShutDown {
-		cache.isShutDown = true
-		cache.mutex.Unlock()
-		feedback := make(chan struct{})
-		cache.shutdownSignal <- feedback
-		<-feedback
-		close(cache.shutdownSignal)
-		cache.Purge()
-	} else {
-		cache.mutex.Unlock()
-		return ErrClosed
-	}
-	return nil
-}
-
-// Set is a thread-safe way to add new items to the map.
-func (cache *Cache) Set(key string, data interface{}) error {
-	return cache.SetWithTTL(key, data, ItemExpireWithGlobalTTL)
-}
-
-// SetWithTTL is a thread-safe way to add new items to the map with individual ttl.
-func (cache *Cache) SetWithTTL(key string, data interface{}, ttl time.Duration) error {
-	cache.mutex.Lock()
-	if cache.isShutDown {
-		cache.mutex.Unlock()
-		return ErrClosed
-	}
-	item, exists, _ := cache.getItem(key)
-
-	oldExpireTime := time.Time{}
-	if !cache.priorityQueue.isEmpty() {
-		oldExpireTime = cache.priorityQueue.root().expireAt
-	}
-
-	if exists {
-		item.data = data
-		item.ttl = ttl
-	} else {
-		if cache.sizeLimit != 0 && len(cache.items) >= cache.sizeLimit {
-			cache.removeItem(cache.priorityQueue.items[0], EvictedSize)
-		}
-		item = newItem(key, data, ttl)
-		cache.items[key] = item
-	}
-	cache.metrics.Inserted++
-
-	if item.ttl == 0 {
-		item.ttl = cache.ttl
-	}
-
-	item.touch()
-
-	if exists {
-		cache.priorityQueue.update(item)
-	} else {
-		cache.priorityQueue.push(item)
-	}
-
-	nowExpireTime := cache.priorityQueue.root().expireAt
-
-	cache.mutex.Unlock()
-	if !exists && cache.newItemCallback != nil {
-		cache.newItemCallback(key, data)
-	}
-
-	// notify expiration only if the latest expire time is changed
-	if (oldExpireTime.IsZero() && !nowExpireTime.IsZero()) || oldExpireTime.After(nowExpireTime) {
-		cache.notifyExpiration()
-	}
-	return nil
-}
-
-// Get is a thread-safe way to lookup items
-// Every lookup, also touches the item, hence extending its life
-func (cache *Cache) Get(key string) (interface{}, error) {
-	return cache.GetByLoader(key, nil)
-}
-
-// GetWithTTL has exactly the same behaviour as Get but also returns
-// the remaining TTL for a specific item at the moment its retrieved
-func (cache *Cache) GetWithTTL(key string) (interface{}, time.Duration, error) {
-	return cache.GetByLoaderWithTtl(key, nil)
-}
-
-// GetByLoader can take a per key loader function (i.e. to propagate context)
-func (cache *Cache) GetByLoader(key string, customLoaderFunction LoaderFunction) (interface{}, error) {
-	dataToReturn, _, err := cache.GetByLoaderWithTtl(key, customLoaderFunction)
-
-	return dataToReturn, err
-}
-
-// GetByLoaderWithTtl can take a per key loader function (i.e. to propagate context)
-func (cache *Cache) GetByLoaderWithTtl(key string, customLoaderFunction LoaderFunction) (interface{}, time.Duration, error) {
-	cache.mutex.Lock()
-	if cache.isShutDown {
-		cache.mutex.Unlock()
-		return nil, 0, ErrClosed
-	}
-
-	cache.metrics.Hits++
-	item, exists, triggerExpirationNotification := cache.getItem(key)
-
-	var dataToReturn interface{}
-	ttlToReturn := time.Duration(0)
-	if exists {
-		cache.metrics.Retrievals++
-		dataToReturn = item.data
-		if !cache.skipTTLExtension {
-			ttlToReturn = item.ttl
-		} else {
-			ttlToReturn = time.Until(item.expireAt)
-		}
-		if ttlToReturn < 0 {
-			ttlToReturn = 0
-		}
-	}
-
-	var err error
-	if !exists {
-		cache.metrics.Misses++
-		err = ErrNotFound
-	}
-
-	loaderFunction := cache.loaderFunction
-	if customLoaderFunction != nil {
-		loaderFunction = customLoaderFunction
-	}
-
-	if loaderFunction == nil || exists {
-		cache.mutex.Unlock()
-	}
-
-	if loaderFunction != nil && !exists {
-		type loaderResult struct {
-			data interface{}
-			ttl  time.Duration
-		}
-		ch := cache.loaderLock.DoChan(key, func() (interface{}, error) {
-			// cache is not blocked during io
-			invokeData, ttl, err := cache.invokeLoader(key, loaderFunction)
-			lr := &loaderResult{
-				data: invokeData,
-				ttl:  ttl,
-			}
-			return lr, err
-		})
-		cache.mutex.Unlock()
-		res := <-ch
-		dataToReturn = res.Val.(*loaderResult).data
-		ttlToReturn = res.Val.(*loaderResult).ttl
-		err = res.Err
-	}
-
-	if triggerExpirationNotification {
-		cache.notifyExpiration()
-	}
-
-	return dataToReturn, ttlToReturn, err
-}
-
-func (cache *Cache) notifyExpiration() {
-	cache.mutex.Lock()
-	if cache.hasNotified {
-		cache.mutex.Unlock()
-		return
-	}
-	cache.hasNotified = true
-	cache.mutex.Unlock()
-
-	cache.expirationNotification <- true
-}
-
-func (cache *Cache) invokeLoader(key string, loaderFunction LoaderFunction) (dataToReturn interface{}, ttl time.Duration, err error) {
-	dataToReturn, ttl, err = loaderFunction(key)
-	if err == nil {
-		err = cache.SetWithTTL(key, dataToReturn, ttl)
-		if err != nil {
-			dataToReturn = nil
-		}
-	}
-	return dataToReturn, ttl, err
-}
-
-// Remove removes an item from the cache if it exists, triggers expiration callback when set. Can return ErrNotFound if the entry was not present.
-func (cache *Cache) Remove(key string) error {
-	cache.mutex.Lock()
-	defer cache.mutex.Unlock()
-	if cache.isShutDown {
-		return ErrClosed
-	}
-
-	object, exists := cache.items[key]
-	if !exists {
-		return ErrNotFound
-	}
-	cache.removeItem(object, Removed)
-
-	return nil
-}
-
-// Count returns the number of items in the cache. Returns zero when the cache has been closed.
-func (cache *Cache) Count() int {
-	cache.mutex.Lock()
-	defer cache.mutex.Unlock()
-
-	if cache.isShutDown {
-		return 0
-	}
-	length := len(cache.items)
-	return length
-}
-
-// GetKeys returns all keys of items in the cache. Returns nil when the cache has been closed.
-func (cache *Cache) GetKeys() []string {
-	cache.mutex.Lock()
-	defer cache.mutex.Unlock()
-
-	if cache.isShutDown {
-		return nil
-	}
-	keys := make([]string, len(cache.items))
-	i := 0
-	for k := range cache.items {
-		keys[i] = k
-		i++
-	}
-	return keys
-}
-
-// GetItems returns a copy of all items in the cache. Returns nil when the cache has been closed.
-func (cache *Cache) GetItems() map[string]interface{} {
-	cache.mutex.Lock()
-	defer cache.mutex.Unlock()
-
-	if cache.isShutDown {
-		return nil
-	}
-	items := make(map[string]interface{}, len(cache.items))
-	for k := range cache.items {
-		item, exists, _ := cache.getItem(k)
-		if exists {
-			items[k] = item.data
-		}
-	}
-	return items
-}
-
-// SetTTL sets the global TTL value for items in the cache, which can be overridden at the item level.
-func (cache *Cache) SetTTL(ttl time.Duration) error {
-	cache.mutex.Lock()
-
-	if cache.isShutDown {
-		cache.mutex.Unlock()
-		return ErrClosed
-	}
-	cache.ttl = ttl
-	cache.mutex.Unlock()
-	cache.notifyExpiration()
-	return nil
-}
-
-// SetExpirationCallback sets a callback that will be called when an item expires
-func (cache *Cache) SetExpirationCallback(callback ExpireCallback) {
-	cache.mutex.Lock()
-	defer cache.mutex.Unlock()
-	cache.expireCallback = callback
-}
-
-// SetExpirationReasonCallback sets a callback that will be called when an item expires, includes reason of expiry
-func (cache *Cache) SetExpirationReasonCallback(callback ExpireReasonCallback) {
-	cache.mutex.Lock()
-	defer cache.mutex.Unlock()
-	cache.expireReasonCallback = callback
-}
-
-// SetCheckExpirationCallback sets a callback that will be called when an item is about to expire
-// in order to allow external code to decide whether the item expires or remains for another TTL cycle
-func (cache *Cache) SetCheckExpirationCallback(callback CheckExpireCallback) {
-	cache.mutex.Lock()
-	defer cache.mutex.Unlock()
-	cache.checkExpireCallback = callback
-}
-
-// SetNewItemCallback sets a callback that will be called when a new item is added to the cache
-func (cache *Cache) SetNewItemCallback(callback ExpireCallback) {
-	cache.mutex.Lock()
-	defer cache.mutex.Unlock()
-	cache.newItemCallback = callback
-}
-
-// SkipTTLExtensionOnHit allows the user to change the cache behaviour. When this flag is set to true it will
-// no longer extend TTL of items when they are retrieved using Get, or when their expiration condition is evaluated
-// using SetCheckExpirationCallback.
-func (cache *Cache) SkipTTLExtensionOnHit(value bool) {
-	cache.mutex.Lock()
-	defer cache.mutex.Unlock()
-	cache.skipTTLExtension = value
-}
-
-// SetLoaderFunction allows you to set a function to retrieve cache misses. The signature matches that of the Get function.
-// Additional Get calls on the same key block while fetching is in progress (groupcache style).
-func (cache *Cache) SetLoaderFunction(loader LoaderFunction) {
-	cache.mutex.Lock()
-	defer cache.mutex.Unlock()
-	cache.loaderFunction = loader
-}
-
-// Purge will remove all entries
-func (cache *Cache) Purge() error {
-	cache.mutex.Lock()
-	defer cache.mutex.Unlock()
-	if cache.isShutDown {
-		return ErrClosed
-	}
-	cache.metrics.Evicted += int64(len(cache.items))
-	cache.items = make(map[string]*item)
-	cache.priorityQueue = newPriorityQueue()
-	return nil
-}
-
-// SetCacheSizeLimit sets a limit to the amount of cached items.
-// If a new item is getting cached, the closes item to being timed out will be replaced
-// Set to 0 to turn off
-func (cache *Cache) SetCacheSizeLimit(limit int) {
-	cache.mutex.Lock()
-	defer cache.mutex.Unlock()
-	cache.sizeLimit = limit
-}
-
-// NewCache is a helper to create instance of the Cache struct
-func NewCache() *Cache {
-
-	shutdownChan := make(chan chan struct{})
-
-	cache := &Cache{
-		items:                  make(map[string]*item),
-		loaderLock:             &singleflight.Group{},
-		priorityQueue:          newPriorityQueue(),
-		expirationNotification: make(chan bool, 1),
-		expirationTime:         time.Now(),
-		shutdownSignal:         shutdownChan,
-		isShutDown:             false,
-		loaderFunction:         nil,
-		sizeLimit:              0,
-		metrics:                Metrics{},
-	}
-	go cache.startExpirationProcessing()
-	return cache
-}
-
-// GetMetrics exposes the metrics of the cache. This is a snapshot copy of the metrics.
-func (cache *Cache) GetMetrics() Metrics {
-	cache.mutex.Lock()
-	defer cache.mutex.Unlock()
-	return cache.metrics
-}
-
-// Touch resets the TTL of the key when it exists, returns ErrNotFound if the key is not present.
-func (cache *Cache) Touch(key string) error {
-	cache.mutex.Lock()
-	defer cache.mutex.Unlock()
-	item, exists := cache.items[key]
-	if !exists {
-		return ErrNotFound
-	}
-	item.touch()
-	return nil
-}
-
-func min(duration time.Duration, second time.Duration) time.Duration {
-	if duration < second {
-		return duration
-	}
-	return second
-}
diff --git a/vendor/github.com/ReneKroon/ttlcache/v2/evictionreason_enumer.go b/vendor/github.com/ReneKroon/ttlcache/v2/evictionreason_enumer.go
deleted file mode 100644
index dcff95d64..000000000
--- a/vendor/github.com/ReneKroon/ttlcache/v2/evictionreason_enumer.go
+++ /dev/null
@@ -1,52 +0,0 @@
-// Code generated by "enumer -type EvictionReason"; DO NOT EDIT.
-
-//
-package ttlcache
-
-import (
-	"fmt"
-)
-
-const _EvictionReasonName = "RemovedEvictedSizeExpiredClosed"
-
-var _EvictionReasonIndex = [...]uint8{0, 7, 18, 25, 31}
-
-func (i EvictionReason) String() string {
-	if i < 0 || i >= EvictionReason(len(_EvictionReasonIndex)-1) {
-		return fmt.Sprintf("EvictionReason(%d)", i)
-	}
-	return _EvictionReasonName[_EvictionReasonIndex[i]:_EvictionReasonIndex[i+1]]
-}
-
-var _EvictionReasonValues = []EvictionReason{0, 1, 2, 3}
-
-var _EvictionReasonNameToValueMap = map[string]EvictionReason{
-	_EvictionReasonName[0:7]:   0,
-	_EvictionReasonName[7:18]:  1,
-	_EvictionReasonName[18:25]: 2,
-	_EvictionReasonName[25:31]: 3,
-}
-
-// EvictionReasonString retrieves an enum value from the enum constants string name.
-// Throws an error if the param is not part of the enum.
-func EvictionReasonString(s string) (EvictionReason, error) {
-	if val, ok := _EvictionReasonNameToValueMap[s]; ok {
-		return val, nil
-	}
-	return 0, fmt.Errorf("%s does not belong to EvictionReason values", s)
-}
-
-// EvictionReasonValues returns all values of the enum
-func EvictionReasonValues() []EvictionReason {
-	return _EvictionReasonValues
-}
-
-// IsAEvictionReason returns "true" if the value is listed in the enum definition. "false" otherwise
-func (i EvictionReason) IsAEvictionReason() bool {
-	for _, v := range _EvictionReasonValues {
-		if i == v {
-			return true
-		}
-	}
-	return false
-}
diff --git a/vendor/github.com/ReneKroon/ttlcache/v2/item.go b/vendor/github.com/ReneKroon/ttlcache/v2/item.go
deleted file mode 100644
index 2f78f49cc..000000000
--- a/vendor/github.com/ReneKroon/ttlcache/v2/item.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package ttlcache
-
-import (
-	"time"
-)
-
-const (
-	// ItemNotExpire Will avoid the item being expired by TTL, but can still be exired by callback etc.
-	ItemNotExpire time.Duration = -1
-	// ItemExpireWithGlobalTTL will use the global TTL when set.
-	ItemExpireWithGlobalTTL time.Duration = 0
-)
-
-func newItem(key string, data interface{}, ttl time.Duration) *item {
-	item := &item{
-		data: data,
-		ttl:  ttl,
-		key:  key,
-	}
-	// since nobody is aware yet of this item, it's safe to touch without lock here
-	item.touch()
-	return item
-}
-
-type item struct {
-	key        string
-	data       interface{}
-	ttl        time.Duration
-	expireAt   time.Time
-	queueIndex int
-}
-
-// Reset the item expiration time
-func (item *item) touch() {
-	if item.ttl > 0 {
-		item.expireAt = time.Now().Add(item.ttl)
-	}
-}
-
-// Verify if the item is expired
-func (item *item) expired() bool {
-	if item.ttl <= 0 {
-		return false
-	}
-	return item.expireAt.Before(time.Now())
-}
diff --git a/vendor/github.com/ReneKroon/ttlcache/v2/metrics.go b/vendor/github.com/ReneKroon/ttlcache/v2/metrics.go
deleted file mode 100644
index 5f672b12d..000000000
--- a/vendor/github.com/ReneKroon/ttlcache/v2/metrics.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package ttlcache
-
-// Metrics contains common cache metrics so you can calculate hit and miss rates
-type Metrics struct {
-	// succesful inserts
-	Inserted int64
-	// retrieval attempts
-	Retrievals int64
-	// all get calls that were in the cache (excludes loader invocations)
-	Hits int64
-	// entries not in cache (includes loader invocations)
-	Misses int64
-	// items removed from the cache in any way
-	Evicted int64
-}
diff --git a/vendor/github.com/ReneKroon/ttlcache/v2/priority_queue.go b/vendor/github.com/ReneKroon/ttlcache/v2/priority_queue.go
deleted file mode 100644
index 5d4054886..000000000
--- a/vendor/github.com/ReneKroon/ttlcache/v2/priority_queue.go
+++ /dev/null
@@ -1,85 +0,0 @@
-package ttlcache
-
-import (
-	"container/heap"
-)
-
-func newPriorityQueue() *priorityQueue {
-	queue := &priorityQueue{}
-	heap.Init(queue)
-	return queue
-}
-
-type priorityQueue struct {
-	items []*item
-}
-
-func (pq *priorityQueue) isEmpty() bool {
-	return len(pq.items) == 0
-}
-
-func (pq *priorityQueue) root() *item {
-	if len(pq.items) == 0 {
-		return nil
-	}
-
-	return pq.items[0]
-}
-
-func (pq *priorityQueue) update(item *item) {
-	heap.Fix(pq, item.queueIndex)
-}
-
-func (pq *priorityQueue) push(item *item) {
-	heap.Push(pq, item)
-}
-
-func (pq *priorityQueue) pop() *item {
-	if pq.Len() == 0 {
-		return nil
-	}
-	return heap.Pop(pq).(*item)
-}
-
-func (pq *priorityQueue) remove(item *item) {
-	heap.Remove(pq, item.queueIndex)
-}
-
-func (pq priorityQueue) Len() int {
-	length := len(pq.items)
-	return length
-}
-
-// Less will consider items with time.Time default value (epoch start) as more than set items.
-func (pq priorityQueue) Less(i, j int) bool {
-	if pq.items[i].expireAt.IsZero() {
-		return false
-	}
-	if pq.items[j].expireAt.IsZero() {
-		return true
-	}
-	return pq.items[i].expireAt.Before(pq.items[j].expireAt)
-}
-
-func (pq priorityQueue) Swap(i, j int) {
-	pq.items[i], pq.items[j] = pq.items[j], pq.items[i]
-	pq.items[i].queueIndex = i
-	pq.items[j].queueIndex = j
-}
-
-func (pq *priorityQueue) Push(x interface{}) {
-	item := x.(*item)
-	item.queueIndex = len(pq.items)
-	pq.items = append(pq.items, item)
-}
-
-func (pq *priorityQueue) Pop() interface{} {
-	old := pq.items
-	n := len(old)
-	item := old[n-1]
-	item.queueIndex = -1
-	// de-reference the element to be popped for Garbage Collector to de-allocate the memory
-	old[n-1] = nil
-	pq.items = old[0 : n-1]
-	return item
-}
diff --git a/vendor/github.com/ReneKroon/ttlcache/v2/CHANGELOG.md b/vendor/github.com/jellydator/ttlcache/v3/CHANGELOG.md
similarity index 100%
rename from vendor/github.com/ReneKroon/ttlcache/v2/CHANGELOG.md
rename to vendor/github.com/jellydator/ttlcache/v3/CHANGELOG.md
diff --git a/vendor/github.com/ReneKroon/ttlcache/v2/LICENSE b/vendor/github.com/jellydator/ttlcache/v3/LICENSE
similarity index 97%
rename from vendor/github.com/ReneKroon/ttlcache/v2/LICENSE
rename to vendor/github.com/jellydator/ttlcache/v3/LICENSE
index b3b587dce..f36a3b967 100644
--- a/vendor/github.com/ReneKroon/ttlcache/v2/LICENSE
+++ b/vendor/github.com/jellydator/ttlcache/v3/LICENSE
@@ -1,6 +1,6 @@
 MIT License
 
-Copyright (c) 2018 Rene Kroon
+Copyright (c) 2022 Jellydator
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/vendor/github.com/jellydator/ttlcache/v3/README.md b/vendor/github.com/jellydator/ttlcache/v3/README.md
new file mode 100644
index 000000000..c5dcc5e69
--- /dev/null
+++ b/vendor/github.com/jellydator/ttlcache/v3/README.md
@@ -0,0 +1,129 @@
+# TTLCache - an in-memory cache with item expiration
+
+## Features
+- Simple API.
+- Type parameters.
+- Item expiration and automatic deletion.
+- Automatic expiration time extension on each `Get` call.
+- `Loader` interface that is used to load/lazily initialize missing cache 
+items.
+- Subscription to cache events (insertion and eviction).
+- Metrics.
+- Configurability.
+
+## Installation
+```
+go get github.com/jellydator/ttlcache/v3
+```
+
+## Usage
+The main type of `ttlcache` is `Cache`. It represents a single 
+in-memory data store.
+
+To create a new instance of `ttlcache.Cache`, the `ttlcache.New()` function 
+should be called:
+```go
+func main() {
+	cache := ttlcache.New[string, string]()
+}
+```
+
+Note that by default, a new cache instance does not let any of its
+items to expire or be automatically deleted. However, this feature
+can be activated by passing a few additional options into the 
+`ttlcache.New()` function and calling the `cache.Start()` method:
+```go
+func main() {
+	cache := ttlcache.New[string, string](
+		ttlcache.WithTTL[string, string](30 * time.Minute),
+	)
+
+	go cache.Start() // starts automatic expired item deletion
+}
+```
+
+Even though the `cache.Start()` method handles expired item deletion well,
+there may be times when the system that uses `ttlcache` needs to determine 
+when to delete the expired items itself. For example, it may need to 
+delete them only when the resource load is at its lowest (e.g., after 
+midnight, when the number of users/HTTP requests drops). So, in situations 
+like these, instead of calling `cache.Start()`, the system could 
+periodically call `cache.DeleteExpired()`:
+```go
+func main() {
+	cache := ttlcache.New[string, string](
+		ttlcache.WithTTL[string, string](30 * time.Minute),
+	)
+
+	for {
+		time.Sleep(4 * time.Hour)
+		cache.DeleteExpired()
+	}
+}
+```
+
+The data stored in `ttlcache.Cache` can be retrieved and updated with 
+`Set`, `Get`, `Delete`, etc. methods:
+```go
+func main() {
+	cache := ttlcache.New[string, string](
+		ttlcache.WithTTL[string, string](30 * time.Minute),
+	)
+
+	// insert data
+	cache.Set("first", "value1", ttlcache.DefaultTTL)
+	cache.Set("second", "value2", ttlcache.NoTTL)
+	cache.Set("third", "value3", ttlcache.DefaultTTL)
+
+	// retrieve data
+	item := cache.Get("first")
+	fmt.Println(item.Value(), item.ExpiresAt())
+
+	// delete data
+	cache.Delete("second")
+	cache.DeleteExpired()
+	cache.DeleteAll()
+}
+```
+
+To subscribe to insertion and eviction events, `cache.OnInsertion()` and 
+`cache.OnEviction()` methods should be used:
+```go
+func main() {
+	cache := ttlcache.New[string, string](
+		ttlcache.WithTTL[string, string](30 * time.Minute),
+		ttlcache.WithCapacity[string, string](300),
+	)
+
+	cache.OnInsertion(func(item *ttlcache.Item[string, string]) {
+		fmt.Println(item.Value(), item.ExpiresAt())
+	})
+	cache.OnEviction(func(reason ttlcache.EvictionReason, item *ttlcache.Item[string, string]) {
+		if reason == ttlcache.EvictionReasonCapacityReached {
+			fmt.Println(item.Key(), item.Value())
+		}
+	})
+
+	cache.Set("first", "value1", ttlcache.DefaultTTL)
+	cache.DeleteAll()
+}
+```
+
+To load data when the cache does not have it, a custom or
+existing implementation of `ttlcache.Loader` can be used:
+```go
+func main() {
+	loader := ttlcache.LoaderFunc[string, string](
+		func(c *ttlcache.Cache[string, string], key string) *ttlcache.Item[string, string] {
+			// load from file/make an HTTP request
+			item := c.Set("key from file", "value from file")
+			return item
+		},
+	)
+	cache := ttlcache.New[string, string](
+		ttlcache.WithLoader[string, string](loader),
+	)
+
+	item := cache.Get("key from file")
+}
+```
diff --git a/vendor/github.com/jellydator/ttlcache/v3/cache.go b/vendor/github.com/jellydator/ttlcache/v3/cache.go
new file mode 100644
index 000000000..0c8407cb2
--- /dev/null
+++ b/vendor/github.com/jellydator/ttlcache/v3/cache.go
@@ -0,0 +1,574 @@
+package ttlcache
+
+import (
+	"container/list"
+	"context"
+	"fmt"
+	"sync"
+	"time"
+
+	"golang.org/x/sync/singleflight"
+)
+
+// Available eviction reasons.
+const (
+	EvictionReasonDeleted EvictionReason = iota + 1
+	EvictionReasonCapacityReached
+	EvictionReasonExpired
+)
+
+// EvictionReason is used to specify why a certain item was
+// evicted/deleted.
+type EvictionReason int
+
+// Cache is a synchronised map of items that are automatically removed
+// when they expire or the capacity is reached.
+type Cache[K comparable, V any] struct {
+	items struct {
+		mu     sync.RWMutex
+		values map[K]*list.Element
+
+		// a generic doubly linked list would be more convenient
+		// (and more performant?). It's possible that this
+		// will be introduced with/in go1.19+
+		lru      *list.List
+		expQueue expirationQueue[K, V]
+
+		timerCh chan time.Duration
+	}
+
+	metricsMu sync.RWMutex
+	metrics   Metrics
+
+	events struct {
+		insertion struct {
+			mu     sync.RWMutex
+			nextID uint64
+			fns    map[uint64]func(*Item[K, V])
+		}
+		eviction struct {
+			mu     sync.RWMutex
+			nextID uint64
+			fns    map[uint64]func(EvictionReason, *Item[K, V])
+		}
+	}
+
+	stopCh  chan struct{}
+	options options[K, V]
+}
+
+// New creates a new instance of cache.
+func New[K comparable, V any](opts ...Option[K, V]) *Cache[K, V] {
+	c := &Cache[K, V]{
+		stopCh: make(chan struct{}),
+	}
+	c.items.values = make(map[K]*list.Element)
+	c.items.lru = list.New()
+	c.items.expQueue = newExpirationQueue[K, V]()
+	c.items.timerCh = make(chan time.Duration, 1) // buffer is important
+	c.events.insertion.fns = make(map[uint64]func(*Item[K, V]))
+	c.events.eviction.fns = make(map[uint64]func(EvictionReason, *Item[K, V]))
+
+	applyOptions(&c.options, opts...)
+
+	return c
+}
+
+// updateExpirations updates the expiration queue and notifies
+// the cache auto cleaner if needed.
+// Not concurrently safe.
+func (c *Cache[K, V]) updateExpirations(fresh bool, elem *list.Element) {
+	var oldExpiresAt time.Time
+
+	if !c.items.expQueue.isEmpty() {
+		oldExpiresAt = c.items.expQueue[0].Value.(*Item[K, V]).expiresAt
+	}
+
+	if fresh {
+		c.items.expQueue.push(elem)
+	} else {
+		c.items.expQueue.update(elem)
+	}
+
+	newExpiresAt := c.items.expQueue[0].Value.(*Item[K, V]).expiresAt
+
+	// check if the closest/soonest expiration timestamp changed
+	if newExpiresAt.IsZero() || (!oldExpiresAt.IsZero() && !newExpiresAt.Before(oldExpiresAt)) {
+		return
+	}
+
+	d := time.Until(newExpiresAt)
+
+	// It's possible that the auto cleaner isn't active or
+	// is busy, so we need to drain the channel before
+	// sending a new value.
+	// Also, since this method is called after locking the items' mutex,
+	// we can be sure that there is no other concurrent call of this
+	// method
+	if len(c.items.timerCh) > 0 {
+		// we need to drain this channel in a select with a default
+		// case because it's possible that the auto cleaner
+		// read this channel just after we entered this if
+		select {
+		case d1 := <-c.items.timerCh:
+			if d1 < d {
+				d = d1
+			}
+		default:
+		}
+	}
+
+	// since the channel has a size 1 buffer, we can be sure
+	// that the line below won't block (we can't overfill the buffer
+	// because we just drained it)
+	c.items.timerCh <- d
+}
+
+// set creates a new item, adds it to the cache and then returns it.
+// Not concurrently safe.
+func (c *Cache[K, V]) set(key K, value V, ttl time.Duration) *Item[K, V] {
+	if ttl == DefaultTTL {
+		ttl = c.options.ttl
+	}
+
+	elem := c.get(key, false)
+	if elem != nil {
+		// update/overwrite an existing item
+		item := elem.Value.(*Item[K, V])
+		item.update(value, ttl)
+		c.updateExpirations(false, elem)
+
+		return item
+	}
+
+	if c.options.capacity != 0 && uint64(len(c.items.values)) >= c.options.capacity {
+		// delete the oldest item
+		c.evict(EvictionReasonCapacityReached, c.items.lru.Back())
+	}
+
+	// create a new item
+	item := newItem(key, value, ttl)
+	elem = c.items.lru.PushFront(item)
+	c.items.values[key] = elem
+	c.updateExpirations(true, elem)
+
+	c.metricsMu.Lock()
+	c.metrics.Insertions++
+	c.metricsMu.Unlock()
+
+	c.events.insertion.mu.RLock()
+	for _, fn := range c.events.insertion.fns {
+		fn(item)
+	}
+	c.events.insertion.mu.RUnlock()
+
+	return item
+}
+
+// get retrieves an item from the cache and extends its expiration
+// time if 'touch' is set to true.
+// It returns nil if the item is not found or is expired.
+// Not concurrently safe.
+func (c *Cache[K, V]) get(key K, touch bool) *list.Element {
+	elem := c.items.values[key]
+	if elem == nil {
+		return nil
+	}
+
+	item := elem.Value.(*Item[K, V])
+	if item.isExpiredUnsafe() {
+		return nil
+	}
+
+	c.items.lru.MoveToFront(elem)
+
+	if touch && item.ttl > 0 {
+		item.touch()
+		c.updateExpirations(false, elem)
+	}
+
+	return elem
+}
+
+// evict deletes items from the cache.
+// If no items are provided, all currently present cache items
+// are evicted.
+// Not concurrently safe.
+func (c *Cache[K, V]) evict(reason EvictionReason, elems ...*list.Element) {
+	if len(elems) > 0 {
+		c.metricsMu.Lock()
+		c.metrics.Evictions += uint64(len(elems))
+		c.metricsMu.Unlock()
+
+		c.events.eviction.mu.RLock()
+		for i := range elems {
+			item := elems[i].Value.(*Item[K, V])
+			delete(c.items.values, item.key)
+			c.items.lru.Remove(elems[i])
+			c.items.expQueue.remove(elems[i])
+
+			for _, fn := range c.events.eviction.fns {
+				fn(reason, item)
+			}
+		}
+		c.events.eviction.mu.RUnlock()
+
+		return
+	}
+
+	c.metricsMu.Lock()
+	c.metrics.Evictions += uint64(len(c.items.values))
+	c.metricsMu.Unlock()
+
+	c.events.eviction.mu.RLock()
+	for _, elem := range c.items.values {
+		item := elem.Value.(*Item[K, V])
+
+		for _, fn := range c.events.eviction.fns {
+			fn(reason, item)
+		}
+	}
+	c.events.eviction.mu.RUnlock()
+
+	c.items.values = make(map[K]*list.Element)
+	c.items.lru.Init()
+	c.items.expQueue = newExpirationQueue[K, V]()
+}
+
+// Set creates a new item from the provided key and value, adds
+// it to the cache and then returns it. If an item associated with the
+// provided key already exists, the new item overwrites the existing one.
+func (c *Cache[K, V]) Set(key K, value V, ttl time.Duration) *Item[K, V] {
+	c.items.mu.Lock()
+	defer c.items.mu.Unlock()
+
+	return c.set(key, value, ttl)
+}
+
+// Get retrieves an item from the cache by the provided key.
+// Unless this is disabled, it also extends/touches an item's
+// expiration timestamp on successful retrieval.
+// If the item is not found, a nil value is returned.
+func (c *Cache[K, V]) Get(key K, opts ...Option[K, V]) *Item[K, V] {
+	getOpts := options[K, V]{
+		loader:            c.options.loader,
+		disableTouchOnHit: c.options.disableTouchOnHit,
+	}
+
+	applyOptions(&getOpts, opts...)
+
+	c.items.mu.Lock()
+	elem := c.get(key, !getOpts.disableTouchOnHit)
+	c.items.mu.Unlock()
+
+	if elem == nil {
+		c.metricsMu.Lock()
+		c.metrics.Misses++
+		c.metricsMu.Unlock()
+
+		if getOpts.loader != nil {
+			return getOpts.loader.Load(c, key)
+		}
+
+		return nil
+	}
+
+	c.metricsMu.Lock()
+	c.metrics.Hits++
+	c.metricsMu.Unlock()
+
+	return elem.Value.(*Item[K, V])
+}
+
+// Delete deletes an item from the cache. If the item associated with
+// the key is not found, the method is no-op.
+func (c *Cache[K, V]) Delete(key K) {
+	c.items.mu.Lock()
+	defer c.items.mu.Unlock()
+
+	elem := c.items.values[key]
+	if elem == nil {
+		return
+	}
+
+	c.evict(EvictionReasonDeleted, elem)
+}
+
+// DeleteAll deletes all items from the cache.
+func (c *Cache[K, V]) DeleteAll() {
+	c.items.mu.Lock()
+	c.evict(EvictionReasonDeleted)
+	c.items.mu.Unlock()
+}
+
+// DeleteExpired deletes all expired items from the cache.
+func (c *Cache[K, V]) DeleteExpired() {
+	c.items.mu.Lock()
+	defer c.items.mu.Unlock()
+
+	if c.items.expQueue.isEmpty() {
+		return
+	}
+
+	e := c.items.expQueue[0]
+	for e.Value.(*Item[K, V]).isExpiredUnsafe() {
+		c.evict(EvictionReasonExpired, e)
+
+		if c.items.expQueue.isEmpty() {
+			break
+		}
+
+		// expiration queue has a new root
+		e = c.items.expQueue[0]
+	}
+}
+
+// Touch simulates an item's retrieval without actually returning it.
+// Its main purpose is to extend an item's expiration timestamp.
+// If the item is not found, the method is no-op.
+func (c *Cache[K, V]) Touch(key K) {
+	c.items.mu.Lock()
+	c.get(key, true)
+	c.items.mu.Unlock()
+}
+
+// Len returns the number of items in the cache.
+func (c *Cache[K, V]) Len() int {
+	c.items.mu.RLock()
+	defer c.items.mu.RUnlock()
+
+	return len(c.items.values)
+}
+
+// Keys returns all keys currently present in the cache.
+func (c *Cache[K, V]) Keys() []K {
+	c.items.mu.RLock()
+	defer c.items.mu.RUnlock()
+
+	res := make([]K, 0, len(c.items.values))
+	for k := range c.items.values {
+		res = append(res, k)
+	}
+
+	return res
+}
+
+// Items returns a copy of all items in the cache.
+// It does not update any expiration timestamps.
+func (c *Cache[K, V]) Items() map[K]*Item[K, V] {
+	c.items.mu.RLock()
+	defer c.items.mu.RUnlock()
+
+	items := make(map[K]*Item[K, V], len(c.items.values))
+	for k := range c.items.values {
+		item := c.get(k, false)
+		if item != nil {
+			items[k] = item.Value.(*Item[K, V])
+		}
+	}
+
+	return items
+}
+
+// Metrics returns the metrics of the cache.
+func (c *Cache[K, V]) Metrics() Metrics {
+	c.metricsMu.RLock()
+	defer c.metricsMu.RUnlock()
+
+	return c.metrics
+}
+
+// Start starts an automatic cleanup process that
+// periodically deletes expired items.
+// It blocks until Stop is called.
+func (c *Cache[K, V]) Start() {
+	waitDur := func() time.Duration {
+		c.items.mu.RLock()
+		defer c.items.mu.RUnlock()
+
+		if !c.items.expQueue.isEmpty() &&
+			!c.items.expQueue[0].Value.(*Item[K, V]).expiresAt.IsZero() {
+			d := time.Until(c.items.expQueue[0].Value.(*Item[K, V]).expiresAt)
+			if d <= 0 {
+				// execute immediately
+				return time.Microsecond
+			}
+
+			return d
+		}
+
+		if c.options.ttl > 0 {
+			return c.options.ttl
+		}
+
+		return time.Hour
+	}
+
+	timer := time.NewTimer(waitDur())
+	stop := func() {
+		if !timer.Stop() {
+			// drain the timer chan
+			select {
+			case <-timer.C:
+			default:
+			}
+		}
+	}
+
+	defer stop()
+
+	for {
+		select {
+		case <-c.stopCh:
+			return
+		case d := <-c.items.timerCh:
+			stop()
+			timer.Reset(d)
+		case <-timer.C:
+			c.DeleteExpired()
+			stop()
+			timer.Reset(waitDur())
+		}
+	}
+}
+
+// Stop stops the automatic cleanup process.
+// It blocks until the cleanup process exits.
+func (c *Cache[K, V]) Stop() {
+	c.stopCh <- struct{}{}
+}
+
+// OnInsertion adds the provided function to be executed when
+// a new item is inserted into the cache. The function is executed
+// on a separate goroutine and does not block the flow of the cache
+// manager.
+// The returned function may be called to delete the subscription function
+// from the list of insertion subscribers.
+// When the returned function is called, it blocks until all instances of
+// the same subscription function return. A context is used to notify the
+// subscription function when the returned/deletion function is called.
+func (c *Cache[K, V]) OnInsertion(fn func(context.Context, *Item[K, V])) func() {
+	var (
+		wg          sync.WaitGroup
+		ctx, cancel = context.WithCancel(context.Background())
+	)
+
+	c.events.insertion.mu.Lock()
+	id := c.events.insertion.nextID
+	c.events.insertion.fns[id] = func(item *Item[K, V]) {
+		wg.Add(1)
+		go func() {
+			fn(ctx, item)
+			wg.Done()
+		}()
+	}
+	c.events.insertion.nextID++
+	c.events.insertion.mu.Unlock()
+
+	return func() {
+		cancel()
+
+		c.events.insertion.mu.Lock()
+		delete(c.events.insertion.fns, id)
+		c.events.insertion.mu.Unlock()
+
+		wg.Wait()
+	}
+}
+
+// OnEviction adds the provided function to be executed when
+// an item is evicted/deleted from the cache. The function is executed
+// on a separate goroutine and does not block the flow of the cache
+// manager.
+// The returned function may be called to delete the subscription function
+// from the list of eviction subscribers.
+// When the returned function is called, it blocks until all instances of
+// the same subscription function return. A context is used to notify the
+// subscription function when the returned/deletion function is called.
+func (c *Cache[K, V]) OnEviction(fn func(context.Context, EvictionReason, *Item[K, V])) func() {
+	var (
+		wg          sync.WaitGroup
+		ctx, cancel = context.WithCancel(context.Background())
+	)
+
+	c.events.eviction.mu.Lock()
+	id := c.events.eviction.nextID
+	c.events.eviction.fns[id] = func(r EvictionReason, item *Item[K, V]) {
+		wg.Add(1)
+		go func() {
+			fn(ctx, r, item)
+			wg.Done()
+		}()
+	}
+	c.events.eviction.nextID++
+	c.events.eviction.mu.Unlock()
+
+	return func() {
+		cancel()
+
+		c.events.eviction.mu.Lock()
+		delete(c.events.eviction.fns, id)
+		c.events.eviction.mu.Unlock()
+
+		wg.Wait()
+	}
+}
+
+// Loader is an interface that handles missing data loading.
+type Loader[K comparable, V any] interface {
+	// Load should execute a custom item retrieval logic and
+	// return the item that is associated with the key.
+	// It should return nil if the item is not found/valid.
+	// The method is allowed to fetch data from the cache instance
+	// or update it for future use.
+	Load(c *Cache[K, V], key K) *Item[K, V]
+}
+
+// LoaderFunc type is an adapter that allows the use of ordinary
+// functions as data loaders.
+type LoaderFunc[K comparable, V any] func(*Cache[K, V], K) *Item[K, V]
+
+// Load executes a custom item retrieval logic and returns the item that
+// is associated with the key.
+// It returns nil if the item is not found/valid.
+func (l LoaderFunc[K, V]) Load(c *Cache[K, V], key K) *Item[K, V] {
+	return l(c, key)
+}
+
+// SuppressedLoader wraps another Loader and suppresses duplicate
+// calls to its Load method.
+type SuppressedLoader[K comparable, V any] struct {
+	Loader[K, V]
+
+	group *singleflight.Group
+}
+
+// Load executes a custom item retrieval logic and returns the item that
+// is associated with the key.
+// It returns nil if the item is not found/valid.
+// It also ensures that only one execution of the wrapped Loader's Load
+// method is in-flight for a given key at a time.
+func (l *SuppressedLoader[K, V]) Load(c *Cache[K, V], key K) *Item[K, V] {
+	// there should be a better/generic way to create a
+	// singleflight Group's key. It's possible that a generic
+	// singleflight.Group will be introduced with/in go1.19+
+	strKey := fmt.Sprint(key)
+
+	// the error can be discarded since the singleflight.Group
+	// itself does not return any of its errors, it returns
+	// the error that we return ourselves in the func below, which
+	// is also nil
+	res, _, _ := l.group.Do(strKey, func() (interface{}, error) {
+		item := l.Loader.Load(c, key)
+		if item == nil {
+			return nil, nil
+		}
+
+		return item, nil
+	})
+	if res == nil {
+		return nil
+	}
+
+	return res.(*Item[K, V])
+}
diff --git a/vendor/github.com/jellydator/ttlcache/v3/expiration_queue.go b/vendor/github.com/jellydator/ttlcache/v3/expiration_queue.go
new file mode 100644
index 000000000..0f104588c
--- /dev/null
+++ b/vendor/github.com/jellydator/ttlcache/v3/expiration_queue.go
@@ -0,0 +1,85 @@
+package ttlcache
+
+import (
+	"container/heap"
+	"container/list"
+)
+
+// expirationQueue stores items that are ordered by their expiration
+// timestamps. The 0th item is closest to its expiration.
+type expirationQueue[K comparable, V any] []*list.Element
+
+// newExpirationQueue creates and initializes a new expiration queue.
+func newExpirationQueue[K comparable, V any]() expirationQueue[K, V] {
+	q := make(expirationQueue[K, V], 0)
+	heap.Init(&q)
+	return q
+}
+
+// isEmpty checks if the queue is empty.
+func (q expirationQueue[K, V]) isEmpty() bool {
+	return q.Len() == 0
+}
+
+// update updates an existing item's value and position in the queue.
+func (q *expirationQueue[K, V]) update(elem *list.Element) {
+	heap.Fix(q, elem.Value.(*Item[K, V]).queueIndex)
+}
+
+// push pushes a new item into the queue and updates the order of its
+// elements.
+func (q *expirationQueue[K, V]) push(elem *list.Element) {
+	heap.Push(q, elem)
+}
+
+// remove removes an item from the queue and updates the order of its
+// elements.
+func (q *expirationQueue[K, V]) remove(elem *list.Element) {
+	heap.Remove(q, elem.Value.(*Item[K, V]).queueIndex)
+}
+
+// Len returns the total number of items in the queue.
+func (q expirationQueue[K, V]) Len() int {
+	return len(q)
+}
+
+// Less checks if the item at the i position expires sooner than
+// the one at the j position.
+func (q expirationQueue[K, V]) Less(i, j int) bool {
+	item1, item2 := q[i].Value.(*Item[K, V]), q[j].Value.(*Item[K, V])
+	if item1.expiresAt.IsZero() {
+		return false
+	}
+
+	if item2.expiresAt.IsZero() {
+		return true
+	}
+
+	return item1.expiresAt.Before(item2.expiresAt)
+}
+
+// Swap switches the places of two queue items.
+func (q expirationQueue[K, V]) Swap(i, j int) {
+	q[i], q[j] = q[j], q[i]
+	q[i].Value.(*Item[K, V]).queueIndex = i
+	q[j].Value.(*Item[K, V]).queueIndex = j
+}
+
+// Push appends a new item to the item slice.
+func (q *expirationQueue[K, V]) Push(x interface{}) {
+	elem := x.(*list.Element)
+	elem.Value.(*Item[K, V]).queueIndex = len(*q)
+	*q = append(*q, elem)
+}
+
+// Pop removes and returns the last item.
+func (q *expirationQueue[K, V]) Pop() interface{} {
+	old := *q
+	i := len(old) - 1
+	elem := old[i]
+	elem.Value.(*Item[K, V]).queueIndex = -1
+	old[i] = nil // avoid memory leak
+	*q = old[:i]
+
+	return elem
+}
diff --git a/vendor/github.com/jellydator/ttlcache/v3/item.go b/vendor/github.com/jellydator/ttlcache/v3/item.go
new file mode 100644
index 000000000..58c9c5ce3
--- /dev/null
+++ b/vendor/github.com/jellydator/ttlcache/v3/item.go
@@ -0,0 +1,130 @@
+package ttlcache
+
+import (
+	"sync"
+	"time"
+)
+
+const (
+	// NoTTL indicates that an item should never expire.
+	NoTTL time.Duration = -1
+
+	// DefaultTTL indicates that the default TTL
+	// value should be used.
+	DefaultTTL time.Duration = 0
+)
+
+// Item holds all the information that is associated with a single
+// cache value.
+type Item[K comparable, V any] struct {
+	// the mutex needs to be locked only when:
+	// - data fields are being read inside accessor methods
+	// - data fields are being updated
+	// when data fields are being read in one of the cache's
+	// methods, we can be sure that these fields are not modified in
+	// parallel since the item list is locked by its own mutex as
+	// well, so locking this mutex would be redundant.
+	// In other words, this mutex is only useful when these fields
+	// are being read from the outside (e.g. in event functions).
+	mu         sync.RWMutex
+	key        K
+	value      V
+	ttl        time.Duration
+	expiresAt  time.Time
+	queueIndex int
+}
+
+// newItem creates a new cache item.
+func newItem[K comparable, V any](key K, value V, ttl time.Duration) *Item[K, V] {
+	item := &Item[K, V]{
+		key:   key,
+		value: value,
+		ttl:   ttl,
+	}
+	item.touch()
+
+	return item
+}
+
+// update modifies the item's value and TTL.
+func (item *Item[K, V]) update(value V, ttl time.Duration) {
+	item.mu.Lock()
+	defer item.mu.Unlock()
+
+	item.value = value
+	item.ttl = ttl
+
+	// reset expiration timestamp because the new TTL may be
+	// 0 or below
+	item.expiresAt = time.Time{}
+	item.touchUnsafe()
+}
+
+// touch updates the item's expiration timestamp.
+func (item *Item[K, V]) touch() {
+	item.mu.Lock()
+	defer item.mu.Unlock()
+
+	item.touchUnsafe()
+}
+
+// touchUnsafe updates the item's expiration timestamp without
+// locking the mutex.
+func (item *Item[K, V]) touchUnsafe() {
+	if item.ttl <= 0 {
+		return
+	}
+
+	item.expiresAt = time.Now().Add(item.ttl)
+}
+
+// IsExpired returns a bool value that indicates whether the item
+// is expired.
+func (item *Item[K, V]) IsExpired() bool {
+	item.mu.RLock()
+	defer item.mu.RUnlock()
+
+	return item.isExpiredUnsafe()
+}
+
+// isExpiredUnsafe returns a bool value that indicates whether the
+// the item is expired without locking the mutex
+func (item *Item[K, V]) isExpiredUnsafe() bool {
+	if item.ttl <= 0 {
+		return false
+	}
+
+	return item.expiresAt.Before(time.Now())
+}
+
+// Key returns the key of the item.
+func (item *Item[K, V]) Key() K {
+	item.mu.RLock()
+	defer item.mu.RUnlock()
+
+	return item.key
+}
+
+// Value returns the value of the item.
+func (item *Item[K, V]) Value() V {
+	item.mu.RLock()
+	defer item.mu.RUnlock()
+
+	return item.value
+}
+
+// TTL returns the TTL value of the item.
+func (item *Item[K, V]) TTL() time.Duration {
+	item.mu.RLock()
+	defer item.mu.RUnlock()
+
+	return item.ttl
+}
+
+// ExpiresAt returns the expiration timestamp of the item.
+func (item *Item[K, V]) ExpiresAt() time.Time {
+	item.mu.RLock()
+	defer item.mu.RUnlock()
+
+	return item.expiresAt
+}
diff --git a/vendor/github.com/jellydator/ttlcache/v3/metrics.go b/vendor/github.com/jellydator/ttlcache/v3/metrics.go
new file mode 100644
index 000000000..4bf855961
--- /dev/null
+++ b/vendor/github.com/jellydator/ttlcache/v3/metrics.go
@@ -0,0 +1,21 @@
+package ttlcache
+
+// Metrics contains common cache metrics calculated over the course
+// of the cache's lifetime.
+type Metrics struct {
+	// Insertions specifies how many items were inserted.
+	Insertions uint64
+
+	// Hits specifies how many items were successfully retrieved
+	// from the cache.
+	// Retrievals made with a loader function are not tracked.
+	Hits uint64
+
+	// Misses specifies how many items were not found in the cache.
+	// Retrievals made with a loader function are tracked as well.
+	Misses uint64
+
+	// Evictions specifies how many items were removed from the
+	// cache.
+	Evictions uint64
+}
diff --git a/vendor/github.com/jellydator/ttlcache/v3/options.go b/vendor/github.com/jellydator/ttlcache/v3/options.go
new file mode 100644
index 000000000..3acf88d9b
--- /dev/null
+++ b/vendor/github.com/jellydator/ttlcache/v3/options.go
@@ -0,0 +1,67 @@
+package ttlcache
+
+import "time"
+
+// Option sets a specific cache option.
+type Option[K comparable, V any] interface {
+	apply(opts *options[K, V])
+}
+
+// optionFunc wraps a function and implements the Option interface.
+type optionFunc[K comparable, V any] func(*options[K, V])
+
+// apply calls the wrapped function.
+func (fn optionFunc[K, V]) apply(opts *options[K, V]) {
+	fn(opts)
+}
+
+// options holds all available cache configuration options.
+type options[K comparable, V any] struct {
+	capacity          uint64
+	ttl               time.Duration
+	loader            Loader[K, V]
+	disableTouchOnHit bool
+}
+
+// applyOptions applies the provided option values to the option struct.
+func applyOptions[K comparable, V any](v *options[K, V], opts ...Option[K, V]) {
+	for i := range opts {
+		opts[i].apply(v)
+	}
+}
+
+// WithCapacity sets the maximum capacity of the cache.
+// It has no effect when passing into Get().
+func WithCapacity[K comparable, V any](c uint64) Option[K, V] {
+	return optionFunc[K, V](func(opts *options[K, V]) {
+		opts.capacity = c
+	})
+}
+
+// WithTTL sets the TTL of the cache.
+// It has no effect when passing into Get().
+func WithTTL[K comparable, V any](ttl time.Duration) Option[K, V] {
+	return optionFunc[K, V](func(opts *options[K, V]) {
+		opts.ttl = ttl
+	})
+}
+
+// WithLoader sets the loader of the cache.
+// When passing into Get(), it sets an epheral loader that
+// is used instead of the cache's default one.
+func WithLoader[K comparable, V any](l Loader[K, V]) Option[K, V] {
+	return optionFunc[K, V](func(opts *options[K, V]) {
+		opts.loader = l
+	})
+}
+
+// WithDisableTouchOnHit prevents the cache instance from
+// extending/touching an item's expiration timestamp when it is being
+// retrieved.
+// When passing into Get(), it overrides the default value of the
+// cache.
+func WithDisableTouchOnHit[K comparable, V any]() Option[K, V] {
+	return optionFunc[K, V](func(opts *options[K, V]) {
+		opts.disableTouchOnHit = true
+	})
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 74906478a..8aaf7f4e7 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -31,9 +31,6 @@ github.com/ProtonMail/go-crypto/openpgp/internal/ecc
 github.com/ProtonMail/go-crypto/openpgp/internal/encoding
 github.com/ProtonMail/go-crypto/openpgp/packet
 github.com/ProtonMail/go-crypto/openpgp/s2k
-# github.com/ReneKroon/ttlcache/v2 v2.11.0
-## explicit; go 1.15
-github.com/ReneKroon/ttlcache/v2
 # github.com/acobaugh/osrelease v0.0.0-20181218015638-a93a0a55a249
 ## explicit
 github.com/acobaugh/osrelease
@@ -405,6 +402,9 @@ github.com/inconshreveable/mousetrap
 # github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99
 ## explicit
 github.com/jbenet/go-context/io
+# github.com/jellydator/ttlcache/v3 v3.0.0
+## explicit; go 1.18
+github.com/jellydator/ttlcache/v3
 # github.com/jmespath/go-jmespath v0.4.0
 ## explicit; go 1.14
 github.com/jmespath/go-jmespath


Test patch:
diff --git a/hack/ci/e2e-olm.sh b/hack/ci/e2e-olm.sh
index 69962b7db..4dcb02c25 100755
--- a/hack/ci/e2e-olm.sh
+++ b/hack/ci/e2e-olm.sh
@@ -58,7 +58,7 @@ function deploy_olm() {
 function deploy_spo() {
     # cert-manager first. This should be done using dependencies in the
     # future
-    kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.8.0/cert-manager.yaml
+    kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.9.1/cert-manager.yaml
     kubectl -ncert-manager wait --for condition=ready pod -l app.kubernetes.io/instance=cert-manager
 
     # let's roll..
diff --git a/internal/pkg/daemon/bpfrecorder/bpfrecorder_test.go b/internal/pkg/daemon/bpfrecorder/bpfrecorder_test.go
index 13773c0db..89f8f36d6 100644
--- a/internal/pkg/daemon/bpfrecorder/bpfrecorder_test.go
+++ b/internal/pkg/daemon/bpfrecorder/bpfrecorder_test.go
@@ -78,14 +78,6 @@ func TestRun(t *testing.T) {
 				require.Nil(t, err)
 			},
 		},
-		{ // SetTTL fails
-			prepare: func(mock *bpfrecorderfakes.FakeImpl) {
-				mock.SetTTLReturns(errTest)
-			},
-			assert: func(err error) {
-				require.NotNil(t, err)
-			},
-		},
 		{ // Getenv returns nothing
 			prepare: func(mock *bpfrecorderfakes.FakeImpl) {
 				mock.GetenvReturns("")
diff --git a/internal/pkg/daemon/enricher/audit_test.go b/internal/pkg/daemon/enricher/audit_test.go
index bd66fa60f..c466f145d 100644
--- a/internal/pkg/daemon/enricher/audit_test.go
+++ b/internal/pkg/daemon/enricher/audit_test.go
@@ -20,6 +20,8 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/require"
+
+	"sigs.k8s.io/security-profiles-operator/internal/pkg/daemon/enricher/types"
 )
 
 func Test_isAuditLine(t *testing.T) {
@@ -80,19 +82,19 @@ func Test_extractAuditLine(t *testing.T) {
 	tests := []struct {
 		name    string
 		logLine string
-		want    *auditLine
+		want    *types.AuditLine
 		wantErr error
 	}{
 		{
 			"Should extract seccomp log lines",
 			// nolint:lll // no need to wrap
 			`audit: type=1326 audit(1612299677.115:549067): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=3109464 comm="sh" exe="/bin/busybox" sig=0 arch=c000003e syscall=0 compat=0 ip=0x7fce771ae923 code=0x7ffc0000`,
-			&auditLine{
-				auditType:    "seccomp",
-				timestampID:  "1612299677.115:549067",
-				systemCallID: 0,
-				processID:    3109464,
-				executable:   "/bin/busybox",
+			&types.AuditLine{
+				AuditType:    "seccomp",
+				TimestampID:  "1612299677.115:549067",
+				SystemCallID: 0,
+				ProcessID:    3109464,
+				Executable:   "/bin/busybox",
 			},
 			nil,
 		},
@@ -100,12 +102,12 @@ func Test_extractAuditLine(t *testing.T) {
 			"Should extract seccomp log lines",
 			// nolint:lll // no need to wrap
 			`type=SECCOMP msg=audit(1613596317.899:6461): auid=4294967295 uid=0 gid=0 ses=4294967295 subj=system_u:system_r:spc_t:s0:c284,c594 pid=2039886 comm="ls" exe="/bin/ls" sig=0 arch=c000003e syscall=3 compat=0 ip=0x7f62dce3d4c7 code=0x7ffc0000AUID="unset" UID="root" GID="root" ARCH=x86_64 SYSCALL=close`,
-			&auditLine{
-				auditType:    "seccomp",
-				timestampID:  "1613596317.899:6461",
-				systemCallID: 3,
-				processID:    2039886,
-				executable:   "/bin/ls",
+			&types.AuditLine{
+				AuditType:    "seccomp",
+				TimestampID:  "1613596317.899:6461",
+				SystemCallID: 3,
+				ProcessID:    2039886,
+				Executable:   "/bin/ls",
 			},
 			nil,
 		},
@@ -113,16 +115,16 @@ func Test_extractAuditLine(t *testing.T) {
 			"Should extract selinux log lines",
 			// nolint:lll // no need to wrap
 			`type=AVC msg=audit(1613173578.156:2945): avc:  denied  { read } for  pid=75593 comm="security-profil" name="token" dev="tmpfs" ino=612459 scontext=system_u:system_r:container_t:s0:c4,c808 tcontext=system_u:object_r:var_lib_t:s0 tclass=lnk_file permissive=0`,
-			&auditLine{
-				auditType:    "selinux",
-				timestampID:  "1613173578.156:2945",
-				systemCallID: 0,
-				processID:    75593,
-				executable:   "",
-				perm:         "read",
-				scontext:     "system_u:system_r:container_t:s0:c4,c808",
-				tcontext:     "system_u:object_r:var_lib_t:s0",
-				tclass:       "lnk_file",
+			&types.AuditLine{
+				AuditType:    "selinux",
+				TimestampID:  "1613173578.156:2945",
+				SystemCallID: 0,
+				ProcessID:    75593,
+				Executable:   "",
+				Perm:         "read",
+				Scontext:     "system_u:system_r:container_t:s0:c4,c808",
+				Tcontext:     "system_u:object_r:var_lib_t:s0",
+				Tclass:       "lnk_file",
 			},
 			nil,
 		},
diff --git a/internal/pkg/daemon/enricher/enricher_test.go b/internal/pkg/daemon/enricher/enricher_test.go
index 36270b850..e3ebeefd8 100644
--- a/internal/pkg/daemon/enricher/enricher_test.go
+++ b/internal/pkg/daemon/enricher/enricher_test.go
@@ -31,6 +31,7 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
 	"sigs.k8s.io/security-profiles-operator/internal/pkg/daemon/enricher/enricherfakes"
+	"sigs.k8s.io/security-profiles-operator/internal/pkg/daemon/enricher/types"
 )
 
 const (
@@ -108,15 +109,6 @@ func TestRun(t *testing.T) {
 				require.Nil(t, err)
 			},
 		},
-		{ // failure on SetTTL
-			runAsync: false,
-			prepare: func(mock *enricherfakes.FakeImpl, lineChan chan *tail.Line) {
-				mock.SetTTLReturns(errTest)
-			},
-			assert: func(mock *enricherfakes.FakeImpl, lineChan chan *tail.Line, err error) {
-				require.NotNil(t, err)
-			},
-		},
 		{ // failure on Getenv
 			runAsync: false,
 			prepare: func(mock *enricherfakes.FakeImpl, lineChan chan *tail.Line) {
@@ -299,19 +291,19 @@ func TestRun(t *testing.T) {
 
 				// add something to the mock backlog
 				mock.GetFromBacklogReturns(
-					[]*auditLine{
+					[]*types.AuditLine{
 						{
-							auditType:    "selinux",
-							timestampID:  "1613173578.156:2945",
-							systemCallID: 0,
-							processID:    75593,
-							executable:   "",
-							perm:         "read",
-							scontext:     "system_u:system_r:container_t:s0:c4,c808",
-							tcontext:     "system_u:object_r:var_lib_t:s0",
-							tclass:       "lnk_file",
+							AuditType:    "selinux",
+							TimestampID:  "1613173578.156:2945",
+							SystemCallID: 0,
+							ProcessID:    75593,
+							Executable:   "",
+							Perm:         "read",
+							Scontext:     "system_u:system_r:container_t:s0:c4,c808",
+							Tcontext:     "system_u:object_r:var_lib_t:s0",
+							Tclass:       "lnk_file",
 						},
-					}, nil,
+					},
 				)
 
 				for mock.GetFromBacklogCallCount() != 1 {
diff --git a/test/e2e_test.go b/test/e2e_test.go
index a3aee64bb..397036af1 100644
--- a/test/e2e_test.go
+++ b/test/e2e_test.go
@@ -32,7 +32,7 @@ import (
 )
 
 const (
-	certmanager       = "https://github.com/cert-manager/cert-manager/releases/download/v1.8.0/cert-manager.yaml"
+	certmanager       = "https://github.com/cert-manager/cert-manager/releases/download/v1.9.1/cert-manager.yaml"
 	manifest          = "deploy/operator.yaml"
 	namespaceManifest = "deploy/namespace-operator.yaml"
 	testNamespace     = "test-ns"