Skip to content

Commit bfeae3a

Browse files
committed
test: experiment with sliced of atomic integers instead of sync map
Signed-off-by: Michael Hoffmann <[email protected]>
1 parent 6765d04 commit bfeae3a

File tree

3 files changed

+56
-12
lines changed

3 files changed

+56
-12
lines changed

execution/execution.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func New(ctx context.Context, expr parser.Expr, queryable storage.Queryable, min
6868
// TODO(fpetkovski): Adjust the step for sub-queries once they are supported.
6969
Step: step.Milliseconds(),
7070
}
71-
limits := limits.NewLimits(maxSamples)
71+
limits := limits.NewLimits(maxSamples, opts)
7272

7373
return newOperator(expr, selectorPool, opts, hints, limits)
7474
}

execution/limits/limits.go

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,79 @@
11
package limits
22

33
import (
4-
"sync"
5-
64
"github.com/efficientgo/core/errors"
75
"go.uber.org/atomic"
6+
7+
"github.com/thanos-io/promql-engine/query"
88
)
99

1010
type Limits struct {
1111
maxSamples int
1212

13-
curSamplesPerTimestamp sync.Map
13+
start int64
14+
step int64
15+
stepsBatch int64
16+
17+
samplesPerTimestamp []*atomic.Int64
18+
periodPerTimestamp []*atomic.Int64
1419
}
1520

16-
func NewLimits(maxSamples int) *Limits {
17-
return &Limits{
21+
// NewLimits returns a pointer to a Limits struct. It can be used to
22+
// track samples that enter the engine in some timestamp and limit it
23+
// to a maximum number. Since the engine processes "stepsBatch" timestamps
24+
// in parallel the resulting memory overhead will be "O(stepsBatch*maxSamples)".
25+
func NewLimits(maxSamples int, opts *query.Options) *Limits {
26+
step := opts.Step.Milliseconds()
27+
if opts.NumSteps() == 1 {
28+
step = 1
29+
}
30+
start := opts.Start.UnixMilli()
31+
stepsBatch := opts.StepsBatch
32+
33+
res := &Limits{
1834
maxSamples: maxSamples,
35+
36+
start: start,
37+
step: step,
38+
stepsBatch: stepsBatch,
39+
40+
periodPerTimestamp: make([]*atomic.Int64, stepsBatch),
41+
samplesPerTimestamp: make([]*atomic.Int64, stepsBatch),
1942
}
43+
44+
for i := int64(0); i < stepsBatch; i++ {
45+
res.periodPerTimestamp[i] = atomic.NewInt64(0)
46+
res.samplesPerTimestamp[i] = atomic.NewInt64(0)
47+
}
48+
49+
return res
2050
}
2151

52+
// AccountSamplesForTimestamp keeps track of the samples used for timestamp t.
53+
// It will return an error if a step wants to add use more samples then the configured
54+
// maxSamples value.
2255
func (l *Limits) AccountSamplesForTimestamp(t int64, n int) error {
2356
if l.maxSamples == 0 {
2457
return nil
2558
}
26-
v, _ := l.curSamplesPerTimestamp.LoadOrStore(t, atomic.NewInt64(0))
27-
av := v.(*atomic.Int64)
59+
// TODO: properly this method would need a lock but so far it seems to work well enough.
60+
61+
idx := ((t - l.start) / l.step)
62+
idxmod := idx % l.stepsBatch
63+
64+
// This assumes that if we process "stepsBatch+1" we have processed "1" already.
65+
// This is accurate so long we dont move the coalesce operator higher up the execution
66+
// tree. It allows us to account only for the last "stepsBatch" timestamps and keep a
67+
// constant memory overhead.
68+
if period := idx / l.stepsBatch; period > l.periodPerTimestamp[idxmod].Load() {
69+
l.periodPerTimestamp[idxmod].Store(period)
70+
l.samplesPerTimestamp[idxmod].Store(0)
71+
}
2872

29-
if cur := av.Load(); cur+int64(n) > int64(l.maxSamples) {
73+
if cur := l.samplesPerTimestamp[idxmod].Load(); cur+int64(n) > int64(l.maxSamples) {
3074
return errors.New("query processing would load too many samples into memory in query execution")
3175
}
76+
l.samplesPerTimestamp[idxmod].Add(int64(n))
3277

33-
av.Add(int64(n))
3478
return nil
3579
}

execution/scan/matrix_selector.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,9 @@ func (o *matrixSelector) Next(ctx context.Context) ([]model.StepVector, error) {
172172
var err error
173173

174174
if !o.isExtFunction {
175-
rangeSamples, err = selectPoints(series.samples, o.limits, seriesTs, mint, maxt, o.scanners[i].previousSamples)
175+
rangeSamples, err = selectPoints(series.samples, o.limits, ts, mint, maxt, o.scanners[i].previousSamples)
176176
} else {
177-
rangeSamples, err = selectExtPoints(series.samples, o.limits, seriesTs, mint, maxt, o.scanners[i].previousSamples, o.extLookbackDelta, &o.scanners[i].metricAppearedTs)
177+
rangeSamples, err = selectExtPoints(series.samples, o.limits, ts, mint, maxt, o.scanners[i].previousSamples, o.extLookbackDelta, &o.scanners[i].metricAppearedTs)
178178
}
179179

180180
if err != nil {

0 commit comments

Comments
 (0)