2021-03-18 16:40:47 +00:00
package store
import (
"context"
"encoding/hex"
"errors"
2021-06-10 13:00:06 +00:00
"math"
2021-11-23 15:03:12 +00:00
"sync"
2021-11-05 14:27:30 +00:00
"time"
2021-03-18 16:40:47 +00:00
"github.com/libp2p/go-libp2p-core/host"
"github.com/libp2p/go-libp2p-core/network"
"github.com/libp2p/go-libp2p-core/peer"
libp2pProtocol "github.com/libp2p/go-libp2p-core/protocol"
2021-04-07 21:16:29 +00:00
"github.com/libp2p/go-msgio/protoio"
2022-01-18 18:17:06 +00:00
"go.uber.org/zap"
2021-03-22 16:45:13 +00:00
2022-05-30 15:55:30 +00:00
"github.com/status-im/go-waku/logging"
2021-10-25 19:41:08 +00:00
"github.com/status-im/go-waku/waku/persistence"
2021-06-28 13:20:23 +00:00
"github.com/status-im/go-waku/waku/v2/metrics"
2021-03-18 16:40:47 +00:00
"github.com/status-im/go-waku/waku/v2/protocol"
2021-04-22 00:09:37 +00:00
"github.com/status-im/go-waku/waku/v2/protocol/pb"
2021-12-06 10:49:13 +00:00
"github.com/status-im/go-waku/waku/v2/protocol/swap"
2021-06-16 10:14:22 +00:00
"github.com/status-im/go-waku/waku/v2/utils"
2021-03-18 16:40:47 +00:00
)
2022-02-23 15:06:47 +00:00
// StoreID_v20beta4 is the current Waku Store protocol identifier
const StoreID_v20beta4 = libp2pProtocol . ID ( "/vac/waku/store/2.0.0-beta4" )
2021-11-05 20:09:48 +00:00
// MaxPageSize is the maximum number of waku messages to return per page
const MaxPageSize = 100
2021-03-18 16:40:47 +00:00
2022-06-01 19:51:28 +00:00
// MaxTimeVariance is the maximum duration in the future allowed for a message timestamp
const MaxTimeVariance = time . Duration ( 20 ) * time . Second
2021-04-15 02:17:53 +00:00
var (
2022-04-25 19:31:26 +00:00
// ErrNoPeersAvailable is returned when there are no store peers in the peer store
// that could be used to retrieve message history
ErrNoPeersAvailable = errors . New ( "no suitable remote peers" )
// ErrInvalidId is returned when no RequestID is given
ErrInvalidId = errors . New ( "invalid request id" )
// ErrFailedToResumeHistory is returned when the node attempted to retrieve historic
// messages to fill its own message history but for some reason it failed
2021-06-10 13:00:06 +00:00
ErrFailedToResumeHistory = errors . New ( "failed to resume the history" )
2022-04-25 19:31:26 +00:00
// ErrFailedQuery is emitted when the query fails to return results
ErrFailedQuery = errors . New ( "failed to resolve the query" )
2022-06-01 19:51:28 +00:00
ErrFutureMessage = errors . New ( "message timestamp in the future" )
2021-04-15 02:17:53 +00:00
)
2022-05-30 18:48:22 +00:00
func findMessages ( query * pb . HistoryQuery , msgProvider MessageProvider ) ( [ ] * pb . WakuMessage , * pb . PagingInfo , error ) {
if query . PagingInfo == nil {
query . PagingInfo = & pb . PagingInfo {
Direction : pb . PagingInfo_FORWARD ,
2021-03-18 16:40:47 +00:00
}
}
2022-05-30 18:48:22 +00:00
if query . PagingInfo . PageSize == 0 || query . PagingInfo . PageSize > uint64 ( MaxPageSize ) {
query . PagingInfo . PageSize = MaxPageSize
2021-03-18 16:40:47 +00:00
}
2022-05-30 18:48:22 +00:00
queryResult , err := msgProvider . Query ( query )
if err != nil {
return nil , nil , err
2022-01-18 18:17:06 +00:00
}
2022-05-30 18:48:22 +00:00
if len ( queryResult ) == 0 { // no pagination is needed for an empty list
newPagingInfo := & pb . PagingInfo { PageSize : 0 , Cursor : query . PagingInfo . Cursor , Direction : query . PagingInfo . Direction }
return nil , newPagingInfo , nil
2021-03-18 16:40:47 +00:00
}
2022-05-30 18:48:22 +00:00
lastMsgIdx := len ( queryResult ) - 1
newCursor := protocol . NewEnvelope ( queryResult [ lastMsgIdx ] . Message , queryResult [ lastMsgIdx ] . ReceiverTime , queryResult [ lastMsgIdx ] . PubsubTopic ) . Index ( )
2021-03-18 16:40:47 +00:00
2022-05-30 18:48:22 +00:00
newPagingInfo := & pb . PagingInfo { PageSize : query . PagingInfo . PageSize , Cursor : newCursor , Direction : query . PagingInfo . Direction }
if newPagingInfo . PageSize > uint64 ( len ( queryResult ) ) {
newPagingInfo . PageSize = uint64 ( len ( queryResult ) )
2021-03-18 16:40:47 +00:00
}
2022-05-30 18:48:22 +00:00
resultMessages := make ( [ ] * pb . WakuMessage , len ( queryResult ) )
for i := range queryResult {
resultMessages [ i ] = queryResult [ i ] . Message
2021-03-18 16:40:47 +00:00
}
2022-05-30 18:48:22 +00:00
return resultMessages , newPagingInfo , nil
2021-03-18 16:40:47 +00:00
}
2021-11-05 20:09:48 +00:00
func ( store * WakuStore ) FindMessages ( query * pb . HistoryQuery ) * pb . HistoryResponse {
2021-04-22 00:09:37 +00:00
result := new ( pb . HistoryResponse )
2021-04-15 02:17:53 +00:00
2022-05-30 18:48:22 +00:00
messages , newPagingInfo , err := findMessages ( query , store . msgProvider )
if err != nil {
if err == persistence . ErrInvalidCursor {
result . Error = pb . HistoryResponse_INVALID_CURSOR
} else {
// TODO: return error in pb.HistoryResponse
store . log . Error ( "obtaining messages from db" , zap . Error ( err ) )
2021-03-18 16:40:47 +00:00
}
}
2022-05-30 18:48:22 +00:00
result . Messages = messages
result . PagingInfo = newPagingInfo
2021-03-18 16:40:47 +00:00
return result
}
type MessageProvider interface {
2021-10-25 19:41:08 +00:00
GetAll ( ) ( [ ] persistence . StoredMessage , error )
2022-05-30 18:48:22 +00:00
Query ( query * pb . HistoryQuery ) ( [ ] persistence . StoredMessage , error )
Put ( env * protocol . Envelope ) error
MostRecentTimestamp ( ) ( int64 , error )
2021-04-12 17:59:09 +00:00
Stop ( )
2021-03-18 16:40:47 +00:00
}
2021-10-31 19:00:38 +00:00
type Query struct {
Topic string
ContentTopics [ ] string
2022-02-23 15:01:53 +00:00
StartTime int64
EndTime int64
2021-10-31 19:00:38 +00:00
}
2021-11-05 20:09:48 +00:00
// Result represents a valid response from a store node
2021-10-31 19:00:38 +00:00
type Result struct {
Messages [ ] * pb . WakuMessage
query * pb . HistoryQuery
cursor * pb . Index
peerId peer . ID
}
func ( r * Result ) Cursor ( ) * pb . Index {
return r . cursor
}
func ( r * Result ) PeerID ( ) peer . ID {
return r . peerId
}
func ( r * Result ) Query ( ) * pb . HistoryQuery {
return r . query
}
2021-03-18 16:40:47 +00:00
type WakuStore struct {
2021-11-05 14:27:30 +00:00
ctx context . Context
MsgC chan * protocol . Envelope
2021-11-23 15:03:12 +00:00
wg * sync . WaitGroup
2021-07-11 18:11:38 +00:00
2022-05-30 15:55:30 +00:00
log * zap . Logger
2022-01-18 18:17:06 +00:00
2021-11-01 12:38:03 +00:00
started bool
2022-05-30 18:48:22 +00:00
msgProvider MessageProvider
h host . Host
swap * swap . WakuSwap
2021-03-18 16:40:47 +00:00
}
2022-03-18 19:56:34 +00:00
type Store interface {
Start ( ctx context . Context )
Query ( ctx context . Context , query Query , opts ... HistoryRequestOption ) ( * Result , error )
Next ( ctx context . Context , r * Result ) ( * Result , error )
Resume ( ctx context . Context , pubsubTopic string , peerList [ ] peer . ID ) ( int , error )
MessageChannel ( ) chan * protocol . Envelope
Stop ( )
}
2021-11-05 20:09:48 +00:00
// NewWakuStore creates a WakuStore using an specific MessageProvider for storing the messages
2022-05-30 15:55:30 +00:00
func NewWakuStore ( host host . Host , swap * swap . WakuSwap , p MessageProvider , maxNumberOfMessages int , maxRetentionDuration time . Duration , log * zap . Logger ) * WakuStore {
2021-03-18 16:40:47 +00:00
wakuStore := new ( WakuStore )
wakuStore . msgProvider = p
2021-11-09 16:18:57 +00:00
wakuStore . h = host
2021-12-06 10:49:13 +00:00
wakuStore . swap = swap
2021-11-23 15:03:12 +00:00
wakuStore . wg = & sync . WaitGroup { }
2022-01-18 18:17:06 +00:00
wakuStore . log = log . Named ( "store" )
2021-03-18 23:21:45 +00:00
return wakuStore
}
2021-03-18 16:40:47 +00:00
2021-11-05 20:09:48 +00:00
// SetMessageProvider allows switching the message provider used with a WakuStore
func ( store * WakuStore ) SetMessageProvider ( p MessageProvider ) {
2021-04-18 23:41:42 +00:00
store . msgProvider = p
}
2021-11-05 20:09:48 +00:00
// Start initializes the WakuStore by enabling the protocol and fetching records from a message provider
2021-11-09 16:18:57 +00:00
func ( store * WakuStore ) Start ( ctx context . Context ) {
2021-11-01 12:38:03 +00:00
if store . started {
2021-04-07 21:16:29 +00:00
return
}
2022-05-30 18:48:22 +00:00
if store . msgProvider == nil {
store . log . Info ( "Store protocol started (no message provider)" )
return
}
2021-11-01 12:38:03 +00:00
store . started = true
store . ctx = ctx
2021-11-04 16:19:59 +00:00
store . MsgC = make ( chan * protocol . Envelope , 1024 )
2021-11-01 12:38:03 +00:00
2022-02-23 15:06:47 +00:00
store . h . SetStreamHandlerMatch ( StoreID_v20beta4 , protocol . PrefixTextMatch ( string ( StoreID_v20beta4 ) ) , store . onRequest )
2021-03-18 16:40:47 +00:00
2021-11-23 15:03:12 +00:00
store . wg . Add ( 1 )
2021-06-28 13:20:23 +00:00
go store . storeIncomingMessages ( ctx )
2021-04-19 00:03:16 +00:00
2022-01-18 18:17:06 +00:00
store . log . Info ( "Store protocol started" )
2021-10-25 19:41:08 +00:00
}
2022-02-23 15:01:53 +00:00
func ( store * WakuStore ) storeMessage ( env * protocol . Envelope ) error {
2022-06-01 19:51:28 +00:00
// Ensure that messages don't "jump" to the front of the queue with future timestamps
if env . Index ( ) . SenderTime - env . Index ( ) . ReceiverTime > int64 ( MaxTimeVariance ) {
return ErrFutureMessage
}
2022-05-30 18:48:22 +00:00
err := store . msgProvider . Put ( env )
2021-06-10 13:00:06 +00:00
if err != nil {
2022-05-30 15:55:30 +00:00
store . log . Error ( "storing message" , zap . Error ( err ) )
2021-10-30 23:19:03 +00:00
metrics . RecordStoreError ( store . ctx , "store_failure" )
2022-02-23 15:01:53 +00:00
return err
2021-06-10 13:00:06 +00:00
}
2021-06-28 13:20:23 +00:00
2022-02-23 15:01:53 +00:00
return nil
2021-06-10 13:00:06 +00:00
}
2021-06-28 13:20:23 +00:00
func ( store * WakuStore ) storeIncomingMessages ( ctx context . Context ) {
2021-11-23 15:03:12 +00:00
defer store . wg . Done ( )
2021-06-10 13:00:06 +00:00
for envelope := range store . MsgC {
2022-02-23 15:01:53 +00:00
_ = store . storeMessage ( envelope )
2021-03-18 16:40:47 +00:00
}
}
func ( store * WakuStore ) onRequest ( s network . Stream ) {
defer s . Close ( )
2022-05-30 15:55:30 +00:00
logger := store . log . With ( logging . HostID ( "peer" , s . Conn ( ) . RemotePeer ( ) ) )
2021-04-22 00:09:37 +00:00
historyRPCRequest := & pb . HistoryRPC { }
2021-03-18 23:21:45 +00:00
2021-04-07 21:16:29 +00:00
writer := protoio . NewDelimitedWriter ( s )
2021-11-25 14:18:33 +00:00
reader := protoio . NewDelimitedReader ( s , math . MaxInt32 )
2021-03-18 23:21:45 +00:00
2021-04-07 21:16:29 +00:00
err := reader . ReadMsg ( historyRPCRequest )
2021-03-18 16:40:47 +00:00
if err != nil {
2022-05-30 15:55:30 +00:00
logger . Error ( "reading request" , zap . Error ( err ) )
2021-10-30 23:19:03 +00:00
metrics . RecordStoreError ( store . ctx , "decodeRPCFailure" )
2021-03-18 16:40:47 +00:00
return
}
2022-05-30 15:55:30 +00:00
logger = logger . With ( zap . String ( "id" , historyRPCRequest . RequestId ) )
if query := historyRPCRequest . Query ; query != nil {
logger = logger . With ( logging . Filters ( query . GetContentFilters ( ) ) )
}
logger . Info ( "received query" )
2021-03-18 16:40:47 +00:00
2021-04-22 00:09:37 +00:00
historyResponseRPC := & pb . HistoryRPC { }
2021-03-18 16:40:47 +00:00
historyResponseRPC . RequestId = historyRPCRequest . RequestId
historyResponseRPC . Response = store . FindMessages ( historyRPCRequest . Query )
2022-05-30 15:55:30 +00:00
logger = logger . With ( zap . Int ( "messages" , len ( historyResponseRPC . Response . Messages ) ) )
2021-04-07 21:16:29 +00:00
err = writer . WriteMsg ( historyResponseRPC )
2021-03-18 16:40:47 +00:00
if err != nil {
2022-05-30 15:55:30 +00:00
logger . Error ( "writing response" , zap . Error ( err ) , logging . PagingInfo ( historyResponseRPC . Response . PagingInfo ) )
2021-07-29 12:40:54 +00:00
_ = s . Reset ( )
2021-03-18 16:40:47 +00:00
} else {
2022-05-30 15:55:30 +00:00
logger . Info ( "response sent" )
2021-03-18 16:40:47 +00:00
}
}
2021-04-15 02:17:53 +00:00
type HistoryRequestParameters struct {
2021-04-15 17:55:40 +00:00
selectedPeer peer . ID
2021-04-15 02:17:53 +00:00
requestId [ ] byte
2021-04-22 00:09:37 +00:00
cursor * pb . Index
pageSize uint64
asc bool
2021-04-15 02:17:53 +00:00
s * WakuStore
}
type HistoryRequestOption func ( * HistoryRequestParameters )
2021-11-05 20:09:48 +00:00
// WithPeer is an option used to specify the peerID to request the message history
2021-04-15 17:55:40 +00:00
func WithPeer ( p peer . ID ) HistoryRequestOption {
2021-04-15 02:17:53 +00:00
return func ( params * HistoryRequestParameters ) {
2021-04-15 17:55:40 +00:00
params . selectedPeer = p
2021-04-15 02:17:53 +00:00
}
}
2021-11-05 20:09:48 +00:00
// WithAutomaticPeerSelection is an option used to randomly select a peer from the store
// to request the message history
2021-04-15 02:17:53 +00:00
func WithAutomaticPeerSelection ( ) HistoryRequestOption {
return func ( params * HistoryRequestParameters ) {
2022-05-30 15:55:30 +00:00
p , err := utils . SelectPeer ( params . s . h , string ( StoreID_v20beta4 ) , params . s . log )
2021-06-16 10:14:22 +00:00
if err == nil {
params . selectedPeer = * p
} else {
2022-05-30 15:55:30 +00:00
params . s . log . Info ( "selecting peer" , zap . Error ( err ) )
2021-06-16 10:14:22 +00:00
}
2021-04-15 02:17:53 +00:00
}
}
2021-11-09 23:34:04 +00:00
func WithFastestPeerSelection ( ctx context . Context ) HistoryRequestOption {
return func ( params * HistoryRequestParameters ) {
2022-05-30 15:55:30 +00:00
p , err := utils . SelectPeerWithLowestRTT ( ctx , params . s . h , string ( StoreID_v20beta4 ) , params . s . log )
2021-11-09 23:34:04 +00:00
if err == nil {
params . selectedPeer = * p
} else {
2022-05-30 15:55:30 +00:00
params . s . log . Info ( "selecting peer" , zap . Error ( err ) )
2021-11-09 23:34:04 +00:00
}
}
}
2021-04-15 02:17:53 +00:00
func WithRequestId ( requestId [ ] byte ) HistoryRequestOption {
return func ( params * HistoryRequestParameters ) {
params . requestId = requestId
}
}
func WithAutomaticRequestId ( ) HistoryRequestOption {
return func ( params * HistoryRequestParameters ) {
2021-04-28 20:10:44 +00:00
params . requestId = protocol . GenerateRequestId ( )
2021-04-15 02:17:53 +00:00
}
}
2021-04-22 00:09:37 +00:00
func WithCursor ( c * pb . Index ) HistoryRequestOption {
2021-04-15 02:17:53 +00:00
return func ( params * HistoryRequestParameters ) {
params . cursor = c
}
}
2021-11-05 20:09:48 +00:00
// WithPaging is an option used to specify the order and maximum number of records to return
2021-04-15 02:17:53 +00:00
func WithPaging ( asc bool , pageSize uint64 ) HistoryRequestOption {
return func ( params * HistoryRequestParameters ) {
params . asc = asc
params . pageSize = pageSize
}
}
2021-11-05 20:09:48 +00:00
// Default options to be used when querying a store node for results
2021-04-15 02:17:53 +00:00
func DefaultOptions ( ) [ ] HistoryRequestOption {
return [ ] HistoryRequestOption {
WithAutomaticRequestId ( ) ,
WithAutomaticPeerSelection ( ) ,
2021-11-05 20:09:48 +00:00
WithPaging ( true , MaxPageSize ) ,
2021-04-15 02:17:53 +00:00
}
}
2021-06-10 13:00:06 +00:00
func ( store * WakuStore ) queryFrom ( ctx context . Context , q * pb . HistoryQuery , selectedPeer peer . ID , requestId [ ] byte ) ( * pb . HistoryResponse , error ) {
2022-05-30 15:55:30 +00:00
logger := store . log . With ( logging . HostID ( "peer" , selectedPeer ) )
logger . Info ( "querying message history" )
2021-10-10 15:46:00 +00:00
2022-03-03 16:04:03 +00:00
// We connect first so dns4 addresses are resolved (NewStream does not do it)
err := store . h . Connect ( ctx , store . h . Peerstore ( ) . PeerInfo ( selectedPeer ) )
if err != nil {
2022-05-30 15:55:30 +00:00
logger . Error ( "connecting to peer" , zap . Error ( err ) )
2022-03-03 16:04:03 +00:00
return nil , err
}
2022-02-23 15:06:47 +00:00
connOpt , err := store . h . NewStream ( ctx , selectedPeer , StoreID_v20beta4 )
2021-06-10 13:00:06 +00:00
if err != nil {
2022-05-30 15:55:30 +00:00
logger . Error ( "creating stream to peer" , zap . Error ( err ) )
2021-06-10 13:00:06 +00:00
return nil , err
}
defer connOpt . Close ( )
2021-07-29 12:40:54 +00:00
defer func ( ) {
_ = connOpt . Reset ( )
} ( )
2021-06-10 13:00:06 +00:00
historyRequest := & pb . HistoryRPC { Query : q , RequestId : hex . EncodeToString ( requestId ) }
writer := protoio . NewDelimitedWriter ( connOpt )
2021-11-25 14:18:33 +00:00
reader := protoio . NewDelimitedReader ( connOpt , math . MaxInt32 )
2021-06-10 13:00:06 +00:00
err = writer . WriteMsg ( historyRequest )
if err != nil {
2022-05-30 15:55:30 +00:00
logger . Error ( "writing request" , zap . Error ( err ) )
2021-06-10 13:00:06 +00:00
return nil , err
}
historyResponseRPC := & pb . HistoryRPC { }
err = reader . ReadMsg ( historyResponseRPC )
if err != nil {
2022-05-30 15:55:30 +00:00
logger . Error ( "reading response" , zap . Error ( err ) )
2021-10-30 23:19:03 +00:00
metrics . RecordStoreError ( store . ctx , "decodeRPCFailure" )
2021-06-10 13:00:06 +00:00
return nil , err
}
2022-05-30 18:48:22 +00:00
metrics . RecordMessage ( ctx , "retrieved" , len ( historyResponseRPC . Response . Messages ) )
2021-06-28 13:20:23 +00:00
2021-06-10 13:00:06 +00:00
return historyResponseRPC . Response , nil
}
2021-10-31 19:00:38 +00:00
func ( store * WakuStore ) Query ( ctx context . Context , query Query , opts ... HistoryRequestOption ) ( * Result , error ) {
q := & pb . HistoryQuery {
PubsubTopic : query . Topic ,
ContentFilters : [ ] * pb . ContentFilter { } ,
StartTime : query . StartTime ,
EndTime : query . EndTime ,
PagingInfo : & pb . PagingInfo { } ,
}
for _ , cf := range query . ContentTopics {
q . ContentFilters = append ( q . ContentFilters , & pb . ContentFilter { ContentTopic : cf } )
}
2021-04-15 02:17:53 +00:00
params := new ( HistoryRequestParameters )
params . s = store
2021-04-28 20:10:44 +00:00
optList := DefaultOptions ( )
optList = append ( optList , opts ... )
for _ , opt := range optList {
2021-04-15 02:17:53 +00:00
opt ( params )
}
2021-04-15 17:55:40 +00:00
if params . selectedPeer == "" {
2021-04-15 02:17:53 +00:00
return nil , ErrNoPeersAvailable
}
if len ( params . requestId ) == 0 {
return nil , ErrInvalidId
}
if params . cursor != nil {
q . PagingInfo . Cursor = params . cursor
}
if params . asc {
2021-04-22 00:09:37 +00:00
q . PagingInfo . Direction = pb . PagingInfo_FORWARD
2021-04-15 02:17:53 +00:00
} else {
2021-04-22 00:09:37 +00:00
q . PagingInfo . Direction = pb . PagingInfo_BACKWARD
2021-03-18 16:40:47 +00:00
}
2021-04-15 02:17:53 +00:00
q . PagingInfo . PageSize = params . pageSize
2021-03-18 16:40:47 +00:00
2021-10-31 19:00:38 +00:00
response , err := store . queryFrom ( ctx , q , params . selectedPeer , params . requestId )
if err != nil {
return nil , err
}
if response . Error == pb . HistoryResponse_INVALID_CURSOR {
return nil , errors . New ( "invalid cursor" )
}
return & Result {
Messages : response . Messages ,
cursor : response . PagingInfo . Cursor ,
query : q ,
peerId : params . selectedPeer ,
} , nil
}
2021-11-05 20:09:48 +00:00
// Next is used with to retrieve the next page of rows from a query response.
// If no more records are found, the result will not contain any messages.
// This function is useful for iterating over results without having to manually
// specify the cursor and pagination order and max number of results
2021-10-31 19:00:38 +00:00
func ( store * WakuStore ) Next ( ctx context . Context , r * Result ) ( * Result , error ) {
q := & pb . HistoryQuery {
2022-05-30 18:48:22 +00:00
PubsubTopic : r . Query ( ) . PubsubTopic ,
ContentFilters : r . Query ( ) . ContentFilters ,
StartTime : r . Query ( ) . StartTime ,
EndTime : r . Query ( ) . EndTime ,
2021-10-31 19:00:38 +00:00
PagingInfo : & pb . PagingInfo {
2022-05-30 18:48:22 +00:00
PageSize : r . Query ( ) . PagingInfo . PageSize ,
Direction : r . Query ( ) . PagingInfo . Direction ,
2021-10-31 19:00:38 +00:00
Cursor : & pb . Index {
2022-05-30 18:48:22 +00:00
Digest : r . Cursor ( ) . Digest ,
ReceiverTime : r . Cursor ( ) . ReceiverTime ,
SenderTime : r . Cursor ( ) . SenderTime ,
PubsubTopic : r . Cursor ( ) . PubsubTopic ,
2021-10-31 19:00:38 +00:00
} ,
} ,
}
2022-05-30 18:48:22 +00:00
response , err := store . queryFrom ( ctx , q , r . PeerID ( ) , protocol . GenerateRequestId ( ) )
2021-10-31 19:00:38 +00:00
if err != nil {
return nil , err
}
if response . Error == pb . HistoryResponse_INVALID_CURSOR {
return nil , errors . New ( "invalid cursor" )
}
return & Result {
Messages : response . Messages ,
cursor : response . PagingInfo . Cursor ,
query : q ,
2022-05-30 18:48:22 +00:00
peerId : r . PeerID ( ) ,
2021-10-31 19:00:38 +00:00
} , nil
2021-06-10 13:00:06 +00:00
}
2022-01-18 18:17:06 +00:00
func ( store * WakuStore ) queryLoop ( ctx context . Context , query * pb . HistoryQuery , candidateList [ ] peer . ID ) ( [ ] * pb . WakuMessage , error ) {
2021-06-10 13:00:06 +00:00
// loops through the candidateList in order and sends the query to each until one of the query gets resolved successfully
// returns the number of retrieved messages, or error if all the requests fail
2022-01-18 18:17:06 +00:00
queryWg := sync . WaitGroup { }
queryWg . Add ( len ( candidateList ) )
resultChan := make ( chan * pb . HistoryResponse , len ( candidateList ) )
2021-06-10 13:00:06 +00:00
for _ , peer := range candidateList {
2022-01-18 18:17:06 +00:00
func ( ) {
defer queryWg . Done ( )
result , err := store . queryFrom ( ctx , query , peer , protocol . GenerateRequestId ( ) )
if err == nil {
resultChan <- result
2022-02-23 15:01:53 +00:00
return
2022-01-18 18:17:06 +00:00
}
2022-05-30 15:55:30 +00:00
store . log . Error ( "resuming history" , logging . HostID ( "peer" , peer ) , zap . Error ( err ) )
2022-01-18 18:17:06 +00:00
} ( )
}
queryWg . Wait ( )
close ( resultChan )
var messages [ ] * pb . WakuMessage
hasResults := false
for result := range resultChan {
hasResults = true
messages = append ( messages , result . Messages ... )
}
if hasResults {
return messages , nil
2021-03-18 16:40:47 +00:00
}
2021-06-10 13:00:06 +00:00
return nil , ErrFailedQuery
}
2022-05-30 18:48:22 +00:00
func ( store * WakuStore ) findLastSeen ( ) ( int64 , error ) {
return store . msgProvider . MostRecentTimestamp ( )
2021-06-10 13:00:06 +00:00
}
2021-03-18 16:40:47 +00:00
2022-02-23 15:01:53 +00:00
func max ( x , y int64 ) int64 {
if x > y {
return x
}
return y
}
2021-11-05 20:09:48 +00:00
// Resume retrieves the history of waku messages published on the default waku pubsub topic since the last time the waku store node has been online
2021-06-10 13:00:06 +00:00
// messages are stored in the store node's messages field and in the message db
// the offline time window is measured as the difference between the current time and the timestamp of the most recent persisted waku message
// an offset of 20 second is added to the time window to count for nodes asynchrony
// the history is fetched from one of the peers persisted in the waku store node's peer manager unit
// peerList indicates the list of peers to query from. The history is fetched from the first available peer in this list. Such candidates should be found through a discovery method (to be developed).
// if no peerList is passed, one of the peers in the underlying peer manager unit of the store protocol is picked randomly to fetch the history from. The history gets fetched successfully if the dialed peer has been online during the queried time window.
// the resume proc returns the number of retrieved messages if no error occurs, otherwise returns the error string
2021-10-09 18:18:53 +00:00
func ( store * WakuStore ) Resume ( ctx context . Context , pubsubTopic string , peerList [ ] peer . ID ) ( int , error ) {
2021-11-01 12:38:03 +00:00
if ! store . started {
return 0 , errors . New ( "can't resume: store has not started" )
}
2021-10-12 13:12:54 +00:00
currentTime := utils . GetUnixEpoch ( )
2022-05-30 18:48:22 +00:00
lastSeenTime , err := store . findLastSeen ( )
if err != nil {
return 0 , err
}
2021-06-10 13:00:06 +00:00
2022-02-23 15:01:53 +00:00
var offset int64 = int64 ( 20 * time . Nanosecond )
2021-06-10 13:00:06 +00:00
currentTime = currentTime + offset
2022-02-23 15:01:53 +00:00
lastSeenTime = max ( lastSeenTime - offset , 0 )
2021-06-10 13:00:06 +00:00
rpc := & pb . HistoryQuery {
PubsubTopic : pubsubTopic ,
StartTime : lastSeenTime ,
EndTime : currentTime ,
PagingInfo : & pb . PagingInfo {
PageSize : 0 ,
Direction : pb . PagingInfo_BACKWARD ,
} ,
}
2021-04-07 21:16:29 +00:00
2021-12-08 14:21:30 +00:00
if len ( peerList ) == 0 {
2022-05-30 15:55:30 +00:00
p , err := utils . SelectPeer ( store . h , string ( StoreID_v20beta4 ) , store . log )
2021-06-16 10:14:22 +00:00
if err != nil {
2022-05-30 15:55:30 +00:00
store . log . Info ( "selecting peer" , zap . Error ( err ) )
2021-06-10 13:00:06 +00:00
return - 1 , ErrNoPeersAvailable
}
2021-03-18 16:40:47 +00:00
2021-12-08 14:21:30 +00:00
peerList = append ( peerList , * p )
}
2022-01-18 18:17:06 +00:00
messages , err := store . queryLoop ( ctx , rpc , peerList )
2021-12-08 14:21:30 +00:00
if err != nil {
2022-05-30 15:55:30 +00:00
store . log . Error ( "resuming history" , zap . Error ( err ) )
2021-12-08 14:21:30 +00:00
return - 1 , ErrFailedToResumeHistory
2021-03-18 16:40:47 +00:00
}
2022-02-23 15:01:53 +00:00
msgCount := 0
2022-01-18 18:17:06 +00:00
for _ , msg := range messages {
2022-05-30 18:48:22 +00:00
if err = store . storeMessage ( protocol . NewEnvelope ( msg , utils . GetUnixEpoch ( ) , pubsubTopic ) ) ; err == nil {
2022-02-23 15:01:53 +00:00
msgCount ++
}
2021-03-18 16:40:47 +00:00
}
2022-05-30 15:55:30 +00:00
store . log . Info ( "retrieved messages since the last online time" , zap . Int ( "messages" , len ( messages ) ) )
2021-11-01 12:38:03 +00:00
2022-02-23 15:01:53 +00:00
return msgCount , nil
2021-03-18 16:40:47 +00:00
}
2022-03-18 19:56:34 +00:00
func ( store * WakuStore ) MessageChannel ( ) chan * protocol . Envelope {
return store . MsgC
}
2021-03-18 16:40:47 +00:00
// TODO: queryWithAccounting
2021-10-11 22:45:54 +00:00
2021-11-05 20:09:48 +00:00
// Stop closes the store message channel and removes the protocol stream handler
func ( store * WakuStore ) Stop ( ) {
store . started = false
2021-11-01 14:42:55 +00:00
2021-11-05 20:09:48 +00:00
if store . MsgC != nil {
close ( store . MsgC )
2021-11-01 14:42:55 +00:00
}
2021-11-05 20:09:48 +00:00
if store . h != nil {
2022-02-23 15:06:47 +00:00
store . h . RemoveStreamHandler ( StoreID_v20beta4 )
2021-11-01 14:42:55 +00:00
}
2021-11-23 15:03:12 +00:00
store . wg . Wait ( )
2021-10-11 22:45:54 +00:00
}