aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsangtran <tranquangsang12.7@gmail.com>2026-09-05 17:06:30 +0700
committersangtran <tranquangsang12.7@gmail.com>2026-09-05 17:06:30 +0700
commite626cee8f7de0dd3ffca4c7297e59a87f790cc02 (patch)
tree1b57f5e3134459872890e3df7484be5cf3cb3ba6
parent67be8b5e2b5188d4291ed0a3c3a91b5e40bfbebe (diff)
signatureVerified sangtran <tranquangsang12.7@gmail.com> Key: 8DD79823D235E1BB ultimate trust
downloadflux-e626cee8f7de0dd3ffca4c7297e59a87f790cc02.tar.gz
flux-e626cee8f7de0dd3ffca4c7297e59a87f790cc02.zip
Verified feat: add on pebble state backend and using sync Pool to reuse buffer []byte
-rw-r--r--README.md7
-rw-r--r--core/pool.go38
-rw-r--r--core/state_backend.go18
-rw-r--r--go.mod2
-rw-r--r--state/memory_backend.go8
-rw-r--r--state/pebble_backend.go30
-rw-r--r--state/pebble_state.go54
7 files changed, 137 insertions, 20 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a180257
--- /dev/null
+++ b/README.md
@@ -0,0 +1,7 @@
1Flux
2====
3
4Flux is an event-driven stream processing engine written in Go,
5designed from first principles to handle stateful stream computation,
6out-of-order event time processing, high-throughput windowing,
7and distributed fault-tolerant state backend.
diff --git a/core/pool.go b/core/pool.go
new file mode 100644
index 0000000..484b342
--- /dev/null
+++ b/core/pool.go
@@ -0,0 +1,38 @@
1package core
2
3import "sync"
4
5
6const (
7 DefaultBufferSize = 1024
8 MaxBufferSize = 64 * 1024
9)
10
11var bytesPool = sync.Pool{
12 New: func () any {
13 b := make([]byte, DefaultBufferSize)
14 return &b
15 },
16}
17
18func GetBuffer(size int) []byte {
19 bufPtr := bytesPool.Get().(*[]byte)
20
21 buf := *bufPtr
22
23
24 if size > cap(buf) {
25 buf = make([]byte, size)
26 }
27
28 return buf[:size]
29}
30
31
32func PutBuffer(buf []byte) {
33 if cap(buf) < MaxBufferSize{
34 // reset
35 buf = buf[:0]
36 bytesPool.Put(&buf)
37 }
38}
diff --git a/core/state_backend.go b/core/state_backend.go
index 5870739..ae9fe1d 100644
--- a/core/state_backend.go
+++ b/core/state_backend.go
@@ -2,6 +2,9 @@ package core
2 2
3import ( 3import (
4 "context" 4 "context"
5 "encoding/json"
6 "fmt"
7
5) 8)
6 9
7type StateBackend interface { 10type StateBackend interface {
@@ -22,6 +25,7 @@ type stateBackendKeyType struct{}
22 25
23var stateBackendKey stateBackendKeyType 26var stateBackendKey stateBackendKeyType
24 27
28// type adapter
25type typedValueState[T any] struct { 29type typedValueState[T any] struct {
26 inner ValueState[any] 30 inner ValueState[any]
27} 31}
@@ -32,14 +36,24 @@ func CastValueType[T any](inner ValueState[any]) ValueState[T] {
32 36
33func (s *typedValueState[T]) Value(ctx context.Context) (T, error) { 37func (s *typedValueState[T]) Value(ctx context.Context) (T, error) {
34 val, err := s.inner.Value(ctx) 38 val, err := s.inner.Value(ctx)
39 var zero T
35 if err != nil { 40 if err != nil {
36 var zero T
37 return zero, err 41 return zero, err
38 } 42 }
39 if val == nil { 43 if val == nil {
40 var zero T
41 return zero, nil 44 return zero, nil
42 } 45 }
46
47 if rawBytes, ok := val.([]byte); ok {
48 defer PutBuffer(rawBytes)
49
50 var result T
51 if err := json.Unmarshal(rawBytes, &result); err != nil {
52 return zero, fmt.Errorf("failed to unmarshal state: %w", err)
53 }
54 return result, nil
55 }
56
43 return val.(T), nil 57 return val.(T), nil
44} 58}
45 59
diff --git a/go.mod b/go.mod
index df5991f..a99ec15 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
1module flux 1module flux
2 2
3go 1.26.3 3go 1.27
4 4
5require github.com/cockroachdb/pebble/v2 v2.1.7 5require github.com/cockroachdb/pebble/v2 v2.1.7
6 6
diff --git a/state/memory_backend.go b/state/memory_backend.go
index dda577e..cce8194 100644
--- a/state/memory_backend.go
+++ b/state/memory_backend.go
@@ -39,15 +39,15 @@ func (ms *MemoryStateBackend) SetCurrentKey(key string) error {
39 return nil 39 return nil
40} 40}
41 41
42func (ms *MemoryStateBackend) GetValueState(ctx context.Context, statename string) (core.ValueState[any], error) { 42func (ms *MemoryStateBackend) GetValueState(ctx context.Context, stateName string) (core.ValueState[any], error) {
43 // Assign if state not existed 43 // Assign if state not existed
44 44
45 if _, ok := ms.states[statename]; !ok { 45 if _, ok := ms.states[stateName]; !ok {
46 // create state name map if not existed 46 // create state name map if not existed
47 ms.states[statename] = make(map[int]map[string]map[any]any) 47 ms.states[stateName] = make(map[int]map[string]map[any]any)
48 } 48 }
49 49
50 return &memoryStateValue{backend: ms, stateName: statename}, nil 50 return &memoryStateValue{backend: ms, stateName: stateName}, nil
51} 51}
52 52
53func (ms *MemoryStateBackend) GetCurrentNamespace() any { 53func (ms *MemoryStateBackend) GetCurrentNamespace() any {
diff --git a/state/pebble_backend.go b/state/pebble_backend.go
index e428988..fbb8d68 100644
--- a/state/pebble_backend.go
+++ b/state/pebble_backend.go
@@ -33,10 +33,11 @@ func NewPebbleStateBackend(dir string, maxParalleism int) (*PebbleStateBackend,
33 return nil, fmt.Errorf("failed to open pebbledb %w", err) 33 return nil, fmt.Errorf("failed to open pebbledb %w", err)
34 } 34 }
35 return &PebbleStateBackend{ 35 return &PebbleStateBackend{
36 maxParallelism: maxParalleism, 36 maxParallelism: maxParalleism,
37 db: db, 37 db: db,
38 stateMap: make(map[string]uint8), 38 stateMap: make(map[string]uint8),
39 nextStateID: 1, 39 nextStateID: 1,
40 currentNamespace: &core.VoidNamespace{},
40 }, nil 41 }, nil
41} 42}
42 43
@@ -60,23 +61,27 @@ func (p *PebbleStateBackend) GetCurrentNamespace() any {
60} 61}
61 62
62// GetValueState(ctx context.Context, key string) (ValueState[any], error) 63// GetValueState(ctx context.Context, key string) (ValueState[any], error)
63func (p *PebbleStateBackend) GetValueState(ctx context.Context, statename string) (core.ValueState[any], error) { 64func (p *PebbleStateBackend) GetValueState(ctx context.Context, stateName string) (core.ValueState[any], error) {
64 stateID := p.getStateID(statename) 65 stateID := p.getStateID(stateName)
65 return nil, nil 66
67 return &pebbleStateValue{
68 backend: p,
69 stateID: stateID,
70 }, nil
66} 71}
67 72
68func (p *PebbleStateBackend) getStateID(statename string) uint8 { 73func (p *PebbleStateBackend) getStateID(stateName string) uint8 {
69 p.mu.Lock() 74 p.mu.Lock()
70 defer p.mu.Unlock() 75 defer p.mu.Unlock()
71 76
72 if _, ok := p.stateMap[statename]; !ok { 77 if _, ok := p.stateMap[stateName]; !ok {
73 78
74 id := p.nextStateID 79 id := p.nextStateID
75 p.stateMap[statename] = id 80 p.stateMap[stateName] = id
76 p.nextStateID++ 81 p.nextStateID++
77 } 82 }
78 83
79 return p.stateMap[statename] 84 return p.stateMap[stateName]
80} 85}
81 86
82func (p *PebbleStateBackend) calculateNamespaceSize() int { 87func (p *PebbleStateBackend) calculateNamespaceSize() int {
@@ -94,7 +99,7 @@ func (p *PebbleStateBackend) calculateNamespaceSize() int {
94 return nsLen 99 return nsLen
95} 100}
96 101
97func (p *PebbleStateBackend) deserialize(statename string) []byte { 102func (p *PebbleStateBackend) deserialize(stateID uint8) []byte {
98 // this will store in SSTable, order is important, keygroup MUST be first for scaling 103 // this will store in SSTable, order is important, keygroup MUST be first for scaling
99 // 2B(KeyGroup) + 1B(StateID) + 2B(KeyLen) + len(Key) + 16B(Window) 104 // 2B(KeyGroup) + 1B(StateID) + 2B(KeyLen) + len(Key) + 16B(Window)
100 105
@@ -105,7 +110,6 @@ func (p *PebbleStateBackend) deserialize(statename string) []byte {
105 b = binary.BigEndian.AppendUint16(b, uint16(p.currentGroupKey)) 110 b = binary.BigEndian.AppendUint16(b, uint16(p.currentGroupKey))
106 111
107 // add state id 112 // add state id
108 stateID := p.getStateID(statename)
109 b = append(b, stateID) 113 b = append(b, stateID)
110 114
111 // add key 115 // add key
diff --git a/state/pebble_state.go b/state/pebble_state.go
new file mode 100644
index 0000000..3eeaa05
--- /dev/null
+++ b/state/pebble_state.go
@@ -0,0 +1,54 @@
1package state
2
3import (
4 "context"
5 "encoding/json"
6 "errors"
7 "flux/core"
8
9 "github.com/cockroachdb/pebble/v2"
10)
11
12type pebbleStateValue struct {
13 backend *PebbleStateBackend
14 stateID uint8
15}
16
17func (p *pebbleStateValue) Update(ctx context.Context, newVal any) (any, error) {
18 key := p.backend.deserialize(p.stateID)
19 // convert newVal to []byte raw
20 v, err := json.Marshal(newVal)
21 if err != nil {
22 return nil, err
23 }
24
25 err = p.backend.db.Set(key, v, pebble.NoSync)
26 if err != nil {
27 return nil, err
28 }
29
30 return newVal, nil
31}
32
33func (p *pebbleStateValue) Value(ctx context.Context) (any, error) {
34 key := p.backend.deserialize(p.stateID)
35 v, closer, err := p.backend.db.Get(key)
36 if err != nil {
37 if errors.Is(err, pebble.ErrNotFound) {
38 return nil, nil
39 }
40 return nil, err
41 }
42
43 defer closer.Close()
44
45 res := core.GetBuffer(len(v))
46 copy(res, v)
47 return v, nil
48}
49
50func (p *pebbleStateValue) Clear(ctx context.Context) error {
51 key := p.backend.deserialize(p.stateID)
52
53 return p.backend.db.Delete(key, pebble.NoSync)
54}