rewrite for single stream
This commit is contained in:
parent
978a320b43
commit
7bb3d5e144
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
@ -17,10 +18,12 @@ import (
|
||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/node/account"
|
||||
"go.uber.org/zap"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
@ -77,6 +80,9 @@ func main() {
|
||||
log.Info("test success!")
|
||||
}
|
||||
|
||||
b := &bench{service: a.MustComponent(consensusclient.CName).(consensusclient.Service)}
|
||||
b.run()
|
||||
|
||||
// wait exit signal
|
||||
exit := make(chan os.Signal, 1)
|
||||
signal.Notify(exit, os.Interrupt, syscall.SIGKILL, syscall.SIGTERM, syscall.SIGQUIT)
|
||||
@ -206,11 +212,18 @@ func testStream(service consensusclient.Service) (err error) {
|
||||
}
|
||||
log.Info("log created", zap.String("id", bson.ObjectId(newLogId).Hex()), zap.Duration("dur", time.Since(st)))
|
||||
|
||||
stream, err := service.WatchLog(ctx, newLogId)
|
||||
stream, err := service.WatchLog(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer stream.Close()
|
||||
|
||||
st = time.Now()
|
||||
if err = stream.WatchIds([][]byte{newLogId}); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("watch", zap.String("id", bson.ObjectId(newLogId).Hex()), zap.Duration("dur", time.Since(st)))
|
||||
|
||||
sr := readStream(stream)
|
||||
for i := 0; i < 10; i++ {
|
||||
st = time.Now()
|
||||
@ -226,35 +239,129 @@ func testStream(service consensusclient.Service) (err error) {
|
||||
lastRecId = recId
|
||||
log.Info("record created", zap.String("id", bson.ObjectId(lastRecId).Hex()), zap.Duration("dur", time.Since(st)))
|
||||
}
|
||||
fmt.Println(sr.log.Records)
|
||||
sr.validate()
|
||||
return nil
|
||||
}
|
||||
|
||||
func readStream(stream consensusproto.DRPCConsensus_WatchLogClient) *streamReader {
|
||||
sr := &streamReader{stream: stream}
|
||||
func readStream(stream consensusclient.Stream) *streamReader {
|
||||
sr := &streamReader{stream: stream, logs: map[string]*consensusproto.Log{}}
|
||||
go sr.read()
|
||||
return sr
|
||||
}
|
||||
|
||||
type streamReader struct {
|
||||
stream consensusproto.DRPCConsensus_WatchLogClient
|
||||
log *consensusproto.Log
|
||||
stream consensusclient.Stream
|
||||
logs map[string]*consensusproto.Log
|
||||
}
|
||||
|
||||
func (sr *streamReader) read() {
|
||||
for {
|
||||
event, err := sr.stream.Recv()
|
||||
if err != nil {
|
||||
recs := sr.stream.WaitLogs()
|
||||
if len(recs) == 0 {
|
||||
return
|
||||
}
|
||||
fmt.Println("received event", event)
|
||||
if sr.log == nil {
|
||||
sr.log = &consensusproto.Log{
|
||||
Id: event.LogId,
|
||||
Records: event.Records,
|
||||
for _, rec := range recs {
|
||||
if el, ok := sr.logs[string(rec.Id)]; !ok {
|
||||
sr.logs[string(rec.Id)] = &consensusproto.Log{
|
||||
Id: rec.Id,
|
||||
Records: rec.Records,
|
||||
}
|
||||
} else {
|
||||
sr.log.Records = append(event.Records, sr.log.Records...)
|
||||
el.Records = append(rec.Records, el.Records...)
|
||||
sr.logs[string(rec.Id)] = el
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sr *streamReader) validate() {
|
||||
var lc, rc int
|
||||
for _, log := range sr.logs {
|
||||
lc++
|
||||
rc += len(log.Records)
|
||||
validateLog(log)
|
||||
}
|
||||
fmt.Println("logs valid; log count:", lc, "records:", rc)
|
||||
}
|
||||
|
||||
func validateLog(log *consensusproto.Log) {
|
||||
var prevId []byte
|
||||
for _, rec := range log.Records {
|
||||
if len(prevId) != 0 {
|
||||
if !bytes.Equal(prevId, rec.Id) {
|
||||
panic(fmt.Sprintf("invalid log: %+v", log))
|
||||
}
|
||||
}
|
||||
prevId = rec.PrevId
|
||||
}
|
||||
}
|
||||
|
||||
type bench struct {
|
||||
service consensusclient.Service
|
||||
stream consensusclient.Stream
|
||||
}
|
||||
|
||||
func (b *bench) run() {
|
||||
var err error
|
||||
b.stream, err = b.service.WatchLog(context.Background())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer b.stream.Close()
|
||||
sr := readStream(b.stream)
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 100; i++ {
|
||||
time.Sleep(time.Second / 100)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
b.client()
|
||||
}()
|
||||
fmt.Println("total streams:", i+1)
|
||||
}
|
||||
wg.Wait()
|
||||
sr.validate()
|
||||
}
|
||||
|
||||
func (b *bench) client() {
|
||||
ctx := context.Background()
|
||||
|
||||
// create log
|
||||
newLogId := []byte(bson.NewObjectId())
|
||||
lastRecId := []byte(bson.NewObjectId())
|
||||
err := b.service.AddLog(ctx, &consensusproto.Log{
|
||||
Id: newLogId,
|
||||
Records: []*consensusproto.Record{
|
||||
{
|
||||
Id: lastRecId,
|
||||
Payload: []byte("test"),
|
||||
CreatedUnix: uint64(time.Now().Unix()),
|
||||
},
|
||||
},
|
||||
})
|
||||
for i := 0; i < 5; i++ {
|
||||
fmt.Println("watch", bson.ObjectId(newLogId).Hex())
|
||||
if err = b.stream.WatchIds([][]byte{newLogId}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for i := 0; i < rand.Intn(20); i++ {
|
||||
time.Sleep(time.Millisecond * time.Duration(rand.Intn(10000)))
|
||||
recId := []byte(bson.NewObjectId())
|
||||
err = b.service.AddRecord(ctx, newLogId, &consensusproto.Record{
|
||||
Id: recId,
|
||||
PrevId: lastRecId,
|
||||
Payload: []byte("some payload 1 2 3 4 5 6 6 7 oijoj"),
|
||||
CreatedUnix: uint64(time.Now().Unix()),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
lastRecId = recId
|
||||
}
|
||||
if err = b.stream.UnwatchIds([][]byte{newLogId}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("unwatch", bson.ObjectId(newLogId).Hex())
|
||||
time.Sleep(time.Minute * 1)
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,10 +13,3 @@ type Record struct {
|
||||
Payload []byte `bson:"payload"`
|
||||
Created time.Time `bson:"created"'`
|
||||
}
|
||||
|
||||
func (l Log) CopyRecords() Log {
|
||||
l2 := l
|
||||
l2.Records = make([]Record, len(l.Records))
|
||||
copy(l2.Records, l.Records)
|
||||
return l2
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ func New() Service {
|
||||
type Service interface {
|
||||
AddLog(ctx context.Context, clog *consensusproto.Log) (err error)
|
||||
AddRecord(ctx context.Context, logId []byte, clog *consensusproto.Record) (err error)
|
||||
WatchLog(ctx context.Context, logId []byte) (stream consensusproto.DRPCConsensus_WatchLogClient, err error)
|
||||
WatchLog(ctx context.Context) (stream Stream, err error)
|
||||
app.Component
|
||||
}
|
||||
|
||||
@ -83,15 +83,14 @@ func (s *service) AddRecord(ctx context.Context, logId []byte, clog *consensuspr
|
||||
return
|
||||
}
|
||||
|
||||
func (s *service) WatchLog(ctx context.Context, logId []byte) (stream consensusproto.DRPCConsensus_WatchLogClient, err error) {
|
||||
func (s *service) WatchLog(ctx context.Context) (st Stream, err error) {
|
||||
cl, err := s.dialClient(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if stream, err = cl.WatchLog(ctx, &consensusproto.WatchLogRequest{
|
||||
LogId: logId,
|
||||
}); err != nil {
|
||||
rpcStream, err := cl.WatchLog(ctx)
|
||||
if err != nil {
|
||||
return nil, rpcerr.Unwrap(err)
|
||||
}
|
||||
return
|
||||
return runStream(rpcStream), nil
|
||||
}
|
||||
|
||||
66
consensus/consensusclient/stream.go
Normal file
66
consensus/consensusclient/stream.go
Normal file
@ -0,0 +1,66 @@
|
||||
package consensusclient
|
||||
|
||||
import (
|
||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/consensus/consensusproto"
|
||||
"github.com/cheggaaa/mb/v2"
|
||||
)
|
||||
|
||||
type Stream interface {
|
||||
WatchIds(logIds [][]byte) (err error)
|
||||
UnwatchIds(logIds [][]byte) (err error)
|
||||
WaitLogs() []*consensusproto.Log
|
||||
Close() error
|
||||
}
|
||||
|
||||
func runStream(rpcStream consensusproto.DRPCConsensus_WatchLogClient) Stream {
|
||||
st := &stream{
|
||||
rpcStream: rpcStream,
|
||||
mb: mb.New((*consensusproto.Log)(nil), 100),
|
||||
}
|
||||
go st.readStream()
|
||||
return st
|
||||
}
|
||||
|
||||
type stream struct {
|
||||
rpcStream consensusproto.DRPCConsensus_WatchLogClient
|
||||
mb *mb.MB[*consensusproto.Log]
|
||||
}
|
||||
|
||||
func (s *stream) WatchIds(logIds [][]byte) (err error) {
|
||||
return s.rpcStream.Send(&consensusproto.WatchLogRequest{
|
||||
WatchIds: logIds,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *stream) UnwatchIds(logIds [][]byte) (err error) {
|
||||
return s.rpcStream.Send(&consensusproto.WatchLogRequest{
|
||||
UnwatchIds: logIds,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *stream) WaitLogs() []*consensusproto.Log {
|
||||
return s.mb.Wait()
|
||||
}
|
||||
|
||||
func (s *stream) readStream() {
|
||||
defer s.Close()
|
||||
for {
|
||||
event, err := s.rpcStream.Recv()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = s.mb.Add(&consensusproto.Log{
|
||||
Id: event.LogId,
|
||||
Records: event.Records,
|
||||
}); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stream) Close() error {
|
||||
if err := s.mb.Close(); err == nil {
|
||||
return s.rpcStream.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -317,7 +317,8 @@ func (m *AddRecordRequest) GetRecord() *Record {
|
||||
}
|
||||
|
||||
type WatchLogRequest struct {
|
||||
LogId []byte `protobuf:"bytes,1,opt,name=logId,proto3" json:"logId,omitempty"`
|
||||
WatchIds [][]byte `protobuf:"bytes,1,rep,name=watchIds,proto3" json:"watchIds,omitempty"`
|
||||
UnwatchIds [][]byte `protobuf:"bytes,2,rep,name=unwatchIds,proto3" json:"unwatchIds,omitempty"`
|
||||
}
|
||||
|
||||
func (m *WatchLogRequest) Reset() { *m = WatchLogRequest{} }
|
||||
@ -353,9 +354,16 @@ func (m *WatchLogRequest) XXX_DiscardUnknown() {
|
||||
|
||||
var xxx_messageInfo_WatchLogRequest proto.InternalMessageInfo
|
||||
|
||||
func (m *WatchLogRequest) GetLogId() []byte {
|
||||
func (m *WatchLogRequest) GetWatchIds() [][]byte {
|
||||
if m != nil {
|
||||
return m.LogId
|
||||
return m.WatchIds
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *WatchLogRequest) GetUnwatchIds() [][]byte {
|
||||
if m != nil {
|
||||
return m.UnwatchIds
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -428,36 +436,37 @@ func init() {
|
||||
}
|
||||
|
||||
var fileDescriptor_b8d7f1c16b400059 = []byte{
|
||||
// 458 bytes of a gzipped FileDescriptorProto
|
||||
// 480 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0x41, 0x8b, 0xd3, 0x40,
|
||||
0x14, 0xee, 0x24, 0x35, 0xbb, 0x7d, 0x69, 0xbb, 0xf1, 0xb1, 0x48, 0xd8, 0xc5, 0x50, 0xe2, 0xc1,
|
||||
0x22, 0xd2, 0x95, 0x2a, 0x78, 0xf2, 0xb0, 0x96, 0x8a, 0x0b, 0xc5, 0x42, 0xa0, 0x0a, 0x9e, 0x8c,
|
||||
0x99, 0x69, 0x0c, 0x1b, 0x32, 0x75, 0x66, 0xba, 0x74, 0xff, 0x85, 0x3f, 0xc4, 0x1f, 0xe2, 0x45,
|
||||
0xd8, 0xa3, 0x47, 0x69, 0xff, 0x88, 0x74, 0xd2, 0xac, 0xc9, 0xb6, 0x3d, 0xec, 0x25, 0xc9, 0x7c,
|
||||
0xdf, 0xfb, 0xde, 0xf7, 0xe6, 0x7b, 0x04, 0xce, 0x22, 0x9e, 0x49, 0x96, 0xc9, 0xb9, 0xfc, 0xff,
|
||||
0x35, 0x13, 0x5c, 0xf1, 0x33, 0xfd, 0x2c, 0xa1, 0x3d, 0x0d, 0x60, 0x33, 0xcc, 0xae, 0x07, 0x05,
|
||||
0xe6, 0xc7, 0x60, 0x8e, 0x78, 0x8c, 0x6d, 0x30, 0x12, 0xea, 0x92, 0x0e, 0xe9, 0x36, 0x03, 0x23,
|
||||
0xa1, 0xd8, 0x83, 0x03, 0xc1, 0x22, 0x2e, 0xa8, 0x74, 0x8d, 0x8e, 0xd9, 0xb5, 0xfb, 0xc7, 0xbd,
|
||||
0xb2, 0xac, 0x17, 0x68, 0x32, 0x28, 0x8a, 0xb0, 0x03, 0x76, 0x24, 0x58, 0xa8, 0x18, 0x9d, 0x64,
|
||||
0xc9, 0xc2, 0x35, 0x3b, 0xa4, 0x5b, 0x0f, 0xca, 0x90, 0x9f, 0x82, 0x95, 0x8b, 0xb6, 0xbc, 0x1e,
|
||||
0x81, 0x35, 0x13, 0xec, 0xea, 0x82, 0xba, 0x86, 0xc6, 0x36, 0x27, 0x74, 0xe1, 0x60, 0x16, 0x5e,
|
||||
0xa7, 0x3c, 0xa4, 0xba, 0x5f, 0x33, 0x28, 0x8e, 0x77, 0xdd, 0xea, 0xdb, 0x6e, 0x75, 0x30, 0xc6,
|
||||
0x97, 0xfe, 0x2b, 0x68, 0x9d, 0x53, 0x3a, 0xe2, 0x71, 0xc0, 0xbe, 0xcf, 0x99, 0x54, 0xf8, 0x04,
|
||||
0xcc, 0x94, 0xc7, 0xda, 0xdb, 0xee, 0x3f, 0xac, 0x5e, 0x69, 0x5d, 0xb6, 0x66, 0xfd, 0x8f, 0xe0,
|
||||
0x9c, 0x53, 0xba, 0xb9, 0xe1, 0x46, 0x78, 0x0c, 0x0f, 0x52, 0x1e, 0x5f, 0x14, 0x63, 0xe7, 0x07,
|
||||
0x7c, 0x0e, 0x56, 0x1e, 0x80, 0x9e, 0x7c, 0x5f, 0x48, 0x9b, 0x1a, 0xff, 0x29, 0x1c, 0x7d, 0x0a,
|
||||
0x55, 0xf4, 0xad, 0x34, 0xcf, 0xce, 0xb6, 0xfe, 0x04, 0x5a, 0x45, 0xe1, 0xf0, 0x8a, 0x65, 0xfb,
|
||||
0xdc, 0xef, 0xb9, 0xa3, 0x67, 0x5f, 0xe0, 0x70, 0x28, 0xc4, 0x80, 0x53, 0x26, 0xb1, 0x0d, 0x30,
|
||||
0xc9, 0xd8, 0x62, 0xc6, 0x22, 0xc5, 0xa8, 0x53, 0xc3, 0x16, 0x34, 0xd6, 0x6e, 0x8b, 0x44, 0x2a,
|
||||
0xe9, 0x10, 0x3c, 0x02, 0x7b, 0xc4, 0xe3, 0x0f, 0x5c, 0xbd, 0xe3, 0xf3, 0x8c, 0x3a, 0x06, 0x22,
|
||||
0xb4, 0xf3, 0x76, 0x03, 0x9e, 0x4d, 0xd3, 0x24, 0x52, 0x8e, 0x89, 0x0e, 0xd8, 0x43, 0x21, 0xb8,
|
||||
0x18, 0x4f, 0xa7, 0x92, 0x29, 0xe7, 0xa7, 0xd1, 0xff, 0x4d, 0xa0, 0x71, 0xeb, 0x8f, 0xaf, 0xc1,
|
||||
0xca, 0xd3, 0xc7, 0xd3, 0xea, 0x60, 0x95, 0x9d, 0x9c, 0x38, 0x55, 0x72, 0x7c, 0x89, 0x6f, 0xa0,
|
||||
0x71, 0xbb, 0x00, 0xf4, 0xb6, 0xb4, 0x95, 0xcd, 0xec, 0x90, 0xbf, 0x87, 0xc3, 0x22, 0x3e, 0x7c,
|
||||
0x5c, 0x65, 0xef, 0xe4, 0x7f, 0x72, 0xba, 0x9b, 0xd6, 0xa9, 0xbf, 0x20, 0x6f, 0xfb, 0xbf, 0x96,
|
||||
0x1e, 0xb9, 0x59, 0x7a, 0xe4, 0xef, 0xd2, 0x23, 0x3f, 0x56, 0x5e, 0xed, 0x66, 0xe5, 0xd5, 0xfe,
|
||||
0xac, 0xbc, 0xda, 0x67, 0x77, 0xdf, 0x5f, 0xf7, 0xd5, 0xd2, 0xaf, 0x97, 0xff, 0x02, 0x00, 0x00,
|
||||
0xff, 0xff, 0x2f, 0xe9, 0x67, 0xad, 0x98, 0x03, 0x00, 0x00,
|
||||
0x14, 0xee, 0x24, 0x6b, 0xb7, 0x7d, 0x69, 0xbb, 0xf1, 0xb1, 0x48, 0xe8, 0x62, 0x08, 0xf1, 0x12,
|
||||
0x44, 0xba, 0x52, 0x05, 0x4f, 0x1e, 0xd6, 0x52, 0xa1, 0x52, 0x2d, 0x04, 0xaa, 0xe0, 0xc9, 0x98,
|
||||
0x99, 0xc6, 0xb0, 0x21, 0x53, 0x67, 0xd2, 0xb5, 0xfb, 0x2f, 0xfc, 0x21, 0xfe, 0x10, 0x6f, 0xee,
|
||||
0xd1, 0xa3, 0xb4, 0x7f, 0x44, 0x3a, 0x69, 0x6a, 0xb2, 0xdd, 0x1e, 0xbc, 0x24, 0x79, 0xdf, 0x37,
|
||||
0xef, 0x7d, 0xdf, 0xbc, 0x8f, 0xc0, 0x79, 0xc8, 0x53, 0xc9, 0x52, 0xb9, 0x90, 0xff, 0xbe, 0xe6,
|
||||
0x82, 0x67, 0xfc, 0x5c, 0x3d, 0x4b, 0x68, 0x4f, 0x01, 0xd8, 0x0a, 0xd2, 0xeb, 0x41, 0x81, 0xb9,
|
||||
0x11, 0xe8, 0x63, 0x1e, 0x61, 0x07, 0xb4, 0x98, 0x5a, 0xc4, 0x21, 0x5e, 0xcb, 0xd7, 0x62, 0x8a,
|
||||
0x3d, 0x38, 0x16, 0x2c, 0xe4, 0x82, 0x4a, 0x4b, 0x73, 0x74, 0xcf, 0xe8, 0x9f, 0xf6, 0xca, 0x6d,
|
||||
0x3d, 0x5f, 0x91, 0x7e, 0x71, 0x08, 0x1d, 0x30, 0x42, 0xc1, 0x82, 0x8c, 0xd1, 0x69, 0x1a, 0x2f,
|
||||
0x2d, 0xdd, 0x21, 0xde, 0x91, 0x5f, 0x86, 0xdc, 0x04, 0xea, 0x79, 0xd3, 0x9e, 0xd6, 0x03, 0xa8,
|
||||
0xcf, 0x05, 0xbb, 0x1a, 0x51, 0x4b, 0x53, 0xd8, 0xb6, 0x42, 0x0b, 0x8e, 0xe7, 0xc1, 0x75, 0xc2,
|
||||
0x03, 0xaa, 0xe6, 0xb5, 0xfc, 0xa2, 0xbc, 0xad, 0x76, 0xb4, 0xaf, 0x76, 0x04, 0xda, 0xe4, 0xd2,
|
||||
0x7d, 0x0e, 0xed, 0x0b, 0x4a, 0xc7, 0x3c, 0xf2, 0xd9, 0xd7, 0x05, 0x93, 0x19, 0x3e, 0x02, 0x3d,
|
||||
0xe1, 0x91, 0xd2, 0x36, 0xfa, 0xf7, 0xab, 0x57, 0xda, 0x1c, 0xdb, 0xb0, 0xee, 0x7b, 0x30, 0x2f,
|
||||
0x28, 0xdd, 0xde, 0x70, 0xdb, 0x78, 0x0a, 0xf7, 0x12, 0x1e, 0x8d, 0x0a, 0xdb, 0x79, 0x81, 0x4f,
|
||||
0xa0, 0x9e, 0x2f, 0x40, 0x39, 0x3f, 0xb4, 0xa4, 0xed, 0x19, 0xf7, 0x2d, 0x9c, 0x7c, 0x08, 0xb2,
|
||||
0xf0, 0x4b, 0xc9, 0x4f, 0x17, 0x1a, 0xdf, 0x36, 0xd0, 0x88, 0x4a, 0x8b, 0x38, 0xba, 0xd7, 0xf2,
|
||||
0x77, 0x35, 0xda, 0x00, 0x8b, 0x74, 0xc7, 0x6a, 0x8a, 0x2d, 0x21, 0xee, 0x14, 0xda, 0xc5, 0xb8,
|
||||
0xe1, 0x15, 0x4b, 0x0f, 0x79, 0xfc, 0xcf, 0x24, 0x1f, 0x7f, 0x82, 0xc6, 0x50, 0x88, 0x01, 0xa7,
|
||||
0x4c, 0x62, 0x07, 0x60, 0x9a, 0xb2, 0xe5, 0x9c, 0x85, 0x19, 0xa3, 0x66, 0x0d, 0xdb, 0xd0, 0xdc,
|
||||
0xa8, 0x2d, 0x63, 0x99, 0x49, 0x93, 0xe0, 0x09, 0x18, 0x63, 0x1e, 0xbd, 0xe3, 0xd9, 0x6b, 0xbe,
|
||||
0x48, 0xa9, 0xa9, 0x21, 0x42, 0x27, 0x1f, 0x37, 0xe0, 0xe9, 0x2c, 0x89, 0xc3, 0xcc, 0xd4, 0xd1,
|
||||
0x04, 0x63, 0x28, 0x04, 0x17, 0x93, 0xd9, 0x4c, 0xb2, 0xcc, 0xfc, 0xa1, 0xf5, 0x7f, 0x11, 0x68,
|
||||
0xee, 0xf4, 0xf1, 0x05, 0xd4, 0xf3, 0x8c, 0xf0, 0xac, 0x6a, 0xac, 0x92, 0x5c, 0xd7, 0xac, 0x92,
|
||||
0x93, 0x4b, 0x7c, 0x09, 0xcd, 0x5d, 0x4c, 0x68, 0xef, 0xf5, 0x56, 0xf2, 0xbb, 0xa3, 0xfd, 0x0d,
|
||||
0x34, 0x8a, 0xf5, 0xe1, 0xc3, 0x2a, 0x7b, 0x2b, 0xa5, 0xee, 0xd9, 0xdd, 0xb4, 0xda, 0xba, 0x47,
|
||||
0x9e, 0x92, 0x57, 0xfd, 0x9f, 0x2b, 0x9b, 0xdc, 0xac, 0x6c, 0xf2, 0x67, 0x65, 0x93, 0xef, 0x6b,
|
||||
0xbb, 0x76, 0xb3, 0xb6, 0x6b, 0xbf, 0xd7, 0x76, 0xed, 0xa3, 0x75, 0xe8, 0xef, 0xfc, 0x5c, 0x57,
|
||||
0xaf, 0x67, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xf0, 0xc9, 0xa1, 0x1f, 0xc0, 0x03, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *Log) Marshal() (dAtA []byte, err error) {
|
||||
@ -678,13 +687,24 @@ func (m *WatchLogRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.LogId) > 0 {
|
||||
i -= len(m.LogId)
|
||||
copy(dAtA[i:], m.LogId)
|
||||
i = encodeVarintConsensus(dAtA, i, uint64(len(m.LogId)))
|
||||
if len(m.UnwatchIds) > 0 {
|
||||
for iNdEx := len(m.UnwatchIds) - 1; iNdEx >= 0; iNdEx-- {
|
||||
i -= len(m.UnwatchIds[iNdEx])
|
||||
copy(dAtA[i:], m.UnwatchIds[iNdEx])
|
||||
i = encodeVarintConsensus(dAtA, i, uint64(len(m.UnwatchIds[iNdEx])))
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
}
|
||||
}
|
||||
if len(m.WatchIds) > 0 {
|
||||
for iNdEx := len(m.WatchIds) - 1; iNdEx >= 0; iNdEx-- {
|
||||
i -= len(m.WatchIds[iNdEx])
|
||||
copy(dAtA[i:], m.WatchIds[iNdEx])
|
||||
i = encodeVarintConsensus(dAtA, i, uint64(len(m.WatchIds[iNdEx])))
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
}
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
@ -834,10 +854,18 @@ func (m *WatchLogRequest) Size() (n int) {
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
l = len(m.LogId)
|
||||
if l > 0 {
|
||||
if len(m.WatchIds) > 0 {
|
||||
for _, b := range m.WatchIds {
|
||||
l = len(b)
|
||||
n += 1 + l + sovConsensus(uint64(l))
|
||||
}
|
||||
}
|
||||
if len(m.UnwatchIds) > 0 {
|
||||
for _, b := range m.UnwatchIds {
|
||||
l = len(b)
|
||||
n += 1 + l + sovConsensus(uint64(l))
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@ -1461,7 +1489,7 @@ func (m *WatchLogRequest) Unmarshal(dAtA []byte) error {
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field LogId", wireType)
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field WatchIds", wireType)
|
||||
}
|
||||
var byteLen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
@ -1488,10 +1516,40 @@ func (m *WatchLogRequest) Unmarshal(dAtA []byte) error {
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.LogId = append(m.LogId[:0], dAtA[iNdEx:postIndex]...)
|
||||
if m.LogId == nil {
|
||||
m.LogId = []byte{}
|
||||
m.WatchIds = append(m.WatchIds, make([]byte, postIndex-iNdEx))
|
||||
copy(m.WatchIds[len(m.WatchIds)-1], dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field UnwatchIds", wireType)
|
||||
}
|
||||
var byteLen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowConsensus
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
byteLen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if byteLen < 0 {
|
||||
return ErrInvalidLengthConsensus
|
||||
}
|
||||
postIndex := iNdEx + byteLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthConsensus
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.UnwatchIds = append(m.UnwatchIds, make([]byte, postIndex-iNdEx))
|
||||
copy(m.UnwatchIds[len(m.UnwatchIds)-1], dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
|
||||
@ -42,7 +42,7 @@ type DRPCConsensusClient interface {
|
||||
|
||||
AddLog(ctx context.Context, in *AddLogRequest) (*Ok, error)
|
||||
AddRecord(ctx context.Context, in *AddRecordRequest) (*Ok, error)
|
||||
WatchLog(ctx context.Context, in *WatchLogRequest) (DRPCConsensus_WatchLogClient, error)
|
||||
WatchLog(ctx context.Context) (DRPCConsensus_WatchLogClient, error)
|
||||
}
|
||||
|
||||
type drpcConsensusClient struct {
|
||||
@ -73,23 +73,18 @@ func (c *drpcConsensusClient) AddRecord(ctx context.Context, in *AddRecordReques
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *drpcConsensusClient) WatchLog(ctx context.Context, in *WatchLogRequest) (DRPCConsensus_WatchLogClient, error) {
|
||||
func (c *drpcConsensusClient) WatchLog(ctx context.Context) (DRPCConsensus_WatchLogClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, "/anyConsensus.Consensus/WatchLog", drpcEncoding_File_consensus_consensusproto_protos_consensus_proto{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &drpcConsensus_WatchLogClient{stream}
|
||||
if err := x.MsgSend(in, drpcEncoding_File_consensus_consensusproto_protos_consensus_proto{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type DRPCConsensus_WatchLogClient interface {
|
||||
drpc.Stream
|
||||
Send(*WatchLogRequest) error
|
||||
Recv() (*WatchLogEvent, error)
|
||||
}
|
||||
|
||||
@ -97,6 +92,10 @@ type drpcConsensus_WatchLogClient struct {
|
||||
drpc.Stream
|
||||
}
|
||||
|
||||
func (x *drpcConsensus_WatchLogClient) Send(m *WatchLogRequest) error {
|
||||
return x.MsgSend(m, drpcEncoding_File_consensus_consensusproto_protos_consensus_proto{})
|
||||
}
|
||||
|
||||
func (x *drpcConsensus_WatchLogClient) Recv() (*WatchLogEvent, error) {
|
||||
m := new(WatchLogEvent)
|
||||
if err := x.MsgRecv(m, drpcEncoding_File_consensus_consensusproto_protos_consensus_proto{}); err != nil {
|
||||
@ -112,7 +111,7 @@ func (x *drpcConsensus_WatchLogClient) RecvMsg(m *WatchLogEvent) error {
|
||||
type DRPCConsensusServer interface {
|
||||
AddLog(context.Context, *AddLogRequest) (*Ok, error)
|
||||
AddRecord(context.Context, *AddRecordRequest) (*Ok, error)
|
||||
WatchLog(*WatchLogRequest, DRPCConsensus_WatchLogStream) error
|
||||
WatchLog(DRPCConsensus_WatchLogStream) error
|
||||
}
|
||||
|
||||
type DRPCConsensusUnimplementedServer struct{}
|
||||
@ -125,7 +124,7 @@ func (s *DRPCConsensusUnimplementedServer) AddRecord(context.Context, *AddRecord
|
||||
return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
|
||||
}
|
||||
|
||||
func (s *DRPCConsensusUnimplementedServer) WatchLog(*WatchLogRequest, DRPCConsensus_WatchLogStream) error {
|
||||
func (s *DRPCConsensusUnimplementedServer) WatchLog(DRPCConsensus_WatchLogStream) error {
|
||||
return drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
|
||||
}
|
||||
|
||||
@ -158,8 +157,7 @@ func (DRPCConsensusDescription) Method(n int) (string, drpc.Encoding, drpc.Recei
|
||||
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
||||
return nil, srv.(DRPCConsensusServer).
|
||||
WatchLog(
|
||||
in1.(*WatchLogRequest),
|
||||
&drpcConsensus_WatchLogStream{in2.(drpc.Stream)},
|
||||
&drpcConsensus_WatchLogStream{in1.(drpc.Stream)},
|
||||
)
|
||||
}, DRPCConsensusServer.WatchLog, true
|
||||
default:
|
||||
@ -206,6 +204,7 @@ func (x *drpcConsensus_AddRecordStream) SendAndClose(m *Ok) error {
|
||||
type DRPCConsensus_WatchLogStream interface {
|
||||
drpc.Stream
|
||||
Send(*WatchLogEvent) error
|
||||
Recv() (*WatchLogRequest, error)
|
||||
}
|
||||
|
||||
type drpcConsensus_WatchLogStream struct {
|
||||
@ -215,3 +214,15 @@ type drpcConsensus_WatchLogStream struct {
|
||||
func (x *drpcConsensus_WatchLogStream) Send(m *WatchLogEvent) error {
|
||||
return x.MsgSend(m, drpcEncoding_File_consensus_consensusproto_protos_consensus_proto{})
|
||||
}
|
||||
|
||||
func (x *drpcConsensus_WatchLogStream) Recv() (*WatchLogRequest, error) {
|
||||
m := new(WatchLogRequest)
|
||||
if err := x.MsgRecv(m, drpcEncoding_File_consensus_consensusproto_protos_consensus_proto{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (x *drpcConsensus_WatchLogStream) RecvMsg(m *WatchLogRequest) error {
|
||||
return x.MsgRecv(m, drpcEncoding_File_consensus_consensusproto_protos_consensus_proto{})
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ service Consensus {
|
||||
// AddRecord adds new record to log
|
||||
rpc AddRecord(AddRecordRequest) returns (Ok);
|
||||
// WatchLog fetches log and subscribes for a changes
|
||||
rpc WatchLog(WatchLogRequest) returns (stream WatchLogEvent);
|
||||
rpc WatchLog(stream WatchLogRequest) returns (stream WatchLogEvent);
|
||||
}
|
||||
|
||||
message Ok {}
|
||||
@ -46,7 +46,8 @@ message AddRecordRequest {
|
||||
}
|
||||
|
||||
message WatchLogRequest {
|
||||
bytes logId = 1;
|
||||
repeated bytes watchIds = 1;
|
||||
repeated bytes unwatchIds = 2;
|
||||
}
|
||||
|
||||
message WatchLogEvent {
|
||||
|
||||
@ -46,30 +46,19 @@ func (c *consensusRpc) AddRecord(ctx context.Context, req *consensusproto.AddRec
|
||||
return &consensusproto.Ok{}, nil
|
||||
}
|
||||
|
||||
func (c *consensusRpc) WatchLog(req *consensusproto.WatchLogRequest, rpcStream consensusproto.DRPCConsensus_WatchLogStream) error {
|
||||
stream, err := c.stream.Subscribe(rpcStream.Context(), req.LogId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func (c *consensusRpc) WatchLog(rpcStream consensusproto.DRPCConsensus_WatchLogStream) error {
|
||||
stream := c.stream.NewStream()
|
||||
defer stream.Close()
|
||||
var logSent bool
|
||||
go c.readStream(stream, rpcStream)
|
||||
for {
|
||||
if !logSent {
|
||||
if err = rpcStream.Send(&consensusproto.WatchLogEvent{
|
||||
LogId: req.LogId,
|
||||
Records: recordsToProto(stream.Records()),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
logSent = true
|
||||
} else {
|
||||
recs := stream.WaitRecords()
|
||||
recs := stream.WaitLogs()
|
||||
if len(recs) == 0 {
|
||||
return rpcStream.Close()
|
||||
}
|
||||
if err = rpcStream.Send(&consensusproto.WatchLogEvent{
|
||||
LogId: req.LogId,
|
||||
Records: recordsToProto(recs),
|
||||
for _, rec := range recs {
|
||||
if err := rpcStream.Send(&consensusproto.WatchLogEvent{
|
||||
LogId: rec.Id,
|
||||
Records: recordsToProto(rec.Records),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -77,6 +66,18 @@ func (c *consensusRpc) WatchLog(req *consensusproto.WatchLogRequest, rpcStream c
|
||||
}
|
||||
}
|
||||
|
||||
func (c *consensusRpc) readStream(st *stream.Stream, rpcStream consensusproto.DRPCConsensus_WatchLogStream) {
|
||||
defer st.Close()
|
||||
for {
|
||||
req, err := rpcStream.Recv()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
st.UnwatchIds(rpcStream.Context(), req.UnwatchIds)
|
||||
st.WatchIds(rpcStream.Context(), req.WatchIds)
|
||||
}
|
||||
}
|
||||
|
||||
func logFromProto(log *consensusproto.Log) consensus.Log {
|
||||
return consensus.Log{
|
||||
Id: log.Id,
|
||||
|
||||
@ -2,7 +2,6 @@ package stream
|
||||
|
||||
import (
|
||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/consensus"
|
||||
"github.com/cheggaaa/mb/v2"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@ -10,9 +9,7 @@ type object struct {
|
||||
logId []byte
|
||||
records []consensus.Record
|
||||
|
||||
streams map[uint32]*stream
|
||||
|
||||
lastStreamId uint32
|
||||
streams map[uint64]*Stream
|
||||
|
||||
mu sync.Mutex
|
||||
}
|
||||
@ -27,7 +24,7 @@ func (o *object) AddRecords(recs []consensus.Record) {
|
||||
diff := recs[0 : len(recs)-len(o.records)]
|
||||
o.records = recs
|
||||
for _, st := range o.streams {
|
||||
st.AddRecords(diff)
|
||||
_ = st.AddRecords(o.logId, diff)
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,18 +34,12 @@ func (o *object) Records() []consensus.Record {
|
||||
return o.records
|
||||
}
|
||||
|
||||
func (o *object) NewStream() Stream {
|
||||
func (o *object) AddStream(s *Stream) {
|
||||
o.mu.Lock()
|
||||
defer o.mu.Unlock()
|
||||
o.lastStreamId++
|
||||
st := &stream{
|
||||
id: o.lastStreamId,
|
||||
obj: o,
|
||||
records: o.records,
|
||||
mb: mb.New(consensus.Record{}, 100),
|
||||
}
|
||||
o.streams[st.id] = st
|
||||
return st
|
||||
o.streams[s.id] = s
|
||||
_ = s.AddRecords(o.logId, o.records)
|
||||
return
|
||||
}
|
||||
|
||||
func (o *object) Locked() bool {
|
||||
@ -57,7 +48,7 @@ func (o *object) Locked() bool {
|
||||
return len(o.streams) > 0
|
||||
}
|
||||
|
||||
func (o *object) removeStream(id uint32) {
|
||||
func (o *object) RemoveStream(id uint64) {
|
||||
o.mu.Lock()
|
||||
defer o.mu.Unlock()
|
||||
delete(o.streams, id)
|
||||
|
||||
@ -7,8 +7,10 @@ import (
|
||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/consensus"
|
||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/consensus/db"
|
||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/ocache"
|
||||
"github.com/cheggaaa/mb/v2"
|
||||
"github.com/mr-tron/base58"
|
||||
"go.uber.org/zap"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -30,21 +32,15 @@ func New() Service {
|
||||
return &service{}
|
||||
}
|
||||
|
||||
type Stream interface {
|
||||
LogId() []byte
|
||||
Records() []consensus.Record
|
||||
WaitRecords() []consensus.Record
|
||||
Close()
|
||||
}
|
||||
|
||||
type Service interface {
|
||||
Subscribe(ctx context.Context, logId []byte) (stream Stream, err error)
|
||||
NewStream() *Stream
|
||||
app.ComponentRunnable
|
||||
}
|
||||
|
||||
type service struct {
|
||||
db db.Service
|
||||
cache ocache.OCache
|
||||
lastStreamId uint64
|
||||
}
|
||||
|
||||
func (s *service) Init(a *app.App) (err error) {
|
||||
@ -58,14 +54,6 @@ func (s *service) Init(a *app.App) (err error) {
|
||||
return s.db.SetChangeReceiver(s.receiveChange)
|
||||
}
|
||||
|
||||
func (s *service) Subscribe(ctx context.Context, logId []byte) (Stream, error) {
|
||||
obj, err := s.getObject(ctx, logId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.NewStream(), nil
|
||||
}
|
||||
|
||||
func (s *service) Name() (name string) {
|
||||
return CName
|
||||
}
|
||||
@ -74,12 +62,39 @@ func (s *service) Run(ctx context.Context) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *service) NewStream() *Stream {
|
||||
return &Stream{
|
||||
id: atomic.AddUint64(&s.lastStreamId, 1),
|
||||
logIds: make(map[string]struct{}),
|
||||
mb: mb.New(consensus.Log{}, 100),
|
||||
s: s,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *service) AddStream(ctx context.Context, logId []byte, stream *Stream) (err error) {
|
||||
obj, err := s.getObject(ctx, logId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
obj.AddStream(stream)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *service) RemoveStream(ctx context.Context, logId []byte, streamId uint64) (err error) {
|
||||
obj, err := s.getObject(ctx, logId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
obj.RemoveStream(streamId)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *service) loadLog(ctx context.Context, id string) (value ocache.Object, err error) {
|
||||
if ctxLog := ctx.Value(ctxLogKey); ctxLog != nil {
|
||||
return &object{
|
||||
logId: ctxLog.(consensus.Log).Id,
|
||||
records: ctxLog.(consensus.Log).Records,
|
||||
streams: make(map[uint32]*stream),
|
||||
streams: make(map[uint64]*Stream),
|
||||
}, nil
|
||||
}
|
||||
logId := logIdFromString(id)
|
||||
@ -90,7 +105,7 @@ func (s *service) loadLog(ctx context.Context, id string) (value ocache.Object,
|
||||
return &object{
|
||||
logId: dbLog.Id,
|
||||
records: dbLog.Records,
|
||||
streams: make(map[uint32]*stream),
|
||||
streams: make(map[uint64]*Stream),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@ -13,11 +13,12 @@ import (
|
||||
|
||||
var ctx = context.Background()
|
||||
|
||||
func TestService_Subscribe(t *testing.T) {
|
||||
func TestService_NewStream(t *testing.T) {
|
||||
fx := newFixture(t)
|
||||
defer fx.Finish(t)
|
||||
|
||||
var expLogId = []byte("logId")
|
||||
var preloadLogId = []byte("preloadId")
|
||||
|
||||
fx.mockDB.fetchLog = func(ctx context.Context, logId []byte) (log consensus.Log, err error) {
|
||||
require.Equal(t, expLogId, logId)
|
||||
@ -31,24 +32,7 @@ func TestService_Subscribe(t *testing.T) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
st1, err := fx.Subscribe(ctx, expLogId)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expLogId, st1.LogId())
|
||||
sr1 := readStream(st1)
|
||||
assert.Equal(t, uint32(1), sr1.id)
|
||||
|
||||
st2, err := fx.Subscribe(ctx, expLogId)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expLogId, st2.LogId())
|
||||
sr2 := readStream(st2)
|
||||
assert.Equal(t, uint32(2), sr2.id)
|
||||
|
||||
fx.mockDB.receiver(expLogId, []consensus.Record{
|
||||
{
|
||||
Id: []byte{'1'},
|
||||
},
|
||||
})
|
||||
fx.mockDB.receiver([]byte("other id"), []consensus.Record{
|
||||
fx.mockDB.receiver(preloadLogId, []consensus.Record{
|
||||
{
|
||||
Id: []byte{'2'},
|
||||
PrevId: []byte{'1'},
|
||||
@ -57,7 +41,38 @@ func TestService_Subscribe(t *testing.T) {
|
||||
Id: []byte{'1'},
|
||||
},
|
||||
})
|
||||
|
||||
st1 := fx.NewStream()
|
||||
sr1 := readStream(st1)
|
||||
assert.Equal(t, uint64(1), sr1.id)
|
||||
st1.WatchIds(ctx, [][]byte{expLogId, preloadLogId})
|
||||
st1.UnwatchIds(ctx, [][]byte{preloadLogId})
|
||||
assert.Equal(t, [][]byte{expLogId}, st1.LogIds())
|
||||
|
||||
st2 := fx.NewStream()
|
||||
sr2 := readStream(st2)
|
||||
assert.Equal(t, uint64(2), sr2.id)
|
||||
st2.WatchIds(ctx, [][]byte{expLogId, preloadLogId})
|
||||
|
||||
fx.mockDB.receiver(expLogId, []consensus.Record{
|
||||
{
|
||||
Id: []byte{'1'},
|
||||
},
|
||||
})
|
||||
fx.mockDB.receiver(expLogId, []consensus.Record{
|
||||
{
|
||||
Id: []byte{'2'},
|
||||
PrevId: []byte{'1'},
|
||||
},
|
||||
{
|
||||
Id: []byte{'1'},
|
||||
},
|
||||
})
|
||||
fx.mockDB.receiver(preloadLogId, []consensus.Record{
|
||||
{
|
||||
Id: []byte{'3'},
|
||||
PrevId: []byte{'4'},
|
||||
},
|
||||
{
|
||||
Id: []byte{'2'},
|
||||
PrevId: []byte{'1'},
|
||||
@ -77,9 +92,15 @@ func TestService_Subscribe(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
require.Equal(t, sr1.records, sr2.records)
|
||||
require.Len(t, sr1.records, 1)
|
||||
assert.Equal(t, []byte{'2'}, sr1.records[0].Id)
|
||||
require.Len(t, sr1.logs, 2)
|
||||
assert.Len(t, sr1.logs[string(expLogId)].Records, 2)
|
||||
assert.Equal(t, []byte{'2'}, sr1.logs[string(expLogId)].Records[0].Id)
|
||||
assert.Equal(t, []byte{'2'}, sr1.logs[string(preloadLogId)].Records[0].Id)
|
||||
|
||||
require.Len(t, sr2.logs, 2)
|
||||
assert.Len(t, sr2.logs[string(expLogId)].Records, 2)
|
||||
assert.Equal(t, []byte{'2'}, sr2.logs[string(expLogId)].Records[0].Id)
|
||||
assert.Equal(t, []byte{'3'}, sr2.logs[string(preloadLogId)].Records[0].Id)
|
||||
}
|
||||
|
||||
func newFixture(t *testing.T) *fixture {
|
||||
@ -104,10 +125,11 @@ func (fx *fixture) Finish(t *testing.T) {
|
||||
require.NoError(t, fx.a.Close(ctx))
|
||||
}
|
||||
|
||||
func readStream(st Stream) *streamReader {
|
||||
func readStream(st *Stream) *streamReader {
|
||||
sr := &streamReader{
|
||||
id: st.(*stream).id,
|
||||
id: st.id,
|
||||
stream: st,
|
||||
logs: map[string]consensus.Log{},
|
||||
finished: make(chan struct{}),
|
||||
}
|
||||
go sr.read()
|
||||
@ -115,21 +137,28 @@ func readStream(st Stream) *streamReader {
|
||||
}
|
||||
|
||||
type streamReader struct {
|
||||
id uint32
|
||||
stream Stream
|
||||
id uint64
|
||||
stream *Stream
|
||||
|
||||
records []consensus.Record
|
||||
logs map[string]consensus.Log
|
||||
finished chan struct{}
|
||||
}
|
||||
|
||||
func (sr *streamReader) read() {
|
||||
defer close(sr.finished)
|
||||
for {
|
||||
records := sr.stream.WaitRecords()
|
||||
if len(records) == 0 {
|
||||
logs := sr.stream.WaitLogs()
|
||||
if len(logs) == 0 {
|
||||
return
|
||||
}
|
||||
sr.records = append(sr.records, records...)
|
||||
for _, l := range logs {
|
||||
if el, ok := sr.logs[string(l.Id)]; !ok {
|
||||
sr.logs[string(l.Id)] = l
|
||||
} else {
|
||||
el.Records = append(l.Records, el.Records...)
|
||||
sr.logs[string(l.Id)] = el
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,34 +1,74 @@
|
||||
package stream
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/consensus"
|
||||
"github.com/cheggaaa/mb/v2"
|
||||
"go.uber.org/zap"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type stream struct {
|
||||
id uint32
|
||||
obj *object
|
||||
records []consensus.Record
|
||||
mb *mb.MB[consensus.Record]
|
||||
type Stream struct {
|
||||
id uint64
|
||||
logIds map[string]struct{}
|
||||
mu sync.Mutex
|
||||
mb *mb.MB[consensus.Log]
|
||||
s *service
|
||||
}
|
||||
|
||||
func (s *stream) LogId() []byte {
|
||||
return s.obj.logId
|
||||
func (s *Stream) LogIds() [][]byte {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
logIds := make([][]byte, 0, len(s.logIds))
|
||||
for logId := range s.logIds {
|
||||
logIds = append(logIds, []byte(logId))
|
||||
}
|
||||
return logIds
|
||||
}
|
||||
|
||||
func (s *stream) AddRecords(records []consensus.Record) {
|
||||
_ = s.mb.Add(records...)
|
||||
func (s *Stream) AddRecords(logId []byte, records []consensus.Record) (err error) {
|
||||
return s.mb.Add(consensus.Log{Id: logId, Records: records})
|
||||
}
|
||||
|
||||
func (s *stream) Records() []consensus.Record {
|
||||
return s.records
|
||||
}
|
||||
|
||||
func (s *stream) WaitRecords() []consensus.Record {
|
||||
func (s *Stream) WaitLogs() []consensus.Log {
|
||||
return s.mb.Wait()
|
||||
}
|
||||
|
||||
func (s *stream) Close() {
|
||||
_ = s.mb.Close()
|
||||
s.obj.removeStream(s.id)
|
||||
func (s *Stream) WatchIds(ctx context.Context, logIds [][]byte) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
for _, logId := range logIds {
|
||||
logIdKey := string(logId)
|
||||
if _, ok := s.logIds[logIdKey]; !ok {
|
||||
s.logIds[logIdKey] = struct{}{}
|
||||
if addErr := s.s.AddStream(ctx, logId, s); addErr != nil {
|
||||
log.Warn("can't add stream for log", zap.Binary("logId", logId), zap.Error(addErr))
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Stream) UnwatchIds(ctx context.Context, logIds [][]byte) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
for _, logId := range logIds {
|
||||
logIdKey := string(logId)
|
||||
if _, ok := s.logIds[logIdKey]; ok {
|
||||
delete(s.logIds, logIdKey)
|
||||
if remErr := s.s.RemoveStream(ctx, logId, s.id); remErr != nil {
|
||||
log.Warn("can't remove stream for log", zap.Binary("logId", logId), zap.Error(remErr))
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Stream) Close() {
|
||||
_ = s.mb.Close()
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
for logId := range s.logIds {
|
||||
_ = s.s.RemoveStream(context.TODO(), []byte(logId), s.id)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user