2424 redisPrefix = "boost-relay"
2525
2626 expiryBidCache = 45 * time .Second
27+ expiryLock = 24 * time .Second
2728
2829 RedisConfigFieldPubkey = "pubkey"
2930 RedisStatsFieldLatestSlot = "latest-slot"
@@ -91,6 +92,7 @@ type RedisCache struct {
9192 prefixTopBidValue string
9293 prefixFloorBid string
9394 prefixFloorBidValue string
95+ prefixProcessingSlot string
9496
9597 // keys
9698 keyValidatorRegistrationTimestamp string
@@ -101,6 +103,8 @@ type RedisCache struct {
101103 keyBlockBuilderStatus string
102104 keyLastSlotDelivered string
103105 keyLastHashDelivered string
106+
107+ currentSlot uint64
104108}
105109
106110func NewRedisCache (prefix , redisURI , readonlyURI string ) (* RedisCache , error ) {
@@ -132,6 +136,7 @@ func NewRedisCache(prefix, redisURI, readonlyURI string) (*RedisCache, error) {
132136 prefixTopBidValue : fmt .Sprintf ("%s/%s:top-bid-value" , redisPrefix , prefix ), // prefix:slot_parentHash_proposerPubkey
133137 prefixFloorBid : fmt .Sprintf ("%s/%s:bid-floor" , redisPrefix , prefix ), // prefix:slot_parentHash_proposerPubkey
134138 prefixFloorBidValue : fmt .Sprintf ("%s/%s:bid-floor-value" , redisPrefix , prefix ), // prefix:slot_parentHash_proposerPubkey
139+ prefixProcessingSlot : fmt .Sprintf ("%s/%s:processing-slot" , redisPrefix , prefix ), // prefix:slot
135140
136141 keyValidatorRegistrationTimestamp : fmt .Sprintf ("%s/%s:validator-registration-timestamp" , redisPrefix , prefix ),
137142 keyRelayConfig : fmt .Sprintf ("%s/%s:relay-config" , redisPrefix , prefix ),
@@ -190,6 +195,11 @@ func (r *RedisCache) keyFloorBidValue(slot uint64, parentHash, proposerPubkey st
190195 return fmt .Sprintf ("%s:%d_%s_%s" , r .prefixFloorBidValue , slot , parentHash , proposerPubkey )
191196}
192197
198+ // keyProcessingSlot returns the key for the counter of builder processes working on a given slot
199+ func (r * RedisCache ) keyProcessingSlot (slot uint64 ) string {
200+ return fmt .Sprintf ("%s:%d" , r .prefixProcessingSlot , slot )
201+ }
202+
193203func (r * RedisCache ) GetObj (key string , obj any ) (err error ) {
194204 value , err := r .client .Get (context .Background (), key ).Result ()
195205 if err != nil {
@@ -800,6 +810,51 @@ func (r *RedisCache) SetFloorBidValue(slot uint64, parentHash, proposerPubkey, v
800810 return err
801811}
802812
813+ // BeginProcessingSlot signals that a builder process is handling blocks for a given slot
814+ func (r * RedisCache ) BeginProcessingSlot (ctx context.Context , slot uint64 ) (err error ) {
815+ // Should never process more than one slot at a time
816+ if r .currentSlot != 0 {
817+ return fmt .Errorf ("already processing slot %d" , r .currentSlot )
818+ }
819+
820+ keyProcessingSlot := r .keyProcessingSlot (slot )
821+ err = r .client .Incr (ctx , keyProcessingSlot ).Err ()
822+ if err != nil {
823+ return err
824+ }
825+ r .currentSlot = slot
826+ err = r .client .Expire (ctx , keyProcessingSlot , expiryLock ).Err ()
827+ return err
828+ }
829+
830+ // EndProcessingSlot signals that a builder process is done handling blocks for the current slot
831+ func (r * RedisCache ) EndProcessingSlot (ctx context.Context ) (err error ) {
832+ // Do not decrement if called multiple times
833+ if r .currentSlot == 0 {
834+ return nil
835+ }
836+
837+ keyProcessingSlot := r .keyProcessingSlot (r .currentSlot )
838+ err = r .client .Decr (ctx , keyProcessingSlot ).Err ()
839+ r .currentSlot = 0
840+ return err
841+ }
842+
843+ // WaitForSlotComplete waits for a slot to be completed by all builder processes
844+ func (r * RedisCache ) WaitForSlotComplete (ctx context.Context , slot uint64 ) (err error ) {
845+ keyProcessingSlot := r .keyProcessingSlot (slot )
846+ for {
847+ processing , err := r .client .Get (ctx , keyProcessingSlot ).Uint64 ()
848+ if err != nil {
849+ return err
850+ }
851+ if processing == 0 {
852+ return nil
853+ }
854+ time .Sleep (50 * time .Millisecond )
855+ }
856+ }
857+
803858func (r * RedisCache ) NewPipeline () redis.Pipeliner { //nolint:ireturn,nolintlint
804859 return r .client .Pipeline ()
805860}
0 commit comments