File: regexp_cache_test.go

package info (click to toggle)
golang-github-victoriametrics-metricsql 0.49.0%2Bds1-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 272 kB
  • sloc: makefile: 2
file content (106 lines) | stat: -rw-r--r-- 3,090 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package metricsql

import (
	"fmt"
	"regexp"
	"testing"
	"time"
)

func TestRegexpCacheConcurrent(t *testing.T) {
	goroutines := 5
	maxChars := 1000
	rc := newRegexpCache(maxChars)
	resultCh := make(chan error, goroutines)
	for i := 0; i < goroutines; i++ {
		go func() {
			resultCh <- testRegexpCache(rc)
		}()
	}
	timer := time.NewTimer(time.Second * 5)
	for i := 0; i < goroutines; i++ {
		select {
		case <-timer.C:
			t.Fatalf("timeout")
		case err := <-resultCh:
			if err != nil {
				t.Fatalf("unexpected error: %s", err)
			}
		}
	}
	maxChars += int(float64(maxChars) * 0.1)
	if chars := rc.CharsCurrent(); chars > maxChars {
		t.Fatalf("too many chars in the regexpCache; got %d; expected no more than %d", chars, maxChars)
	}
}

func testRegexpCache(rc *regexpCache) error {
	for i := 0; i < 10000; i++ {
		key := fmt.Sprintf("foo|regexp-%d", i)
		rcv := rc.Get(key)
		if rcv != nil {
			if rcv.err != nil {
				return fmt.Errorf("unexpected error obtained for key %q: %w", key, rcv.err)
			}
			if re := rcv.r.String(); re != key {
				return fmt.Errorf("unexpected regexp obtained for key %q: %q; want %q", key, re, key)
			}
		} else {
			r, err := regexp.Compile(key)
			rcv := &regexpCacheValue{
				r:   r,
				err: err,
			}
			rc.Put(key, rcv)
		}
	}
	return nil
}

func TestRegexpCache(t *testing.T) {
	fn := func(maxChars int, regexps []string, expectedEntries, expectedChars int) {
		t.Helper()
		rc := newRegexpCache(maxChars)
		for _, re := range regexps {
			r, err := regexp.Compile(re)
			rcv := &regexpCacheValue{
				r:   r,
				err: err,
			}
			rc.Put(re, rcv)
			rcv1 := rc.Get(re)
			if rcv1 != rcv {
				t.Fatalf("unexpected result for regexp %q; got\n%v\nwant\n%v", re, rcv1, rcv)
			}
		}
		if requests := rc.Requests(); requests != uint64(len(regexps)) {
			t.Fatalf("unexpected number of requests; got %d; want %d", requests, len(regexps))
		}
		if misses := rc.Misses(); misses != 0 {
			t.Fatalf("unexpected number of misses; got %d; want 0", misses)
		}
		rcv := rc.Get("non-existing-regexp")
		if rcv != nil {
			t.Fatalf("expecting nil entry; got %v", rcv)
		}
		if misses := rc.Misses(); misses != 1 {
			t.Fatalf("unexpected number of misses; got %d; want 1", misses)
		}
		if entries := rc.Len(); entries != expectedEntries {
			t.Fatalf("unexpected number of entries; got %d; want %d", entries, expectedEntries)
		}
		if chars := rc.CharsCurrent(); chars != expectedChars {
			t.Fatalf("unexpected charsCurrent; got %d; want %d", chars, expectedChars)
		}
	}

	fn(10, []string{"a", "b", "c"}, 3, 3)
	fn(2, []string{"a", "b", "c"}, 3, 3) // overflow by 1 entry is allowed
	fn(2, []string{"a", "b", "c", "d"}, 3, 3)
	fn(1, []string{"a", "b", "c"}, 2, 2)           // overflow by 1 tnery is allowed
	fn(2, []string{"abcd", "efgh", "ijkl"}, 1, 4)  // overflow by 1 entry is allowed
	fn(2, []string{"123", "fd{456", "789"}, 1, 3)  // overflow by 1 entry is allowed
	fn(9, []string{"123", "fd{456", "789"}, 3, 12) // overflow by 1 entry is allowed
	fn(12, []string{"123", "fd{456", "789"}, 3, 12)
	fn(15, []string{"123", "fd{456", "789"}, 3, 12)
}