@@ -12,7 +12,6 @@ import (
1212 "github.com/filecoin-project/go-f3/certs"
1313 "github.com/filecoin-project/go-f3/gpbft"
1414
15- "github.com/Kubuxu/go-broadcast"
1615 "github.com/ipfs/go-datastore"
1716 "github.com/ipfs/go-datastore/namespace"
1817 "github.com/ipfs/go-datastore/query"
@@ -30,11 +29,12 @@ var (
3029
3130// Store is responsible for storing and relaying information about new finality certificates
3231type Store struct {
33- writeLk sync.Mutex
32+ mu sync.RWMutex
3433 ds datastore.Datastore
35- busCerts broadcast.Channel [* certs.FinalityCertificate ]
3634 firstInstance uint64
3735 powerTableFrequency uint64
36+ subscribers map [chan * certs.FinalityCertificate ]struct {}
37+ latestCertificate * certs.FinalityCertificate
3838
3939 latestPowerTable gpbft.PowerEntries
4040}
@@ -44,6 +44,7 @@ func open(ctx context.Context, ds datastore.Datastore) (*Store, error) {
4444 cs := & Store {
4545 ds : namespace .Wrap (ds , datastore .NewKey ("/certstore" )),
4646 powerTableFrequency : defaultPowerTableFrequency ,
47+ subscribers : make (map [chan * certs.FinalityCertificate ]struct {}),
4748 }
4849 err := maybeContinueDelete (ctx , ds )
4950 if err != nil {
@@ -57,14 +58,13 @@ func open(ctx context.Context, ds datastore.Datastore) (*Store, error) {
5758 return nil , fmt .Errorf ("determining latest cert: %w" , err )
5859 }
5960
60- latestCert , err : = cs .Get (ctx , latestInstance )
61+ cs . latestCertificate , err = cs .Get (ctx , latestInstance )
6162 if err != nil {
6263 return nil , fmt .Errorf ("loading latest cert: %w" , err )
6364 }
6465
65- metrics .latestInstance .Record (ctx , int64 (latestCert .GPBFTInstance ))
66- metrics .latestFinalizedEpoch .Record (ctx , latestCert .ECChain .Head ().Epoch )
67- cs .busCerts .Publish (latestCert )
66+ metrics .latestInstance .Record (ctx , int64 (cs .latestCertificate .GPBFTInstance ))
67+ metrics .latestFinalizedEpoch .Record (ctx , cs .latestCertificate .ECChain .Head ().Epoch )
6868
6969 return cs , nil
7070}
@@ -109,7 +109,7 @@ func OpenOrCreateStore(ctx context.Context, ds datastore.Datastore, firstInstanc
109109 return nil , fmt .Errorf ("failed to read initial instance number: %w" , err )
110110 }
111111 cs .firstInstance = firstInstance
112- if latest := cs .Latest () ; latest != nil {
112+ if latest := cs .latestCertificate ; latest != nil {
113113 cs .latestPowerTable , err = cs .GetPowerTable (ctx , latest .GPBFTInstance + 1 )
114114 if err != nil {
115115 return nil , fmt .Errorf ("failed to load latest power table: %w" , err )
@@ -162,7 +162,7 @@ func OpenStore(ctx context.Context, ds datastore.Datastore) (*Store, error) {
162162 return nil , fmt .Errorf ("getting first instance: %w" , err )
163163 }
164164 latestPowerTable := cs .firstInstance
165- if latest := cs .Latest () ; latest != nil {
165+ if latest := cs .latestCertificate ; latest != nil {
166166 latestPowerTable = latest .GPBFTInstance + 1
167167 }
168168 cs .latestPowerTable , err = cs .GetPowerTable (ctx , latestPowerTable )
@@ -195,7 +195,9 @@ func (cs *Store) writeInstanceNumber(ctx context.Context, key datastore.Key, val
195195
196196// Latest returns the newest available certificate
197197func (cs * Store ) Latest () * certs.FinalityCertificate {
198- return cs .busCerts .Last ()
198+ cs .mu .RLock ()
199+ defer cs .mu .RUnlock ()
200+ return cs .latestCertificate
199201}
200202
201203// Get returns the FinalityCertificate at the specified instance, or an error derived from
@@ -349,11 +351,11 @@ func (cs *Store) Put(ctx context.Context, cert *certs.FinalityCertificate) error
349351 }
350352
351353 // Take a lock to ensure ordering.
352- cs .writeLk .Lock ()
353- defer cs .writeLk .Unlock ()
354+ cs .mu .Lock ()
355+ defer cs .mu .Unlock ()
354356
355357 nextCert := cs .firstInstance
356- if latestCert := cs .Latest () ; latestCert != nil {
358+ if latestCert := cs .latestCertificate ; latestCert != nil {
357359 nextCert = latestCert .GPBFTInstance + 1
358360 }
359361 if cert .GPBFTInstance > nextCert {
@@ -412,21 +414,47 @@ func (cs *Store) Put(ctx context.Context, cert *certs.FinalityCertificate) error
412414 }
413415
414416 cs .latestPowerTable = newPowerTable
417+ cs .latestCertificate = cert
418+ for ch := range cs .subscribers {
419+ // Always drain first.
420+ select {
421+ case <- ch :
422+ default :
423+ }
424+ // Then write the latest certificate.
425+ ch <- cs .latestCertificate
426+ }
427+
415428 metrics .latestInstance .Record (ctx , int64 (cert .GPBFTInstance ))
416429 metrics .tipsetsPerInstance .Record (ctx , int64 (len (cert .ECChain .Suffix ())))
417430 metrics .latestFinalizedEpoch .Record (ctx , cert .ECChain .Head ().Epoch )
418- cs .busCerts .Publish (cert )
419431
420432 return nil
421433}
422434
423- // SubscribeForNewCerts is used to subscribe to the broadcast channel.
424- // If the passed channel is full at any point, it will be dropped from subscription and closed.
425- // To stop subscribing, either the closer function can be used or the channel can be abandoned.
426- // Passing a channel multiple times to the Subscribe function will result in a panic.
427- // The channel will receive new certificates sequentially.
428- func (cs * Store ) SubscribeForNewCerts (ch chan <- * certs.FinalityCertificate ) (last * certs.FinalityCertificate , closer func ()) {
429- return cs .busCerts .Subscribe (ch )
435+ // Subscribe subscribes to new certificate notifications. When read, it will always return the
436+ // latest not-yet-seen certificate (including the latest certificate when Subscribe is first
437+ // called, if we have any) but it will drop intermediate certificates. If you need all the
438+ // certificates, you should keep track of the last certificate you received and call GetRange to get
439+ // the ones between.
440+ //
441+ // The caller must call the closer to unsubscribe and release resources.
442+ func (cs * Store ) Subscribe () (out <- chan * certs.FinalityCertificate , closer func ()) {
443+ cs .mu .Lock ()
444+ defer cs .mu .Unlock ()
445+ ch := make (chan * certs.FinalityCertificate , 1 )
446+ if cs .latestCertificate != nil {
447+ ch <- cs .latestCertificate
448+ }
449+ cs .subscribers [ch ] = struct {}{}
450+ return ch , func () {
451+ cs .mu .Lock ()
452+ defer cs .mu .Unlock ()
453+ if _ , ok := cs .subscribers [ch ]; ok {
454+ delete (cs .subscribers , ch )
455+ close (ch )
456+ }
457+ }
430458}
431459
432460var tombstoneKey = datastore .NewKey ("/tombstone" )
0 commit comments