aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsangtran <tranquangsang12.7@gmail.com>2026-08-31 23:51:37 +0700
committersangtran <tranquangsang12.7@gmail.com>2026-08-31 23:51:37 +0700
commit6631d8b00e8c5bafea15ea24415c5b55439193ee (patch)
treea320af141ba14bb02c669dbccbf00bcc850c6dbb
parent96c07bab1086510a55a004e9a1b90c30bc723a5c (diff)
signatureVerified sangtran <tranquangsang12.7@gmail.com> Key: 8DD79823D235E1BB ultimate trust
downloadflux-6631d8b00e8c5bafea15ea24415c5b55439193ee.tar.gz
flux-6631d8b00e8c5bafea15ea24415c5b55439193ee.zip
Verified bug: fix memory_state clear and guard window state empty with implement main.go for test
-rw-r--r--core/operator/window.go4
-rw-r--r--main.go55
-rw-r--r--state/memory_backend.go21
-rw-r--r--state/memory_state.go18
4 files changed, 65 insertions, 33 deletions
diff --git a/core/operator/window.go b/core/operator/window.go
index 610f6e3..3f75d60 100644
--- a/core/operator/window.go
+++ b/core/operator/window.go
@@ -79,6 +79,10 @@ func (o *WindowOperator[IN, OUT]) Process(ctx context.Context, record core.Strea
79 return err 79 return err
80 } 80 }
81 81
82 if len(currentWindowState) == 0 {
83 continue
84 }
85
82 // NOTE: this is workaround create inline window, refactor later 86 // NOTE: this is workaround create inline window, refactor later
83 87
84 if err := o.userfunc.Apply(ctx, timer.Key, timer.Namespace.(core.Window), currentWindowState, out); err != nil { 88 if err := o.userfunc.Apply(ctx, timer.Key, timer.Namespace.(core.Window), currentWindowState, out); err != nil {
diff --git a/main.go b/main.go
index 1b4a4be..91b96e3 100644
--- a/main.go
+++ b/main.go
@@ -19,24 +19,24 @@ type Transaction struct {
19 Timestamp int64 19 Timestamp int64
20} 20}
21 21
22type TransactionSummary[IN Transaction, OUT Transaction] struct { 22type TransactionSummary[IN Transaction, OUT string] struct {
23 totalTransaction int 23 totalTransaction int
24 totalAmount int
25 average int 24 average int
26} 25}
27 26
28func (t *TransactionSummary[IN, OUT]) Apply(ctx context.Context, key string, window core.Window, in []Transaction, out transport.Emitter[Transaction]) error { 27func (t *TransactionSummary[IN, OUT]) Apply(ctx context.Context, key string, window core.Window, in []Transaction, out operator.Emitter[string]) error {
28 totalAmount := 0
29 for _, v := range in { 29 for _, v := range in {
30 t.totalAmount += v.Amount 30 totalAmount += v.Amount
31 out.Emit(ctx, core.StreamRecord[Transaction]{
32 Value: v,
33 Type: core.RecordData,
34 Timestamp: v.Timestamp,
35 })
36 } 31 }
37 32
38 return nil 33 msg := fmt.Sprintf("Khách: %s | Số đơn: %d | Tổng tiền: %d$", key, len(in), totalAmount)
39} 34
35 return out.Emit(ctx, core.StreamRecord[string]{
36 Value: msg,
37 Type: core.RecordData,
38 Timestamp: window.MaxTimestamp(),
39 })}
40 40
41func main() { 41func main() {
42 // init 42 // init
@@ -55,25 +55,45 @@ func main() {
55 { 55 {
56 User: "Sang", 56 User: "Sang",
57 Amount: 4, 57 Amount: 4,
58 Timestamp: 1200, 58 Timestamp: 1500,
59 },
60 {
61 User: "Sang",
62 Amount: 10,
63 Timestamp: 2000,
64 },
65 {
66 User: "Sang",
67 Amount: 12,
68 Timestamp: 2500,
59 }, 69 },
60 { 70 {
61 User: "Sang", 71 User: "Sang",
62 Amount: 10, 72 Amount: 10,
63 Timestamp: 1600, 73 Timestamp: 3000,
64 }, 74 },
65 } 75 }
66 76
67 ch1 := make(chan core.StreamRecord[Transaction], len(txList)) 77 ch1 := make(chan core.StreamRecord[Transaction], len(txList))
68 ch2 := make(chan core.StreamRecord[Transaction], len(txList)) 78 ch2 := make(chan core.StreamRecord[Transaction], len(txList))
79 ch3 := make(chan core.StreamRecord[Transaction], len(txList))
80 ch4 := make(chan core.StreamRecord[string], len(txList))
69 81
70 wo := operator.NewWatermarkGeneratorOperator[Transaction](0, func(in Transaction) int64 { 82 wo := operator.NewWatermarkGeneratorOperator[Transaction](0, func(in Transaction) int64 {
71 return in.Timestamp 83 return in.Timestamp
72 }) 84 })
73 85
86 ko := operator.NewKeyByOperator[Transaction](func(in Transaction) string {
87 return in.User
88 })
89
90 wop := operator.NewWindowOperator[Transaction, string](core.NewTumblingWindows[Transaction](1000), &TransactionSummary[Transaction, string]{})
91
74 var wg sync.WaitGroup 92 var wg sync.WaitGroup
75 93
76 runtime.RunTask[Transaction, Transaction](ctx, transport.NewLocalReceiveChannel(ch1), transport.NewLocalEmitChannel(ch2), wo) 94 runtime.RunTask(ctx, transport.NewLocalReceiveChannel(ch1), transport.NewLocalEmitChannel(ch2), wo)
95 runtime.RunTask(ctx, transport.NewLocalReceiveChannel(ch2), transport.NewLocalEmitChannel(ch3), ko)
96 runtime.RunTask(ctx, transport.NewLocalReceiveChannel(ch3), transport.NewLocalEmitChannel(ch4), wop)
77 97
78 wg.Go(func() { 98 wg.Go(func() {
79 for i, tx := range txList { 99 for i, tx := range txList {
@@ -84,13 +104,12 @@ func main() {
84 }) 104 })
85 105
86 wg.Go(func() { 106 wg.Go(func() {
87 for v := range ch2 { 107 for v := range ch4 {
88 if v.Type == core.RecordData { 108 if v.Type == core.RecordData {
89 fmt.Println("data: ", v.Value) 109 fmt.Println("Total Amount after: ", v.Value)
90 } else {
91 fmt.Println("watermark: ", v.Timestamp)
92 } 110 }
93 } 111 }
94 }) 112 })
113
95 wg.Wait() 114 wg.Wait()
96} 115}
diff --git a/state/memory_backend.go b/state/memory_backend.go
index 6594657..f4043a1 100644
--- a/state/memory_backend.go
+++ b/state/memory_backend.go
@@ -2,6 +2,7 @@ package state
2 2
3import ( 3import (
4 "context" 4 "context"
5
5 "flux/core" 6 "flux/core"
6) 7)
7 8
@@ -11,9 +12,9 @@ import (
11// This should be optimize with ZERO GC Scanning, Memory Alignment 12// This should be optimize with ZERO GC Scanning, Memory Alignment
12 13
13type MemoryStateBackend struct { 14type MemoryStateBackend struct {
14 maxParallelism int 15 maxParallelism int
15 currentKey string 16 currentKey string
16 currentGroupKey int 17 currentGroupKey int
17 currentNamespace any 18 currentNamespace any
18 // 4D structure StateName -> KeyGroup -> Key -> Namespace -> Value 19 // 4D structure StateName -> KeyGroup -> Key -> Namespace -> Value
19 states map[string]map[int]map[string]map[any]any 20 states map[string]map[int]map[string]map[any]any
@@ -36,18 +37,18 @@ func (ms *MemoryStateBackend) SetCurrentKey(key string) error {
36 return nil 37 return nil
37} 38}
38 39
39func (ms *MemoryStateBackend) GetValueState(ctx context.Context, key string) (core.ValueState[any], error) { 40func (ms *MemoryStateBackend) GetValueState(ctx context.Context, statename string) (core.ValueState[any], error) {
40 // Assign if state not existed 41 // Assign if state not existed
41 42
42 if _, ok := ms.states[key]; !ok { 43 if _, ok := ms.states[statename]; !ok {
43 ms.states[key] = make(map[int]map[string]map[any]any) 44 // create state name map if not existed
45 ms.states[statename] = make(map[int]map[string]map[any]any)
44 } 46 }
45 47
46 return &memoryStateValue{backend: ms, stateName: key}, nil 48 return &memoryStateValue{backend: ms, stateName: statename}, nil
47} 49}
48 50
49func (ms *MemoryStateBackend) GetCurrentNamespace() any{ 51func (ms *MemoryStateBackend) GetCurrentNamespace() any {
50
51 return ms.currentNamespace 52 return ms.currentNamespace
52} 53}
53 54
diff --git a/state/memory_state.go b/state/memory_state.go
index 95c7318..97ecad1 100644
--- a/state/memory_state.go
+++ b/state/memory_state.go
@@ -1,7 +1,7 @@
1package state 1package state
2 2
3import ( 3import (
4"context" 4 "context"
5) 5)
6 6
7type memoryStateValue struct { 7type memoryStateValue struct {
@@ -13,7 +13,7 @@ func (m *memoryStateValue) Value(ctx context.Context) (any, error) {
13 kg := m.backend.currentGroupKey 13 kg := m.backend.currentGroupKey
14 key := m.backend.currentKey 14 key := m.backend.currentKey
15 ns := m.backend.currentNamespace 15 ns := m.backend.currentNamespace
16 if kgMap, exist := m.backend.states[m.stateName][kg][key]; exist { 16 if kgMap, ok := m.backend.states[m.stateName][kg][key]; ok {
17 if v, hasKey := kgMap[ns]; hasKey { 17 if v, hasKey := kgMap[ns]; hasKey {
18 return v, nil 18 return v, nil
19 } 19 }
@@ -26,10 +26,15 @@ func (m *memoryStateValue) Update(ctx context.Context, newVal any) (any, error)
26 kg := m.backend.currentGroupKey 26 kg := m.backend.currentGroupKey
27 key := m.backend.currentKey 27 key := m.backend.currentKey
28 ns := m.backend.currentNamespace 28 ns := m.backend.currentNamespace
29 if _, exists := m.backend.states[m.stateName][kg][key]; !exists { 29
30 if _, ok := m.backend.states[m.stateName][kg]; !ok {
30 m.backend.states[m.stateName][kg] = make(map[string]map[any]any) 31 m.backend.states[m.stateName][kg] = make(map[string]map[any]any)
31 } 32 }
32 33
34 if _, ok := m.backend.states[m.stateName][kg][key]; !ok {
35 m.backend.states[m.stateName][kg][key] = make(map[any]any)
36 }
37
33 m.backend.states[m.stateName][kg][key][ns] = newVal 38 m.backend.states[m.stateName][kg][key][ns] = newVal
34 return newVal, nil 39 return newVal, nil
35} 40}
@@ -37,8 +42,11 @@ func (m *memoryStateValue) Update(ctx context.Context, newVal any) (any, error)
37func (m *memoryStateValue) Clear(ctx context.Context) error { 42func (m *memoryStateValue) Clear(ctx context.Context) error {
38 kg := m.backend.currentGroupKey 43 kg := m.backend.currentGroupKey
39 key := m.backend.currentKey 44 key := m.backend.currentKey
40 if kgMap, exists := m.backend.states[m.stateName][kg]; exists { 45 ns := m.backend.currentNamespace
41 delete(kgMap, key) 46 if kgMap, ok := m.backend.states[m.stateName][kg]; ok {
47 if nsMap, ok := kgMap[key]; ok {
48 delete(nsMap, ns)
49 }
42 } 50 }
43 return nil 51 return nil
44} 52}