mirror of
https://gitee.com/rulego/streamsql.git
synced 2025-07-07 16:30:58 +00:00
119 lines
2.3 KiB
Go
119 lines
2.3 KiB
Go
package window
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/rulego/streamsql/model"
|
|
"github.com/spf13/cast"
|
|
)
|
|
|
|
var _ Window = (*CountingWindow)(nil)
|
|
|
|
type CountingWindow struct {
|
|
config model.WindowConfig
|
|
threshold int
|
|
count int
|
|
mu sync.Mutex
|
|
callback func([]model.Row)
|
|
dataBuffer []model.Row
|
|
outputChan chan []model.Row
|
|
ctx context.Context
|
|
cancelFunc context.CancelFunc
|
|
ticker *time.Ticker
|
|
triggerChan chan struct{}
|
|
}
|
|
|
|
func NewCountingWindow(config model.WindowConfig) (*CountingWindow, error) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
threshold := cast.ToInt(config.Params["count"])
|
|
if threshold <= 0 {
|
|
return nil, fmt.Errorf("threshold must be a positive integer")
|
|
}
|
|
|
|
cw := &CountingWindow{
|
|
threshold: threshold,
|
|
dataBuffer: make([]model.Row, 0, threshold),
|
|
outputChan: make(chan []model.Row, 10),
|
|
ctx: ctx,
|
|
cancelFunc: cancel,
|
|
triggerChan: make(chan struct{}, 1),
|
|
}
|
|
|
|
if callback, ok := config.Params["callback"].(func([]model.Row)); ok {
|
|
cw.SetCallback(callback)
|
|
}
|
|
return cw, nil
|
|
}
|
|
|
|
func (cw *CountingWindow) Add(data interface{}) {
|
|
cw.mu.Lock()
|
|
defer cw.mu.Unlock()
|
|
row := model.Row{
|
|
Data: data,
|
|
Timestamp: GetTimestamp(data, cw.config.TsProp),
|
|
}
|
|
cw.dataBuffer = append(cw.dataBuffer, row)
|
|
cw.count++
|
|
shouldTrigger := cw.count >= cw.threshold
|
|
|
|
if shouldTrigger {
|
|
go func() {
|
|
if cw.callback != nil {
|
|
cw.callback(cw.dataBuffer)
|
|
}
|
|
cw.outputChan <- cw.dataBuffer
|
|
cw.Reset()
|
|
}()
|
|
}
|
|
}
|
|
func (cw *CountingWindow) Start() {
|
|
go func() {
|
|
cw.ticker = time.NewTicker(1 * time.Second)
|
|
defer func() {
|
|
cw.ticker.Stop()
|
|
cw.cancelFunc()
|
|
}()
|
|
|
|
for {
|
|
select {
|
|
case <-cw.ticker.C:
|
|
cw.Trigger()
|
|
case <-cw.ctx.Done():
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
func (cw *CountingWindow) Trigger() {
|
|
cw.triggerChan <- struct{}{}
|
|
|
|
go func() {
|
|
cw.mu.Lock()
|
|
defer cw.mu.Unlock()
|
|
|
|
if cw.callback != nil && len(cw.dataBuffer) > 0 {
|
|
cw.callback(cw.dataBuffer)
|
|
}
|
|
cw.Reset()
|
|
}()
|
|
}
|
|
|
|
func (cw *CountingWindow) Reset() {
|
|
cw.mu.Lock()
|
|
defer cw.mu.Unlock()
|
|
cw.count = 0
|
|
cw.dataBuffer = cw.dataBuffer[:0]
|
|
}
|
|
|
|
func (cw *CountingWindow) OutputChan() <-chan []model.Row {
|
|
return cw.outputChan
|
|
}
|
|
|
|
// func (cw *CountingWindow) GetResults() []interface{} {
|
|
// return append([]mode.Row, cw.dataBuffer...)
|
|
// }
|