github.com/ergo-services/ergo@v1.999.224/tests/saga_test.go (about) 1 package tests 2 3 import ( 4 "fmt" 5 "math/rand" 6 "testing" 7 "time" 8 9 "github.com/ergo-services/ergo" 10 "github.com/ergo-services/ergo/etf" 11 "github.com/ergo-services/ergo/gen" 12 "github.com/ergo-services/ergo/node" 13 ) 14 15 // 16 // Worker 17 // 18 type testSagaWorker struct { 19 gen.SagaWorker 20 } 21 22 func (w *testSagaWorker) HandleJobStart(process *gen.SagaWorkerProcess, job gen.SagaJob) error { 23 values := job.Value.([]int) 24 result := sumSlice(values) 25 if err := process.SendResult(result); err != nil { 26 panic(err) 27 } 28 return nil 29 } 30 func (w *testSagaWorker) HandleJobCancel(process *gen.SagaWorkerProcess, reason string) { 31 return 32 } 33 34 // 35 // Saga 36 // 37 type testSaga struct { 38 gen.Saga 39 res chan interface{} 40 result int 41 } 42 43 type testSagaState struct { 44 txs map[gen.SagaTransactionID]*txjobs 45 } 46 47 type txjobs struct { 48 result int 49 jobs map[gen.SagaJobID]bool 50 } 51 52 func (gs *testSaga) InitSaga(process *gen.SagaProcess, args ...etf.Term) (gen.SagaOptions, error) { 53 opts := gen.SagaOptions{ 54 Worker: &testSagaWorker{}, 55 } 56 gs.res = make(chan interface{}, 2) 57 process.State = &testSagaState{ 58 txs: make(map[gen.SagaTransactionID]*txjobs), 59 } 60 return opts, nil 61 } 62 63 func (gs *testSaga) HandleTxNew(process *gen.SagaProcess, id gen.SagaTransactionID, value interface{}) gen.SagaStatus { 64 task := value.(taskTX) 65 values := splitSlice(task.value, task.chunks) 66 state := process.State.(*testSagaState) 67 j := txjobs{ 68 jobs: make(map[gen.SagaJobID]bool), 69 } 70 for i := range values { 71 job_id, err := process.StartJob(id, gen.SagaJobOptions{}, values[i]) 72 if err != nil { 73 return err 74 } 75 j.jobs[job_id] = true 76 } 77 state.txs[id] = &j 78 return gen.SagaStatusOK 79 } 80 81 func (gs *testSaga) HandleTxDone(process *gen.SagaProcess, id gen.SagaTransactionID, result interface{}) (interface{}, gen.SagaStatus) { 82 state := process.State.(*testSagaState) 83 84 gs.result += result.(int) 85 delete(state.txs, id) 86 if len(state.txs) == 0 { 87 gs.res <- gs.result 88 } 89 return nil, gen.SagaStatusOK 90 } 91 92 func (gs *testSaga) HandleTxCancel(process *gen.SagaProcess, id gen.SagaTransactionID, reason string) gen.SagaStatus { 93 return gen.SagaStatusOK 94 } 95 96 func (gs *testSaga) HandleTxResult(process *gen.SagaProcess, id gen.SagaTransactionID, from gen.SagaNextID, result interface{}) gen.SagaStatus { 97 return gen.SagaStatusOK 98 } 99 100 func (gs *testSaga) HandleTxInterim(process *gen.SagaProcess, id gen.SagaTransactionID, from gen.SagaNextID, interim interface{}) gen.SagaStatus { 101 102 return gen.SagaStatusOK 103 } 104 105 func (gs *testSaga) HandleJobResult(process *gen.SagaProcess, id gen.SagaTransactionID, from gen.SagaJobID, result interface{}) gen.SagaStatus { 106 state := process.State.(*testSagaState) 107 j := state.txs[id] 108 j.result += result.(int) 109 delete(j.jobs, from) 110 111 if len(j.jobs) == 0 { 112 process.SendResult(id, j.result) 113 } 114 return gen.SagaStatusOK 115 } 116 117 type task struct { 118 value []int 119 split int 120 chunks int 121 } 122 123 type taskTX struct { 124 value []int 125 chunks int 126 } 127 128 func (gs *testSaga) HandleSagaDirect(process *gen.SagaProcess, ref etf.Ref, message interface{}) (interface{}, gen.DirectStatus) { 129 switch m := message.(type) { 130 case task: 131 values := splitSlice(m.value, m.split) 132 fmt.Printf(" process %v txs with %v value(s) each and chunk size %v: ", len(values), m.split, m.chunks) 133 for i := range values { 134 txValue := taskTX{ 135 value: values[i], 136 chunks: m.chunks, 137 } 138 process.StartTransaction(gen.SagaTransactionOptions{}, txValue) 139 } 140 141 return nil, gen.DirectStatusOK 142 } 143 144 return nil, fmt.Errorf("unknown request %#v", message) 145 } 146 147 func TestSagaSimple(t *testing.T) { 148 fmt.Printf("\n=== Test GenSagaSimple\n") 149 fmt.Printf("Starting node: nodeGenSagaSimple01@localhost...") 150 151 node, _ := ergo.StartNode("nodeGenSagaSimple01@localhost", "cookies", node.Options{}) 152 153 if node == nil { 154 t.Fatal("can't start node") 155 return 156 } 157 fmt.Println("OK") 158 159 fmt.Printf("... Starting Saga processes: ") 160 saga := &testSaga{} 161 saga_process, err := node.Spawn("saga", gen.ProcessOptions{MailboxSize: 10000}, saga) 162 if err != nil { 163 t.Fatal(err) 164 } 165 fmt.Println("OK") 166 167 rand.Seed(time.Now().Unix()) 168 169 slice1 := rand.Perm(1000) 170 sum1 := sumSlice(slice1) 171 startTask1 := task{ 172 value: slice1, 173 split: 10, // 10 items per tx 174 chunks: 5, // size of slice for worker 175 } 176 _, err = saga_process.Direct(startTask1) 177 if err != nil { 178 t.Fatal(err) 179 } 180 waitForResultWithValue(t, saga.res, sum1) 181 182 saga.result = 0 183 slice2 := rand.Perm(100) 184 sum2 := sumSlice(slice2) 185 startTask2 := task{ 186 value: slice2, 187 split: 1, // 1 items per tx 188 chunks: 5, // size of slice for worker 189 } 190 _, err = saga_process.Direct(startTask2) 191 if err != nil { 192 t.Fatal(err) 193 } 194 waitForResultWithValue(t, saga.res, sum2) 195 196 saga.result = 0 197 slice3 := rand.Perm(100) 198 sum3 := sumSlice(slice3) 199 startTask3 := task{ 200 value: slice3, 201 split: 100, // 100 items per tx 202 chunks: 5, // size of slice for worker 203 } 204 _, err = saga_process.Direct(startTask3) 205 if err != nil { 206 t.Fatal(err) 207 } 208 waitForResultWithValue(t, saga.res, sum3) 209 210 saga.result = 0 211 slice4 := rand.Perm(10000) 212 sum4 := sumSlice(slice4) 213 startTask4 := task{ 214 value: slice4, 215 split: 100, // 100 items per tx 216 chunks: 5, // size of slice for worker 217 } 218 _, err = saga_process.Direct(startTask4) 219 if err != nil { 220 t.Fatal(err) 221 } 222 waitForResultWithValue(t, saga.res, sum4) 223 node.Stop() 224 node.Wait() 225 } 226 227 func splitSlice(slice []int, size int) [][]int { 228 var chunks [][]int 229 for i := 0; i < len(slice); i += size { 230 end := i + size 231 if end > len(slice) { 232 end = len(slice) 233 } 234 chunks = append(chunks, slice[i:end]) 235 } 236 return chunks 237 } 238 239 func sumSlice(slice []int) int { 240 var result int 241 for i := range slice { 242 result += slice[i] 243 } 244 return result 245 }