github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/logbook/oplog/simulate_test.go (about) 1 package oplog 2 3 import ( 4 "context" 5 "fmt" 6 "math/rand" 7 "testing" 8 "time" 9 ) 10 11 func TestSimulateLogReplication(t *testing.T) { 12 t.Skip("TODO (b5): restore simulation test") 13 14 ctx, done := context.WithCancel(context.Background()) 15 defer done() 16 17 og := &opGenerator{ctx: ctx, NoopProb: 95} 18 sim := NewSimulation(ctx, 5, og) 19 sim.Setup() 20 sim.Run(100) 21 22 fmt.Println("ops generated", og.opsGenerated) 23 // for _, p := range sim.Peers { 24 // fmt.Printf("peer %d. sent: %d \treceived: %d \tlen: %d\n", p.ID, p.msgsSent, p.msgsReceived, p.Log.Len()) 25 // } 26 27 // for _, p := range sim.Peers { 28 // fmt.Println() 29 // state := p.Log.State() 30 // data, _ := json.Marshal(state) 31 // fmt.Println(string(data)) 32 // } 33 } 34 35 type Simulation struct { 36 ctx context.Context 37 Peers []*Peer 38 } 39 40 func NewSimulation(ctx context.Context, peerCount int, gen *opGenerator) *Simulation { 41 s := &Simulation{ctx: ctx, Peers: make([]*Peer, peerCount)} 42 for i := 0; i < peerCount; i++ { 43 p := &Peer{ 44 ID: i, 45 Log: Log{}, 46 Inbox: make(chan Op, peerCount), 47 48 ops: gen, 49 ticks: make(chan struct{}), 50 } 51 s.Peers[i] = p 52 } 53 54 return s 55 } 56 57 func (s Simulation) Setup() { 58 // wire up all peers into a star topology 59 for i, a := range s.Peers { 60 a.Start(s.ctx) 61 for _, b := range s.Peers[i+1:] { 62 a.Downstreams = append(a.Downstreams, b.Inbox) 63 b.Downstreams = append(b.Downstreams, a.Inbox) 64 } 65 } 66 } 67 68 func (s Simulation) Run(ticks int) { 69 for t := 0; t <= ticks; t++ { 70 for _, p := range s.Peers { 71 p.Tick(t) 72 } 73 } 74 75 for _, p := range s.Peers { 76 p.Finalize() 77 } 78 } 79 80 type opGenerator struct { 81 ctx context.Context 82 NoopProb int 83 opsGenerated int 84 } 85 86 func (og *opGenerator) MaybeGen(id int) Op { 87 var o Op 88 i := rand.Intn(100) 89 if i > og.NoopProb { 90 // Author: fmt.Sprintf("%d", id) 91 o = Op{Ref: fmt.Sprintf("%d", og.opsGenerated), Note: fmt.Sprintf("op number %d", og.opsGenerated)} 92 og.opsGenerated++ 93 } 94 95 return o 96 } 97 98 func (og *opGenerator) Gen() Op { 99 // Author: fmt.Sprintf("%d", id) 100 o := Op{ 101 Ref: fmt.Sprintf("hash_number_%d_plus_somefiller_text", og.opsGenerated), 102 Prev: fmt.Sprintf("hash_number_%d_plus_somefiller_text", og.opsGenerated-1), 103 Timestamp: time.Now().UnixNano(), 104 Note: fmt.Sprintf("op number %d with a long description that shows how stuff gets long and stuff", og.opsGenerated), 105 } 106 og.opsGenerated++ 107 108 return o 109 } 110 111 type Peer struct { 112 ID int 113 Log Log 114 Inbox chan Op 115 Downstreams []chan Op 116 117 ops *opGenerator 118 message *Op 119 msgsSent int 120 msgsReceived int 121 ticks chan struct{} 122 } 123 124 func (p *Peer) Start(ctx context.Context) { 125 go func() { 126 for { 127 <-p.ticks 128 // fmt.Printf("%d ticked\n", p.ID) 129 select { 130 case msg := <-p.Inbox: 131 p.message = &msg 132 case <-ctx.Done(): 133 return 134 } 135 } 136 }() 137 } 138 139 func (p *Peer) Tick(t int) { 140 go func() { p.ticks <- struct{}{} }() 141 142 if p.message != nil { 143 p.processMessage() 144 return 145 } 146 147 op := p.ops.MaybeGen(p.ID) 148 // TODO (b5) - need to restore with a new mechanism for put 149 // p.Log = p.Log.Put(op) 150 p.msgsSent++ 151 for _, ds := range p.Downstreams { 152 ds <- op 153 } 154 } 155 156 func (p *Peer) Finalize() { 157 for len(p.Inbox) > 0 { 158 fmt.Println("draining message") 159 msg := <-p.Inbox 160 p.message = &msg 161 p.processMessage() 162 } 163 } 164 165 func (p *Peer) processMessage() { 166 // op := *p.message 167 p.msgsReceived++ 168 // TODO (b5) - need to restore with a new mechanism for put 169 // p.Log = p.Log.Put(op) 170 p.message = nil 171 }