Repository: hiero-ledger/hiero-block-node
Pull request #797: chore: fixed disabled tests
Author: login: mattp-swirldslabs
Labels: name: Tests
description: issue related to enhancing the tests
Created: 2025-03-06T18:07:39Z | Merged: 2025-03-07T20:44:46Z

## Reviewer Notes
* Realigned disabled unit test to the new consumer polling mechanism
* Fixed broken Producer metric

## Related Issue(s)
Fixes #751 


Linked issue #751: Increase historic and live block streaming test coverage
### Persona

As a block node engineer

### Request

I want to increase test coverage on the historic and live block streaming

### Goal

So that we can catch regression defects, etc

### Technical Notes

* While working on #675 and #672 I refactored much of the streaming consumer classes. I was not able to replace all of the broken tests with tests adapted to the new approach.


Diff:
diff --git a/server/src/main/java/com/hedera/block/server/consumer/OpenRangeStreamManager.java b/server/src/main/java/com/hedera/block/server/consumer/OpenRangeStreamManager.java
index b6ce800049..7e0158d0a3 100644
--- a/server/src/main/java/com/hedera/block/server/consumer/OpenRangeStreamManager.java
+++ b/server/src/main/java/com/hedera/block/server/consumer/OpenRangeStreamManager.java
@@ -384,7 +384,11 @@ public State execute(@NonNull final OpenRangeStreamManager m) throws Exception {
                     if (blockItems.getLast().hasBlockProof()) {
                         final long currentLiveBlockNumber = getBlockNumber(blockItems);
 
-                        LOGGER.log(TRACE, "Found the block proof of live block number: {0}", currentLiveBlockNumber);
+                        LOGGER.log(
+                                TRACE,
+                                "{0} - Found the block proof of live block number: {1}",
+                                m.managerId,
+                                currentLiveBlockNumber);
 
                         // The poller consumed all the block items for the current block,
                         // so we know the poller is now cued up for the start of the next block.
@@ -448,7 +452,7 @@ public State execute(@NonNull final OpenRangeStreamManager m) throws Exception {
                 if (liveDataOpt.isPresent()) {
                     // send data to the client
                     sendData(m, liveDataOpt.get().get());
-                    LOGGER.log(TRACE, "Fetched a live batch with data. Loop to get the next batch.");
+                    LOGGER.log(TRACE, "{0} - Fetched a live batch with data. Loop to get the next batch.", m.managerId);
                 } else {
                     LOGGER.log(TRACE, "{0} - No data returned from live poll. Loop to check again.", m.managerId);
                 }
diff --git a/server/src/main/java/com/hedera/block/server/mediator/LiveStreamMediatorBuilder.java b/server/src/main/java/com/hedera/block/server/mediator/LiveStreamMediatorBuilder.java
index 0066fbf9f1..01e649f790 100644
--- a/server/src/main/java/com/hedera/block/server/mediator/LiveStreamMediatorBuilder.java
+++ b/server/src/main/java/com/hedera/block/server/mediator/LiveStreamMediatorBuilder.java
@@ -1,12 +1,14 @@
 // SPDX-License-Identifier: Apache-2.0
 package com.hedera.block.server.mediator;
 
+import com.hedera.block.server.consumer.StreamManager;
 import com.hedera.block.server.events.BlockNodeEventHandler;
 import com.hedera.block.server.events.ObjectEvent;
 import com.hedera.block.server.metrics.MetricsService;
 import com.hedera.block.server.service.ServiceStatus;
 import com.hedera.hapi.block.BlockItemUnparsed;
 import com.lmax.disruptor.BatchEventProcessor;
+import com.lmax.disruptor.EventPoller;
 import edu.umd.cs.findbugs.annotations.NonNull;
 import java.util.List;
 import java.util.Map;
@@ -31,6 +33,8 @@ public class LiveStreamMediatorBuilder {
                     BatchEventProcessor<ObjectEvent<List<BlockItemUnparsed>>>>
             subscribers;
 
+    private Map<StreamManager, EventPoller<ObjectEvent<List<BlockItemUnparsed>>>> pollSubscribers;
+
     /** The initial capacity of the subscriber map. */
     private static final int SUBSCRIBER_INIT_CAPACITY = 32;
 
@@ -39,6 +43,7 @@ private LiveStreamMediatorBuilder(
             @NonNull final MediatorConfig mediatorConfig,
             @NonNull final ServiceStatus serviceStatus) {
         this.subscribers = new ConcurrentHashMap<>(SUBSCRIBER_INIT_CAPACITY);
+        this.pollSubscribers = new ConcurrentHashMap<>(SUBSCRIBER_INIT_CAPACITY);
         this.metricsService = metricsService;
         this.mediatorConfig = mediatorConfig;
         this.serviceStatus = serviceStatus;
@@ -70,16 +75,25 @@ public static LiveStreamMediatorBuilder newBuilder(
      * @param subscribers is the map of subscribers to set
      * @return the builder
      */
+    // spotless:off
     @NonNull
     public LiveStreamMediatorBuilder subscribers(
-            @NonNull
-                    final Map<
-                                    BlockNodeEventHandler<ObjectEvent<List<BlockItemUnparsed>>>,
-                                    BatchEventProcessor<ObjectEvent<List<BlockItemUnparsed>>>>
-                            subscribers) {
+            @NonNull final Map<BlockNodeEventHandler<ObjectEvent<List<BlockItemUnparsed>>>,
+                               BatchEventProcessor<ObjectEvent<List<BlockItemUnparsed>>>> subscribers) {
         this.subscribers = subscribers;
         return this;
     }
+    // spotless:on
+
+    // spotless:off
+    @NonNull
+    public LiveStreamMediatorBuilder pollSubscribers(
+            @NonNull final Map<StreamManager,
+                               EventPoller<ObjectEvent<List<BlockItemUnparsed>>>> pollSubscribers) {
+        this.pollSubscribers = pollSubscribers;
+        return this;
+    }
+    // spotless:on
 
     /**
      * Use the build method to construct a stream mediator to handle live stream events from a
@@ -89,6 +103,6 @@ public LiveStreamMediatorBuilder subscribers(
      */
     @NonNull
     public LiveStreamMediator build() {
-        return new LiveStreamMediatorImpl(subscribers, serviceStatus, metricsService, mediatorConfig);
+        return new LiveStreamMediatorImpl(subscribers, pollSubscribers, serviceStatus, metricsService, mediatorConfig);
     }
 }
diff --git a/server/src/main/java/com/hedera/block/server/mediator/LiveStreamMediatorImpl.java b/server/src/main/java/com/hedera/block/server/mediator/LiveStreamMediatorImpl.java
index eb59bad1f8..3f6a4bd962 100644
--- a/server/src/main/java/com/hedera/block/server/mediator/LiveStreamMediatorImpl.java
+++ b/server/src/main/java/com/hedera/block/server/mediator/LiveStreamMediatorImpl.java
@@ -3,17 +3,20 @@
 
 import static com.hedera.block.server.metrics.BlockNodeMetricTypes.Counter.LiveBlockItems;
 import static com.hedera.block.server.metrics.BlockNodeMetricTypes.Counter.LiveBlockStreamMediatorError;
+import static com.hedera.block.server.metrics.BlockNodeMetricTypes.Gauge.Consumers;
 import static com.hedera.block.server.metrics.BlockNodeMetricTypes.Gauge.MediatorRingBufferRemainingCapacity;
 import static java.lang.System.Logger;
 import static java.lang.System.Logger.Level.DEBUG;
 import static java.lang.System.Logger.Level.ERROR;
 
+import com.hedera.block.server.consumer.StreamManager;
 import com.hedera.block.server.events.BlockNodeEventHandler;
 import com.hedera.block.server.events.ObjectEvent;
 import com.hedera.block.server.metrics.MetricsService;
 import com.hedera.block.server.service.ServiceStatus;
 import com.hedera.hapi.block.BlockItemUnparsed;
 import com.lmax.disruptor.BatchEventProcessor;
+import com.lmax.disruptor.EventPoller;
 import edu.umd.cs.findbugs.annotations.NonNull;
 import java.util.List;
 import java.util.Map;
@@ -52,11 +55,17 @@ class LiveStreamMediatorImpl extends SubscriptionHandlerBase<List<BlockItemUnpar
                                     BlockNodeEventHandler<ObjectEvent<List<BlockItemUnparsed>>>,
                                     BatchEventProcessor<ObjectEvent<List<BlockItemUnparsed>>>>
                             subscribers,
+            @NonNull final Map<StreamManager, EventPoller<ObjectEvent<List<BlockItemUnparsed>>>> pollSubscribers,
             @NonNull final ServiceStatus serviceStatus,
             @NonNull final MetricsService metricsService,
             @NonNull final MediatorConfig mediatorConfig) {
 
-        super(subscribers, metricsService, mediatorConfig, mediatorConfig.ringBufferSize());
+        super(
+                subscribers,
+                pollSubscribers,
+                metricsService.get(Consumers),
+                mediatorConfig,
+                mediatorConfig.ringBufferSize());
 
         this.serviceStatus = serviceStatus;
         this.metricsService = metricsService;
diff --git a/server/src/main/java/com/hedera/block/server/mediator/SubscriptionHandlerBase.java b/server/src/main/java/com/hedera/block/server/mediator/SubscriptionHandlerBase.java
index 3c72a909c2..86b6b9a97e 100644
--- a/server/src/main/java/com/hedera/block/server/mediator/SubscriptionHandlerBase.java
+++ b/server/src/main/java/com/hedera/block/server/mediator/SubscriptionHandlerBase.java
@@ -1,14 +1,12 @@
 // SPDX-License-Identifier: Apache-2.0
 package com.hedera.block.server.mediator;
 
-import static com.hedera.block.server.metrics.BlockNodeMetricTypes.Gauge.Consumers;
 import static java.lang.System.Logger.Level.DEBUG;
 import static java.lang.System.Logger.Level.WARNING;
 
 import com.hedera.block.server.consumer.StreamManager;
 import com.hedera.block.server.events.BlockNodeEventHandler;
 import com.hedera.block.server.events.ObjectEvent;
-import com.hedera.block.server.metrics.MetricsService;
 import com.lmax.disruptor.BatchEventProcessor;
 import com.lmax.disruptor.BatchEventProcessorBuilder;
 import com.lmax.disruptor.EventPoller;
@@ -18,7 +16,7 @@
 import com.swirlds.metrics.api.LongGauge;
 import edu.umd.cs.findbugs.annotations.NonNull;
 import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.Objects;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
@@ -42,7 +40,7 @@ public abstract class SubscriptionHandlerBase<V> implements SubscriptionHandler<
 
     private final MediatorConfig mediatorConfig;
 
-    private final LongGauge consumerGauge;
+    private final LongGauge subscriptionGauge;
     private final ExecutorService executor;
 
     /**
@@ -53,20 +51,21 @@ public abstract class SubscriptionHandlerBase<V> implements SubscriptionHandler<
      *
      * @param subscribers the map of subscribers to batch event processors. It's recommended the map
      *     implementation is thread-safe
-     *
-     * @param metricsService the metrics service
+     * @param pollSubscribers the map of poll subscribers to event pollers. It's recommended the map
+     *     implementation is thread-safe
+     * @param subscriptionGauge the gauge to track the number of subscribers
      * @param mediatorConfig the configuration
      */
     protected SubscriptionHandlerBase(
             @NonNull final Map<BlockNodeEventHandler<ObjectEvent<V>>, BatchEventProcessor<ObjectEvent<V>>> subscribers,
-            @NonNull final MetricsService metricsService,
+            @NonNull final Map<StreamManager, EventPoller<ObjectEvent<V>>> pollSubscribers,
+            @NonNull final LongGauge subscriptionGauge,
             @NonNull final MediatorConfig mediatorConfig,
             final int ringBufferSize) {
-
         this.subscribers = subscribers;
-        this.pollSubscribers = new ConcurrentHashMap<>();
-        this.consumerGauge = metricsService.get(Consumers);
+        this.pollSubscribers = pollSubscribers;
         this.mediatorConfig = mediatorConfig;
+        this.subscriptionGauge = Objects.requireNonNull(subscriptionGauge);
 
         // Initialize and start the disruptor
         final Disruptor<ObjectEvent<V>> disruptor =
@@ -95,7 +94,7 @@ public void subscribe(@NonNull final BlockNodeEventHandler<ObjectEvent<V>> handl
             subscribers.put(handler, batchEventProcessor);
 
             // Update the subscriber metrics.
-            consumerGauge.set(subscribers.size() + pollSubscribers.size());
+            subscriptionGauge.set(subscribers.size() + pollSubscribers.size());
         }
     }
 
@@ -109,7 +108,7 @@ public Poller<ObjectEvent<V>> subscribePoller(@NonNull final StreamManager strea
             pollSubscribers.put(streamManager, eventPoller);
 
             // Update the subscriber metrics.
-            consumerGauge.set(subscribers.size() + pollSubscribers.size());
+            subscriptionGauge.set(subscribers.size() + pollSubscribers.size());
             LOGGER.log(DEBUG, "Subscribed poller");
 
             return new LiveStreamPoller<>(eventPoller, ringBuffer, mediatorConfig);
@@ -128,7 +127,7 @@ public void unsubscribePoller(@NonNull final StreamManager streamManager) {
         }
 
         // Update the subscriber metrics.
-        consumerGauge.set(subscribers.size() + pollSubscribers.size());
+        subscriptionGauge.set(subscribers.size() + pollSubscribers.size());
         LOGGER.log(DEBUG, "Unsubscribed poller");
     }
 
@@ -162,7 +161,7 @@ public void unsubscribe(@NonNull final BlockNodeEventHandler<ObjectEvent<V>> han
         }
 
         // Update the subscriber metrics.
-        consumerGauge.set(subscribers.size() + pollSubscribers.size());
+        subscriptionGauge.set(subscribers.size() + pollSubscribers.size());
     }
 
     /**
diff --git a/server/src/main/java/com/hedera/block/server/notifier/NotifierImpl.java b/server/src/main/java/com/hedera/block/server/notifier/NotifierImpl.java
index c9127345d2..1bb0185291 100644
--- a/server/src/main/java/com/hedera/block/server/notifier/NotifierImpl.java
+++ b/server/src/main/java/com/hedera/block/server/notifier/NotifierImpl.java
@@ -3,6 +3,7 @@
 
 import static com.hedera.block.server.metrics.BlockNodeMetricTypes.Counter.SuccessfulPubStreamResp;
 import static com.hedera.block.server.metrics.BlockNodeMetricTypes.Gauge.NotifierRingBufferRemainingCapacity;
+import static com.hedera.block.server.metrics.BlockNodeMetricTypes.Gauge.Producers;
 import static java.lang.System.Logger.Level.ERROR;
 
 import com.hedera.block.server.mediator.MediatorConfig;
@@ -61,7 +62,8 @@ public NotifierImpl(
 
         super(
                 new ConcurrentHashMap<>(SUBSCRIBER_INIT_CAPACITY),
-                metricsService,
+                new ConcurrentHashMap<>(SUBSCRIBER_INIT_CAPACITY),
+                metricsService.get(Producers),
                 mediatorConfig,
                 notifierConfig.ringBufferSize());
 
diff --git a/server/src/test/java/com/hedera/block/server/consumer/ConsumerStreamResponseObserverTest.java b/server/src/test/java/com/hedera/block/server/consumer/ConsumerStreamResponseObserverTest.java
index cc45fc1bc7..0a7dc442b3 100644
--- a/server/src/test/java/com/hedera/block/server/consumer/ConsumerStreamResponseObserverTest.java
+++ b/server/src/test/java/com/hedera/block/server/consumer/ConsumerStreamResponseObserverTest.java
@@ -107,7 +107,7 @@ public void testProducerTimeoutWithinWindow() {
 
         // Set up the StreamManager to poll for
         // block items
-        streamManager.execute();
+        assertTrue(streamManager.execute());
 
         // Now publish the block items to the mediator
         streamMediator.publish(blockItems);
@@ -115,7 +115,7 @@ public void testProducerTimeoutWithinWindow() {
         // Call the StreamManager to poll for
         // the block items and send them to the
         // client
-        streamManager.execute();
+        assertTrue(streamManager.execute());
 
         final BlockItemSetUnparsed blockItemSet =
                 BlockItemSetUnparsed.newBuilder().blockItems(blockItems).build();
@@ -136,7 +136,8 @@ public void testProducerTimeoutOutsideWindow() {
         final LiveStreamMediator streamMediator = LiveStreamMediatorBuilder.newBuilder(
                         metricsService, mediatorConfig, serviceStatus)
                 .build();
-        when(testClock.millis()).thenReturn(TEST_TIME, TEST_TIME + TIMEOUT_THRESHOLD_MILLIS + 1);
+        when(testClock.millis())
+                .thenReturn(TEST_TIME, TEST_TIME + TIMEOUT_THRESHOLD_MILLIS, TEST_TIME + TIMEOUT_THRESHOLD_MILLIS + 1);
 
         // Mock live streaming
         when(subscribeStreamRequest.startBlockNumber()).thenReturn(0L);
@@ -161,7 +162,7 @@ public void testProducerTimeoutOutsideWindow() {
 
         // Set up the StreamManager to poll for
         // block items
-        streamManager.execute();
+        assertTrue(streamManager.execute());
 
         // Now publish the block items to the mediator
         streamMediator.publish(blockItems);
@@ -169,7 +170,7 @@ public void testProducerTimeoutOutsideWindow() {
         // Call the StreamManager to poll for
         // the block items and send them to the
         // client
-        streamManager.execute();
+        assertFalse(streamManager.execute());
 
         // verify the mediator unsubscribed the observer
         assertFalse(streamMediator.isSubscribed(streamManager));
diff --git a/server/src/test/java/com/hedera/block/server/consumer/ConsumerStreamRunnableTest.java b/server/src/test/java/com/hedera/block/server/consumer/ConsumerStreamRunnableTest.java
index c2f8e7b118..9fde768cef 100644
--- a/server/src/test/java/com/hedera/block/server/consumer/ConsumerStreamRunnableTest.java
+++ b/server/src/test/java/com/hedera/block/server/consumer/ConsumerStreamRunnableTest.java
@@ -12,6 +12,9 @@
 
 @ExtendWith(MockitoExtension.class)
 public class ConsumerStreamRunnableTest {
+
+    private static final int TEST_TIMEOUT = 1000;
+
     @Mock
     private StreamManager streamManager;
 
@@ -24,6 +27,6 @@ public void testConsumerStreamRunnableLoopingAndExit() {
         final ConsumerStreamRunnable consumerStreamRunnable = new ConsumerStreamRunnable(streamManager);
         consumerStreamRunnable.run();
 
-        verify(streamManager, timeout(1000).times(3)).execute();
+        verify(streamManager, timeout(TEST_TIMEOUT).times(3)).execute();
     }
 }
diff --git a/server/src/test/java/com/hedera/block/server/consumer/OpenRangeStreamManagerTest.java b/server/src/test/java/com/hedera/block/server/consumer/OpenRangeStreamManagerTest.java
index 3554b44dac..4912e81c9d 100644
--- a/server/src/test/java/com/hedera/block/server/consumer/OpenRangeStreamManagerTest.java
+++ b/server/src/test/java/com/hedera/block/server/consumer/OpenRangeStreamManagerTest.java
@@ -12,6 +12,7 @@
 import static com.hedera.block.server.metrics.BlockNodeMetricTypes.Gauge.CurrentBlockNumberOutbound;
 import static com.hedera.block.server.util.PersistTestUtils.generateBlockItemsUnparsedForWithBlockNumber;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.times;
@@ -48,6 +49,7 @@
 @ExtendWith(MockitoExtension.class)
 public class OpenRangeStreamManagerTest {
 
+    private static final int TIMEOUT_THRESHOLD_MILLIS = 100;
     private static final long TEST_TIME = 1_719_427_664_950L;
 
     @Mock
@@ -96,15 +98,15 @@ public class OpenRangeStreamManagerTest {
     public void setUp() {
         // Set up the ConsumerConfig
         when(consumerConfig.maxBlockItemBatchSize()).thenReturn(1000);
-        when(consumerConfig.timeoutThresholdMillis()).thenReturn(1500);
-
-        // Set up the test clock within the time range
-        when(testClock.millis()).thenReturn(TEST_TIME, TEST_TIME + 1);
+        when(consumerConfig.timeoutThresholdMillis()).thenReturn(TIMEOUT_THRESHOLD_MILLIS);
     }
 
     @Test
     public void testHappyPathLiveStream() throws Exception {
 
+        // Set up the test clock within the time range
+        when(testClock.millis()).thenReturn(TEST_TIME, TEST_TIME + 1);
+
         final int NUM_OF_BLOCKS = 15;
 
         final List<BlockUnparsed> blocks = buildBlocks(NUM_OF_BLOCKS);
@@ -159,6 +161,9 @@ public void testHappyPathLiveStream() throws Exception {
     @Test
     public void testHappyPathHistoricToLiveStreamTransition() throws Exception {
 
+        // Set up the test clock within the time range
+        when(testClock.millis()).thenReturn(TEST_TIME, TEST_TIME + 1);
+
         final int NUM_OF_BLOCKS = 16;
         final int liveStreamInitialBlock = 8;
         final List<BlockUnparsed> blocks = buildBlocks(NUM_OF_BLOCKS);
@@ -351,6 +356,46 @@ public void testHappyPathHistoricToLiveStreamTransition() throws Exception {
         //                .onNext(buildResponse(blocks.get(15).blockItems()));
     }
 
+    @Test
+    public void testSubscribeAndUnsubscribeHandlingWithTimeout() {
+
+        // Prep the subscriptionHandler to return the poller when subscribing
+        when(subscriptionHandler.subscribePoller(any())).thenReturn(liveBlockItemPoller);
+
+        // Mock a subscribeStreamRequest indicating a live-streaming request
+        when(subscribeStreamRequest.startBlockNumber()).thenReturn(0L);
+
+        // Set up the test clock with a timeout
+        when(testClock.millis())
+                .thenReturn(TEST_TIME, TEST_TIME + TIMEOUT_THRESHOLD_MILLIS, TEST_TIME + TIMEOUT_THRESHOLD_MILLIS + 1);
+
+        final OpenRangeStreamManager streamManager = ConsumerStreamBuilder.buildStreamManager(
+                testClock,
+                subscribeStreamRequest,
+                subscriptionHandler,
+                helidonConsumerObserver,
+                blockReader,
+                serviceStatus,
+                metricsService,
+                consumerConfig);
+
+        // INIT_LIVE - transition
+        assertEquals(INIT_LIVE, streamManager.getState());
+
+        // First execute() should subscribe to the poller
+        assertTrue(streamManager.execute());
+        assertEquals(LIVE_STREAMING, streamManager.getState());
+
+        // Second execute() should fail because of the timeout
+        assertFalse(streamManager.execute());
+
+        // Verify the subscriptionHandler is called to subscribe
+        verify(subscriptionHandler, times(1)).subscribePoller(any());
+
+        // Verify the subscriptionHandler is called to unsubscribe
+        verify(subscriptionHandler, times(1)).unsubscribePoller(any());
+    }
+
     private static long parseBlockNumber(List<BlockItemUnparsed> blockItems) throws ParseException {
         if (blockItems.getFirst().hasBlockHeader()) {
             return BlockHeader.PROTOBUF
diff --git a/server/src/test/java/com/hedera/block/server/mediator/LiveStreamMediatorImplTest.java b/server/src/test/java/com/hedera/block/server/mediator/LiveStreamMediatorImplTest.java
index b46371397d..05bba82d57 100644
--- a/server/src/test/java/com/hedera/block/server/mediator/LiveStreamMediatorImplTest.java
+++ b/server/src/test/java/com/hedera/block/server/mediator/LiveStreamMediatorImplTest.java
@@ -2,6 +2,7 @@
 package com.hedera.block.server.mediator;
 
 import static com.hedera.block.server.metrics.BlockNodeMetricTypes.Counter.LiveBlockItems;
+import static com.hedera.block.server.metrics.BlockNodeMetricTypes.Gauge.Consumers;
 import static com.hedera.block.server.util.PersistTestUtils.PERSISTENCE_STORAGE_ARCHIVE_ROOT_PATH_KEY;
 import static com.hedera.block.server.util.PersistTestUtils.PERSISTENCE_STORAGE_LIVE_ROOT_PATH_KEY;
 import static com.hedera.block.server.util.PersistTestUtils.PERSISTENCE_STORAGE_UNVERIFIED_ROOT_PATH_KEY;
@@ -310,87 +311,39 @@ void testMediatorPublishEventToSubscribers() throws IOException {
         verify(asyncBlockWriterFactoryMock, timeout(TEST_TIMEOUT).times(1)).create(1L);
     }
 
-    @Disabled("@todo(751) - adapt these tests to the new streaming consumer model")
     @Test
-    void testSubAndUnsubHandling() throws IOException {
-        //        final BlockNodeContext blockNodeContext = TestConfigUtil.getTestBlockNodeContext();
-        //        final ServiceStatus serviceStatus = new ServiceStatusImpl(blockNodeContext);
-        //        final LiveStreamMediator streamMediator = LiveStreamMediatorBuilder.newBuilder(blockNodeContext,
-        // serviceStatus)
-        //                .build();
-        //
-        //        when(testClock.millis()).thenReturn(TEST_TIME, TEST_TIME + TIMEOUT_THRESHOLD_MILLIS);
-        //
-        //        final var concreteObserver1 = ConsumerStreamBuilder.build(
-        //                completionService,
-        //                testClock,
-        //                streamMediator,
-        //                helidonSubscribeStreamObserver1,
-        //                testContext.metricsService(),
-        //                testContext.configuration());
-        //        final var concreteObserver2 = ConsumerStreamBuilder.build(
-        //                completionService,
-        //                testClock,
-        //                streamMediator,
-        //                helidonSubscribeStreamObserver2,
-        //                testContext.metricsService(),
-        //                testContext.configuration());
-        //        final var concreteObserver3 = ConsumerStreamBuilder.build(
-        //                completionService,
-        //                testClock,
-        //                streamMediator,
-        //                helidonSubscribeStreamObserver3,
-        //                testContext.metricsService(),
-        //                testContext.configuration());
-        //
-        //        // Set up the subscribers
-        //        streamMediator.subscribe(concreteObserver1);
-        //        streamMediator.subscribe(concreteObserver2);
-        //        streamMediator.subscribe(concreteObserver3);
-        //
-        //        streamMediator.unsubscribe(concreteObserver1);
-        //        streamMediator.unsubscribe(concreteObserver2);
-        //        streamMediator.unsubscribe(concreteObserver3);
-        //
-        //        // Confirm the counter was never incremented
-        //        assertEquals(0, blockNodeContext.metricsService().get(LiveBlockItems).get());
-    }
+    void testPollSubscribeWhenHandlerAlreadySubscribed() {
 
-    @Disabled("@todo(751) - adapt these tests to the new streaming consumer model")
-    @Test
-    void testSubscribeWhenHandlerAlreadySubscribed() throws IOException {
-        //        final BlockNodeContext blockNodeContext = TestConfigUtil.getTestBlockNodeContext();
-        //        final LongGauge consumersGauge =
-        // blockNodeContext.metricsService().get(BlockNodeMetricTypes.Gauge.Consumers);
-        //        final ServiceStatus serviceStatus = new ServiceStatusImpl(blockNodeContext);
-        //        final LiveStreamMediator streamMediator = LiveStreamMediatorBuilder.newBuilder(blockNodeContext,
-        // serviceStatus)
-        //                .build();
-        //
-        //        final BlockNodeEventHandler<ObjectEvent<List<BlockItemUnparsed>>> concreteObserver1 =
-        //                ConsumerStreamBuilder.build(
-        //                        completionService,
-        //                        testClock,
-        //                        streamMediator,
-        //                        helidonSubscribeStreamObserver1,
-        //                        testContext.metricsService(),
-        //                        testContext.configuration());
-        //
-        //        streamMediator.subscribe(concreteObserver1);
-        //        assertTrue(streamMediator.isSubscribed(concreteObserver1));
-        //        assertEquals(1L, consumersGauge.get());
-        //
-        //        // Attempt to "re-subscribe" the observer
-        //        // Should not increment the counter or change the implementation
-        //        streamMediator.subscribe(concreteObserver1);
-        //        assertTrue(streamMediator.isSubscribed(concreteObserver1));
-        //        assertEquals(1L, consumersGauge.get());
-        //
-        //        streamMediator.unsubscribe(concreteObserver1);
-        //        assertFalse(streamMediator.isSubscribed(concreteObserver1));
-        //
-        //        // Confirm the counter was decremented
-        //        assertEquals(0L, consumersGauge.get());
+        final ServiceStatus serviceStatus = new ServiceStatusImpl(serviceConfig);
+        final LiveStreamMediator streamMediator = LiveStreamMediatorBuilder.newBuilder(
+                        metricsService, mediatorConfig, serviceStatus)
+                .build();
+
+        final StreamManager streamManager = ConsumerStreamBuilder.buildStreamManager(
+                testClock,
+                subscribeStreamRequest,
+                streamMediator,
+                helidonResponseStreamObserver1,
+                blockReader,
+                serviceStatus,
+                metricsService,
+                consumerConfig);
+
+        streamMediator.subscribePoller(streamManager);
+        assertTrue(streamMediator.isSubscribed(streamManager));
+        assertEquals(1L, metricsService.get(Consumers).get());
+
+        // Attempt to "re-subscribe" the streamManager
+        // Should not increment the counter or change the implementation
+        streamMediator.subscribePoller(streamManager);
+        assertTrue(streamMediator.isSubscribed(streamManager));
+        assertEquals(1L, metricsService.get(Consumers).get());
+
+        streamMediator.unsubscribePoller(streamManager);
+        assertFalse(streamMediator.isSubscribed(streamManager));
+
+        // Confirm the counter was decremented
+        assertEquals(0L, metricsService.get(Consumers).get());
     }
 
     @Disabled("@todo(303), @todo(306) - adapt these tests once #303 and #306 are resolved")
@@ -497,7 +450,7 @@ void testOnCloseSubscriptionHandling() throws IOException {
         //        assertTrue(streamMediator.isSubscribed(streamValidator));
     }
 
-    @Disabled("@todo(751) - adapt these tests to the new streaming consumer model")
+    @Disabled("@todo(662): Revisit this code after we implement an error channel")
     @Test
     void testMediatorBlocksPublishAfterException() throws IOException, InterruptedException {
         //        final BlockNodeContext blockNodeContext = TestConfigUtil.getTestBlockNodeContext();
@@ -606,47 +559,46 @@ void testMediatorBlocksPublishAfterException() throws IOException, InterruptedEx
         //        verify(asyncBlockWriterFactoryMock, timeout(TEST_TIMEOUT).times(1)).create(1L);
     }
 
-    @Disabled("@todo(751) - adapt these tests to the new streaming consumer model")
     @Test
     void testUnsubscribeWhenNotSubscribed() throws IOException {
-        //        final BlockNodeContext blockNodeContext = TestConfigUtil.getTestBlockNodeContext();
-        //        final ServiceStatus serviceStatus = new ServiceStatusImpl(blockNodeContext);
-        //        final LiveStreamMediator streamMediator = LiveStreamMediatorBuilder.newBuilder(blockNodeContext,
-        // serviceStatus)
-        //                .build();
-        //
-        //        // register the stream validator
-        //        final StreamPersistenceHandlerImpl handler = new StreamPersistenceHandlerImpl(
-        //                streamMediator,
-        //                notifier,
-        //                blockNodeContext,
-        //                serviceStatus,
-        //                ackHandlerMock,
-        //                asyncBlockWriterFactoryMock,
-        //                executorMock,
-        //                archiverMock,
-        //                persistenceStorageConfig);
-        //        streamMediator.subscribe(handler);
-        //
-        //        final BlockNodeEventHandler<ObjectEvent<List<BlockItemUnparsed>>> testConsumerBlockItemObserver =
-        //                ConsumerStreamBuilder.build(
-        //                        completionService,
-        //                        testClock,
-        //                        streamMediator,
-        //                        helidonSubscribeStreamObserver1,
-        //                        testContext.metricsService(),
-        //                        testContext.configuration());
-        //
-        //        // Confirm the observer is not subscribed
-        //        assertFalse(streamMediator.isSubscribed(testConsumerBlockItemObserver));
-        //
-        //        // Attempt to unsubscribe the observer
-        //        streamMediator.unsubscribe(testConsumerBlockItemObserver);
-        //
-        //        // Confirm the observer is still not subscribed
-        //        assertFalse(streamMediator.isSubscribed(testConsumerBlockItemObserver));
-        //
-        //        // Confirm the stream validator is still subscribed
-        //        assertTrue(streamMediator.isSubscribed(handler));
+        final ServiceStatus serviceStatus = new ServiceStatusImpl(serviceConfig);
+        final LiveStreamMediator streamMediator = LiveStreamMediatorBuilder.newBuilder(
+                        metricsService, mediatorConfig, serviceStatus)
+                .build();
+
+        final StreamPersistenceHandlerImpl handler = new StreamPersistenceHandlerImpl(
+                streamMediator,
+                notifier,
+                metricsService,
+                serviceStatus,
+                ackHandlerMock,
+                asyncBlockWriterFactoryMock,
+                executorMock,
+                archiverMock,
+                pathResolverMock,
+                persistenceStorageConfig);
+        streamMediator.subscribe(handler);
+
+        final StreamManager streamManager = ConsumerStreamBuilder.buildStreamManager(
+                testClock,
+                subscribeStreamRequest,
+                streamMediator,
+                helidonResponseStreamObserver1,
+                blockReader,
+                serviceStatus,
+                metricsService,
+                consumerConfig);
+
+        // Confirm the observer is not subscribed
+        assertFalse(streamMediator.isSubscribed(streamManager));
+
+        // Attempt to unsubscribe the observer
+        streamMediator.unsubscribePoller(streamManager);
+
+        // Confirm the observer is still not subscribed
+        assertFalse(streamMediator.isSubscribed(streamManager));
+
+        // Confirm the stream validator is still subscribed
+        assertTrue(streamMediator.isSubscribed(handler));
     }
 }
diff --git a/server/src/test/java/com/hedera/block/server/notifier/NotifierImplTest.java b/server/src/test/java/com/hedera/block/server/notifier/NotifierImplTest.java
index aebd7809df..44988a1ca1 100644
--- a/server/src/test/java/com/hedera/block/server/notifier/NotifierImplTest.java
+++ b/server/src/test/java/com/hedera/block/server/notifier/NotifierImplTest.java
@@ -60,7 +60,6 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
 import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.junit.jupiter.api.io.TempDir;
@@ -166,7 +165,6 @@ void setUp() throws IOException {
     }
 
     @Test
-    @Disabled("@todo(751) - adapt this test to the new streaming consumer model")
     void testRegistration() throws IOException {
         when(serviceStatus.isRunning()).thenReturn(true);
 
diff --git a/server/src/test/java/com/hedera/block/server/pbj/PbjBlockStreamServiceIntegrationTest.java b/server/src/test/java/com/hedera/block/server/pbj/PbjBlockStreamServiceIntegrationTest.java
index 67b4cef3a1..14cb929166 100644
--- a/server/src/test/java/com/hedera/block/server/pbj/PbjBlockStreamServiceIntegrationTest.java
+++ b/server/src/test/java/com/hedera/block/server/pbj/PbjBlockStreamServiceIntegrationTest.java
@@ -2,7 +2,7 @@
 package com.hedera.block.server.pbj;
 
 import static com.hedera.block.server.util.PbjProtoTestUtils.buildEmptyPublishStreamRequest;
-import static com.hedera.block.server.util.PbjProtoTestUtils.buildEmptySubscribeStreamRequest;
+import static com.hedera.block.server.util.PbjProtoTestUtils.buildLiveStreamSubscribeStreamRequest;
 import static com.hedera.block.server.util.PersistTestUtils.PERSISTENCE_STORAGE_ARCHIVE_ROOT_PATH_KEY;
 import static com.hedera.block.server.util.PersistTestUtils.PERSISTENCE_STORAGE_LIVE_ROOT_PATH_KEY;
 import static com.hedera.block.server.util.PersistTestUtils.PERSISTENCE_STORAGE_UNVERIFIED_ROOT_PATH_KEY;
@@ -22,6 +22,7 @@
 import com.hedera.block.server.ack.AckHandlerImpl;
 import com.hedera.block.server.block.BlockInfo;
 import com.hedera.block.server.consumer.ConsumerConfig;
+import com.hedera.block.server.consumer.StreamManager;
 import com.hedera.block.server.events.BlockNodeEventHandler;
 import com.hedera.block.server.events.ObjectEvent;
 import com.hedera.block.server.mediator.LiveStreamMediator;
@@ -65,6 +66,7 @@
 import com.hedera.pbj.runtime.grpc.ServiceInterface;
 import com.hedera.pbj.runtime.io.buffer.Bytes;
 import com.lmax.disruptor.BatchEventProcessor;
+import com.lmax.disruptor.EventPoller;
 import com.swirlds.config.api.Configuration;
 import io.helidon.webserver.WebServer;
 import java.io.IOException;
@@ -94,6 +96,8 @@ class PbjBlockStreamServiceIntegrationTest {
     private static final int JUNIT_TIMEOUT = 5_000;
     private static final int testTimeout = 2_000;
 
+    private static final int POLLER_SUBSCRIBER_SLEEP = 100;
+
     @Mock
     private Pipeline<? super Bytes> helidonPublishStreamObserver1;
 
@@ -220,19 +224,19 @@ void testPublishBlockStreamRegistrationAndExecution() throws IOException {
                         PbjBlockStreamService.BlockStreamMethod.subscribeBlockStream,
                         optionsMock,
                         subscribeStreamObserver1)
-                .onNext(buildEmptySubscribeStreamRequest());
+                .onNext(buildLiveStreamSubscribeStreamRequest());
         pbjBlockStreamServiceProxy
                 .open(
                         PbjBlockStreamService.BlockStreamMethod.subscribeBlockStream,
                         optionsMock,
                         subscribeStreamObserver2)
-                .onNext(buildEmptySubscribeStreamRequest());
+                .onNext(buildLiveStreamSubscribeStreamRequest());
         pbjBlockStreamServiceProxy
                 .open(
                         PbjBlockStreamService.BlockStreamMethod.subscribeBlockStream,
                         optionsMock,
                         subscribeStreamObserver3)
-                .onNext(buildEmptySubscribeStreamRequest());
+                .onNext(buildLiveStreamSubscribeStreamRequest());
 
         final List<BlockItemUnparsed> blockItems = generateBlockItemsUnparsed(numberOfBlocks);
         for (final BlockItemUnparsed blockItem : blockItems) {
@@ -313,19 +317,19 @@ void testFullProducerConsumerHappyPath() throws IOException {
                         PbjBlockStreamService.BlockStreamMethod.subscribeBlockStream,
                         optionsMock,
                         subscribeStreamObserver1)
-                .onNext(buildEmptySubscribeStreamRequest());
+                .onNext(buildLiveStreamSubscribeStreamRequest());
         pbjBlockStreamServiceProxy
                 .open(
                         PbjBlockStreamService.BlockStreamMethod.subscribeBlockStream,
                         optionsMock,
                         subscribeStreamObserver2)
-                .onNext(buildEmptySubscribeStreamRequest());
+                .onNext(buildLiveStreamSubscribeStreamRequest());
         pbjBlockStreamServiceProxy
                 .open(
                         PbjBlockStreamService.BlockStreamMethod.subscribeBlockStream,
                         optionsMock,
                         subscribeStreamObserver3)
-                .onNext(buildEmptySubscribeStreamRequest());
+                .onNext(buildLiveStreamSubscribeStreamRequest());
 
         final List<BlockItemUnparsed> blockItems = generateBlockItemsUnparsed(numberOfBlocks);
         for (BlockItemUnparsed blockItem : blockItems) {
@@ -354,8 +358,7 @@ void testFullProducerConsumerHappyPath() throws IOException {
 
     @Test
     @Timeout(value = JUNIT_TIMEOUT, unit = TimeUnit.MILLISECONDS)
-    @Disabled("@todo(751) - adapt this test to the new streaming consumer model")
-    void testFullWithSubscribersAddedDynamically() throws IOException {
+    void testFullWithSubscribersAddedDynamically() throws IOException, InterruptedException {
         final int numberOfBlocks = 100;
 
         final ExecutorService executor = Executors.newFixedThreadPool(1);
@@ -373,19 +376,19 @@ void testFullWithSubscribersAddedDynamically() throws IOException {
                         PbjBlockStreamService.BlockStreamMethod.subscribeBlockStream,
                         optionsMock,
                         subscribeStreamObserver1)
-                .onNext(buildEmptySubscribeStreamRequest());
+                .onNext(buildLiveStreamSubscribeStreamRequest());
         pbjBlockStreamServiceProxy
                 .open(
                         PbjBlockStreamService.BlockStreamMethod.subscribeBlockStream,
                         optionsMock,
                         subscribeStreamObserver2)
-                .onNext(buildEmptySubscribeStreamRequest());
+                .onNext(buildLiveStreamSubscribeStreamRequest());
         pbjBlockStreamServiceProxy
                 .open(
                         PbjBlockStreamService.BlockStreamMethod.subscribeBlockStream,
                         optionsMock,
                         subscribeStreamObserver3)
-                .onNext(buildEmptySubscribeStreamRequest());
+                .onNext(buildLiveStreamSubscribeStreamRequest());
 
         final List<BlockItemUnparsed> blockItems = generateBlockItemsUnparsed(numberOfBlocks);
         for (int i = 0; i < blockItems.size(); i++) {
@@ -399,7 +402,10 @@ void testFullWithSubscribersAddedDynamically() throws IOException {
                                 PbjBlockStreamService.BlockStreamMethod.subscribeBlockStream,
                                 optionsMock,
                                 subscribeStreamObserver4)
-                        .onNext(buildEmptySubscribeStreamRequest());
+                        .onNext(buildLiveStreamSubscribeStreamRequest());
+                // Pause here for the StreamManager to
+                // subscribe to the StreamMediator
+                Thread.sleep(POLLER_SUBSCRIBER_SLEEP);
             }
             // Transmit the BlockItem
             producerPipeline.onNext(PublishStreamRequestUnparsed.PROTOBUF.toBytes(publishStreamRequest));
@@ -410,7 +416,10 @@ void testFullWithSubscribersAddedDynamically() throws IOException {
                                 PbjBlockStreamService.BlockStreamMethod.subscribeBlockStream,
                                 optionsMock,
                                 subscribeStreamObserver5)
-                        .onNext(buildEmptySubscribeStreamRequest());
+                        .onNext(buildLiveStreamSubscribeStreamRequest());
+                // Pause here for the StreamManager to
+                // subscribe to the StreamMediator
+                Thread.sleep(POLLER_SUBSCRIBER_SLEEP);
             }
             // Add a new subscriber
             if (i == 88) {
@@ -419,7 +428,10 @@ void testFullWithSubscribersAddedDynamically() throws IOException {
                                 PbjBlockStreamService.BlockStreamMethod.subscribeBlockStream,
                                 optionsMock,
                                 subscribeStreamObserver6)
-                        .onNext(buildEmptySubscribeStreamRequest());
+                        .onNext(buildLiveStreamSubscribeStreamRequest());
+                // Pause here for the StreamManager to
+                // subscribe to the StreamMediator
+                Thread.sleep(POLLER_SUBSCRIBER_SLEEP);
             }
         }
 
@@ -440,17 +452,18 @@ void testFullWithSubscribersAddedDynamically() throws IOException {
 
     @Test
     @Timeout(value = JUNIT_TIMEOUT, unit = TimeUnit.MILLISECONDS)
-    @Disabled("@todo(751) - adapt this test to the new streaming consumer model")
     void testSubAndUnsubWhileStreaming() throws InterruptedException, IOException {
         final int numberOfBlocks = 100;
-        final LinkedHashMap<
+        final Map<
                         BlockNodeEventHandler<ObjectEvent<List<BlockItemUnparsed>>>,
                         BatchEventProcessor<ObjectEvent<List<BlockItemUnparsed>>>>
-                consumers = new LinkedHashMap<>();
+                consumers = new ConcurrentHashMap<>();
+        final LinkedHashMap<StreamManager, EventPoller<ObjectEvent<List<BlockItemUnparsed>>>> pollConsumers =
+                new LinkedHashMap<>();
         final ServiceStatus serviceStatus = new ServiceStatusImpl(serviceConfig);
         final BlockInfo blockInfo = new BlockInfo(1L);
         serviceStatus.setLatestAckedBlock(blockInfo);
-        final LiveStreamMediator streamMediator = buildStreamMediator(consumers, serviceStatus);
+        final LiveStreamMediator streamMediator = buildStreamMediator(consumers, pollConsumers, serviceStatus);
         final AsyncNoOpWriterFactory writerFactory = new AsyncNoOpWriterFactory(ackHandlerMock, metricsService);
         final StreamPersistenceHandlerImpl blockNodeEventHandler = new StreamPersistenceHandlerImpl(
                 streamMediator,
@@ -486,19 +499,19 @@ void testSubAndUnsubWhileStreaming() throws InterruptedException, IOException {
                         PbjBlockStreamService.BlockStreamMethod.subscribeBlockStream,
                         optionsMock,
                         subscribeStreamObserver1)
-                .onNext(buildEmptySubscribeStreamRequest());
+                .onNext(buildLiveStreamSubscribeStreamRequest());
         pbjBlockStreamServiceProxy
                 .open(
                         PbjBlockStreamService.BlockStreamMethod.subscribeBlockStream,
                         optionsMock,
                         subscribeStreamObserver2)
-                .onNext(buildEmptySubscribeStreamRequest());
+                .onNext(buildLiveStreamSubscribeStreamRequest());
         pbjBlockStreamServiceProxy
                 .open(
                         PbjBlockStreamService.BlockStreamMethod.subscribeBlockStream,
                         optionsMock,
                         subscribeStreamObserver3)
-                .onNext(buildEmptySubscribeStreamRequest());
+                .onNext(buildLiveStreamSubscribeStreamRequest());
 
         final List<BlockItemUnparsed> blockItems = generateBlockItemsUnparsed(numberOfBlocks);
         for (int i = 0; i < blockItems.size(); i++) {
@@ -513,20 +526,18 @@ void testSubAndUnsubWhileStreaming() throws InterruptedException, IOException {
             if (i == 10) {
                 // Pause here to ensure the last sent block item is received.
                 // This makes the test deterministic.
-                Thread.sleep(50);
-                final BlockNodeEventHandler<ObjectEvent<List<BlockItemUnparsed>>> k =
-                        consumers.firstEntry().getKey();
-                streamMediator.unsubscribe(k);
+                Thread.sleep(POLLER_SUBSCRIBER_SLEEP);
+                final StreamManager k = pollConsumers.firstEntry().getKey();
+                streamMediator.unsubscribePoller(k);
             }
 
             // Remove 2nd subscriber
             if (i == 60) {
                 // Pause here to ensure the last sent block item is received.
                 // This makes the test deterministic.
-                Thread.sleep(50);
-                final BlockNodeEventHandler<ObjectEvent<List<BlockItemUnparsed>>> k =
-                        consumers.firstEntry().getKey();
-                streamMediator.unsubscribe(k);
+                Thread.sleep(POLLER_SUBSCRIBER_SLEEP);
+                final StreamManager k = pollConsumers.firstEntry().getKey();
+                streamMediator.unsubscribePoller(k);
             }
 
             // Add a new subscriber
@@ -536,17 +547,18 @@ void testSubAndUnsubWhileStreaming() throws InterruptedException, IOException {
                                 PbjBlockStreamService.BlockStreamMethod.subscribeBlockStream,
                                 optionsMock,
                                 subscribeStreamObserver4)
-                        .onNext(buildEmptySubscribeStreamRequest());
+                        .onNext(buildLiveStreamSubscribeStreamRequest());
+                Thread.sleep(POLLER_SUBSCRIBER_SLEEP);
             }
 
             // Remove 3rd subscriber
             if (i == 70) {
                 // Pause here to ensure the last sent block item is received.
                 // This makes the test deterministic.
-                Thread.sleep(50);
-                final BlockNodeEventHandler<ObjectEvent<List<BlockItemUnparsed>>> k =
-                        consumers.firstEntry().getKey();
-                streamMediator.unsubscribe(k);
+                Thread.sleep(POLLER_SUBSCRIBER_SLEEP);
+                final StreamManager k = pollConsumers.firstEntry().getKey();
+                streamMediator.unsubscribePoller(k);
+                Thread.sleep(POLLER_SUBSCRIBER_SLEEP);
             }
 
             // Add a new subscriber
@@ -556,7 +568,8 @@ void testSubAndUnsubWhileStreaming() throws InterruptedException, IOException {
                                 PbjBlockStreamService.BlockStreamMethod.subscribeBlockStream,
                                 optionsMock,
                                 subscribeStreamObserver5)
-                        .onNext(buildEmptySubscribeStreamRequest());
+                        .onNext(buildLiveStreamSubscribeStreamRequest());
+                Thread.sleep(POLLER_SUBSCRIBER_SLEEP);
             }
 
             // Add a new subscriber
@@ -566,7 +579,8 @@ void testSubAndUnsubWhileStreaming() throws InterruptedException, IOException {
                                 PbjBlockStreamService.BlockStreamMethod.subscribeBlockStream,
                                 optionsMock,
                                 subscribeStreamObserver6)
-                        .onNext(buildEmptySubscribeStreamRequest());
+                        .onNext(buildLiveStreamSubscribeStreamRequest());
+                Thread.sleep(POLLER_SUBSCRIBER_SLEEP);
             }
         }
 
@@ -589,12 +603,13 @@ void testSubAndUnsubWhileStreaming() throws InterruptedException, IOException {
 
     @Test
     @Timeout(value = JUNIT_TIMEOUT, unit = TimeUnit.MILLISECONDS)
-    @Disabled("@todo(751) - adapt this test to the new streaming consumer model")
-    void testMediatorExceptionHandlingWhenPersistenceFailure() throws IOException {
-        final ConcurrentHashMap<
+    void testMediatorExceptionHandlingWhenPersistenceFailure() throws IOException, InterruptedException {
+        final Map<
                         BlockNodeEventHandler<ObjectEvent<List<BlockItemUnparsed>>>,
                         BatchEventProcessor<ObjectEvent<List<BlockItemUnparsed>>>>
                 consumers = new ConcurrentHashMap<>();
+        final ConcurrentHashMap<StreamManager, EventPoller<ObjectEvent<List<BlockItemUnparsed>>>> pollConsumers =
+                new ConcurrentHashMap<>();
         // Use a spy to use the real object but also verify the behavior.
         final ServiceStatus serviceStatus = spy(new ServiceStatusImpl(serviceConfig));
         final BlockInfo blockInfo = new BlockInfo(1L);
@@ -607,7 +622,7 @@ void testMediatorExceptionHandlingWhenPersistenceFailure() throws IOException {
         final List<BlockItemUnparsed> blockItems = generateBlockItemsUnparsed(1);
 
         // the mocked factory will throw a npe
-        final LiveStreamMediator streamMediator = buildStreamMediator(consumers, serviceStatus);
+        final LiveStreamMediator streamMediator = buildStreamMediator(consumers, pollConsumers, serviceStatus);
         final Notifier notifier =
                 new NotifierImpl(streamMediator, metricsService, notifierConfig, mediatorConfig, serviceStatus);
         final StreamPersistenceHandlerImpl blockNodeEventHandler = new StreamPersistenceHandlerImpl(
@@ -645,22 +660,24 @@ void testMediatorExceptionHandlingWhenPersistenceFailure() throws IOException {
                         PbjBlockStreamService.BlockStreamMethod.subscribeBlockStream,
                         optionsMock,
                         subscribeStreamObserver1)
-                .onNext(buildEmptySubscribeStreamRequest());
+                .onNext(buildLiveStreamSubscribeStreamRequest());
         pbjBlockStreamServiceProxy
                 .open(
                         PbjBlockStreamService.BlockStreamMethod.subscribeBlockStream,
                         optionsMock,
                         subscribeStreamObserver2)
-                .onNext(buildEmptySubscribeStreamRequest());
+                .onNext(buildLiveStreamSubscribeStreamRequest());
         pbjBlockStreamServiceProxy
                 .open(
                         PbjBlockStreamService.BlockStreamMethod.subscribeBlockStream,
                         optionsMock,
                         subscribeStreamObserver3)
-                .onNext(buildEmptySubscribeStreamRequest());
+                .onNext(buildLiveStreamSubscribeStreamRequest());
+
+        Thread.sleep(POLLER_SUBSCRIBER_SLEEP);
 
-        // 3 subscribers + 1 streamPersistenceHandler
-        assertEquals(5, consumers.size());
+        // 3 subscribers + 2 static handlers
+        assertEquals(5, pollConsumers.size() + consumers.size());
 
         // Transmit a BlockItem
         final Bytes publishStreamRequest =
@@ -700,7 +717,7 @@ void testMediatorExceptionHandlingWhenPersistenceFailure() throws IOException {
                         PbjBlockStreamService.BlockStreamMethod.subscribeBlockStream,
                         optionsMock,
                         subscribeStreamObserver4)
-                .onNext(buildEmptySubscribeStreamRequest());
+                .onNext(buildLiveStreamSubscribeStreamRequest());
 
         // The BlockItem expected to pass through since it was published
         // before the IOException was thrown.
@@ -783,7 +800,8 @@ private PbjBlockStreamServiceProxy buildBlockStreamService(
         final BlockRemover blockRemover = mock(BlockRemover.class);
         final ServiceStatus serviceStatus = new ServiceStatusImpl(serviceConfig);
         serviceStatus.setLatestAckedBlock(new BlockInfo(1L));
-        final LiveStreamMediator streamMediator = buildStreamMediator(new ConcurrentHashMap<>(32), serviceStatus);
+        final LiveStreamMediator streamMediator =
+                buildStreamMediator(new ConcurrentHashMap<>(32), new ConcurrentHashMap<>(32), serviceStatus);
         final Notifier notifier =
                 new NotifierImpl(streamMediator, metricsService, notifierConfig, mediatorConfig, serviceStatus);
         final AckHandler blockManager =
@@ -822,10 +840,12 @@ private LiveStreamMediator buildStreamMediator(
                             BlockNodeEventHandler<ObjectEvent<List<BlockItemUnparsed>>>,
                             BatchEventProcessor<ObjectEvent<List<BlockItemUnparsed>>>>
                     subscribers,
+            final Map<StreamManager, EventPoller<ObjectEvent<List<BlockItemUnparsed>>>> pollSubscribers,
             final ServiceStatus serviceStatus) {
         serviceStatus.setWebServer(webServerMock);
         return LiveStreamMediatorBuilder.newBuilder(metricsService, mediatorConfig, serviceStatus)
                 .subscribers(subscribers)
+                .pollSubscribers(pollSubscribers)
                 .build();
     }
 }
diff --git a/server/src/test/java/com/hedera/block/server/util/PbjProtoTestUtils.java b/server/src/test/java/com/hedera/block/server/util/PbjProtoTestUtils.java
index db83b9aeb5..bda0725117 100644
--- a/server/src/test/java/com/hedera/block/server/util/PbjProtoTestUtils.java
+++ b/server/src/test/java/com/hedera/block/server/util/PbjProtoTestUtils.java
@@ -29,8 +29,10 @@ public static Bytes buildEmptyPublishStreamRequest() {
                 .build());
     }
 
-    public static Bytes buildEmptySubscribeStreamRequest() {
-        return SubscribeStreamRequest.PROTOBUF.toBytes(
-                SubscribeStreamRequest.newBuilder().build());
+    public static Bytes buildLiveStreamSubscribeStreamRequest() {
+        return SubscribeStreamRequest.PROTOBUF.toBytes(SubscribeStreamRequest.newBuilder()
+                .startBlockNumber(0L)
+                .endBlockNumber(0L)
+                .build());
     }
 }


Reviews:
user: login: AlfredoG87
body: LGTM. 

Just one nit/question about the spotless format workaround.
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T04:38:13Z
user: login: jsync-swirlds
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T15:05:52Z
user: login: ata-nas
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T15:06:11Z
user: login: Nana-EC
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T20:44:34Z

Review comments:
diff_hunk: @@ -70,16 +75,25 @@ public static LiveStreamMediatorBuilder newBuilder(
      * @param subscribers is the map of subscribers to set
      * @return the builder
      */
+    // spotless:off
     @NonNull
     public LiveStreamMediatorBuilder subscribers(
-            @NonNull
-                    final Map<
-                                    BlockNodeEventHandler<ObjectEvent<List<BlockItemUnparsed>>>,
-                                    BatchEventProcessor<ObjectEvent<List<BlockItemUnparsed>>>>
-                            subscribers) {
+            @NonNull final Map<BlockNodeEventHandler<ObjectEvent<List<BlockItemUnparsed>>>,
+                               BatchEventProcessor<ObjectEvent<List<BlockItemUnparsed>>>> subscribers) {
         this.subscribers = subscribers;
         return this;
     }
+    // spotless:on
+
+    // spotless:off
path: server/src/main/java/com/hedera/block/server/mediator/LiveStreamMediatorBuilder.java
user: login: AlfredoG87
body: should we use this spotless hacks?

I recognize that spotless is not perfect and this might well be one case of this. 

```
    @NonNull
    public LiveStreamMediatorBuilder subscribers(
            @NonNull
                    final Map<
                                    BlockNodeEventHandler<ObjectEvent<List<BlockItemUnparsed>>>,
                                    BatchEventProcessor<ObjectEvent<List<BlockItemUnparsed>>>>
                            subscribers) {
        this.subscribers = subscribers;
        return this;
    }

```

given the spotless format is uglier but do we want to have this kind of comments all over the place and make a precedent on disabling and enabling spotless?

@jsync-swirlds @ata-nas Thoughts?
created_at: 2025-03-07T04:07:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -70,16 +75,25 @@ public static LiveStreamMediatorBuilder newBuilder(
      * @param subscribers is the map of subscribers to set
      * @return the builder
      */
+    // spotless:off
     @NonNull
     public LiveStreamMediatorBuilder subscribers(
-            @NonNull
-                    final Map<
-                                    BlockNodeEventHandler<ObjectEvent<List<BlockItemUnparsed>>>,
-                                    BatchEventProcessor<ObjectEvent<List<BlockItemUnparsed>>>>
-                            subscribers) {
+            @NonNull final Map<BlockNodeEventHandler<ObjectEvent<List<BlockItemUnparsed>>>,
+                               BatchEventProcessor<ObjectEvent<List<BlockItemUnparsed>>>> subscribers) {
         this.subscribers = subscribers;
         return this;
     }
+    // spotless:on
+
+    // spotless:off
path: server/src/main/java/com/hedera/block/server/mediator/LiveStreamMediatorBuilder.java
user: login: jsync-swirlds
body: We do this in other places (relatively frequently in pbj).
Spotless is useful to have consistency, but on occasion the results are just unreadble.

Rather than go to exceptional lengths to accommodate spotless a _rare_ exception is fine.
created_at: 2025-03-07T15:05:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -70,16 +75,25 @@ public static LiveStreamMediatorBuilder newBuilder(
      * @param subscribers is the map of subscribers to set
      * @return the builder
      */
+    // spotless:off
     @NonNull
     public LiveStreamMediatorBuilder subscribers(
-            @NonNull
-                    final Map<
-                                    BlockNodeEventHandler<ObjectEvent<List<BlockItemUnparsed>>>,
-                                    BatchEventProcessor<ObjectEvent<List<BlockItemUnparsed>>>>
-                            subscribers) {
+            @NonNull final Map<BlockNodeEventHandler<ObjectEvent<List<BlockItemUnparsed>>>,
+                               BatchEventProcessor<ObjectEvent<List<BlockItemUnparsed>>>> subscribers) {
         this.subscribers = subscribers;
         return this;
     }
+    // spotless:on
+
+    // spotless:off
path: server/src/main/java/com/hedera/block/server/mediator/LiveStreamMediatorBuilder.java
user: login: ata-nas
body: I am pro for using the spotless off exceptionally when spotless would really render our code completely unreadable. 
created_at: 2025-03-07T15:06:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -70,16 +75,25 @@ public static LiveStreamMediatorBuilder newBuilder(
      * @param subscribers is the map of subscribers to set
      * @return the builder
      */
+    // spotless:off
     @NonNull
     public LiveStreamMediatorBuilder subscribers(
-            @NonNull
-                    final Map<
-                                    BlockNodeEventHandler<ObjectEvent<List<BlockItemUnparsed>>>,
-                                    BatchEventProcessor<ObjectEvent<List<BlockItemUnparsed>>>>
-                            subscribers) {
+            @NonNull final Map<BlockNodeEventHandler<ObjectEvent<List<BlockItemUnparsed>>>,
+                               BatchEventProcessor<ObjectEvent<List<BlockItemUnparsed>>>> subscribers) {
         this.subscribers = subscribers;
         return this;
     }
+    // spotless:on
+
+    // spotless:off
path: server/src/main/java/com/hedera/block/server/mediator/LiveStreamMediatorBuilder.java
user: login: Nana-EC
body: Sounds like the team is good 
created_at: 2025-03-07T20:44:34Z
author_association: CONTRIBUTOR

PR comments:
user: login: codecov[bot]
created_at: 2025-03-07T20:45:09Z
body: ## [Codecov](https://app.codecov.io/gh/hiero-ledger/hiero-block-node/pull/797?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiero-ledger) Report
All modified and coverable lines are covered by tests :white_check_mark:

```diff
@@             Coverage Diff              @@
##               main     #797      +/-   ##
============================================
+ Coverage     84.30%   89.23%   +4.92%     
- Complexity      632      678      +46     
============================================
  Files           129      129              
  Lines          2919     2926       +7     
  Branches        206      206              
============================================
+ Hits           2461     2611     +150     
+ Misses          390      254     -136     
+ Partials         68       61       -7     
```

| [Files with missing lines](https://app.codecov.io/gh/hiero-ledger/hiero-block-node/pull/797?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiero-ledger) | Coverage Δ | Complexity Δ | |
|---|---|---|---|
| [.../block/server/consumer/OpenRangeStreamManager.java](https://app.codecov.io/gh/hiero-ledger/hiero-block-node/pull/797?src=pr&el=tree&filepath=server%2Fsrc%2Fmain%2Fjava%2Fcom%2Fhedera%2Fblock%2Fserver%2Fconsumer%2FOpenRangeStreamManager.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiero-ledger#diff-c2VydmVyL3NyYy9tYWluL2phdmEvY29tL2hlZGVyYS9ibG9jay9zZXJ2ZXIvY29uc3VtZXIvT3BlblJhbmdlU3RyZWFtTWFuYWdlci5qYXZh) | `86.92% <100.00%> (+1.65%)` | `6.00 <0.00> (ø)` | |
| [...ock/server/mediator/LiveStreamMediatorBuilder.java](https://app.codecov.io/gh/hiero-ledger/hiero-block-node/pull/797?src=pr&el=tree&filepath=server%2Fsrc%2Fmain%2Fjava%2Fcom%2Fhedera%2Fblock%2Fserver%2Fmediator%2FLiveStreamMediatorBuilder.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiero-ledger#diff-c2VydmVyL3NyYy9tYWluL2phdmEvY29tL2hlZGVyYS9ibG9jay9zZXJ2ZXIvbWVkaWF0b3IvTGl2ZVN0cmVhbU1lZGlhdG9yQnVpbGRlci5qYXZh) | `100.00% <100.00%> (+20.00%)` | `5.00 <2.00> (+2.00)` | |
| [.../block/server/mediator/LiveStreamMediatorImpl.java](https://app.codecov.io/gh/hiero-ledger/hiero-block-node/pull/797?src=pr&el=tree&filepath=server%2Fsrc%2Fmain%2Fjava%2Fcom%2Fhedera%2Fblock%2Fserver%2Fmediator%2FLiveStreamMediatorImpl.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiero-ledger#diff-c2VydmVyL3NyYy9tYWluL2phdmEvY29tL2hlZGVyYS9ibG9jay9zZXJ2ZXIvbWVkaWF0b3IvTGl2ZVN0cmVhbU1lZGlhdG9ySW1wbC5qYXZh) | `90.47% <100.00%> (+27.31%)` | `4.00 <0.00> (+1.00)` | |
| [...block/server/mediator/SubscriptionHandlerBase.java](https://app.codecov.io/gh/hiero-ledger/hiero-block-node/pull/797?src=pr&el=tree&filepath=server%2Fsrc%2Fmain%2Fjava%2Fcom%2Fhedera%2Fblock%2Fserver%2Fmediator%2FSubscriptionHandlerBase.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiero-ledger#diff-c2VydmVyL3NyYy9tYWluL2phdmEvY29tL2hlZGVyYS9ibG9jay9zZXJ2ZXIvbWVkaWF0b3IvU3Vic2NyaXB0aW9uSGFuZGxlckJhc2UuamF2YQ==) | `97.77% <100.00%> (+17.77%)` | `11.00 <0.00> (+3.00)` | |
| [...com/hedera/block/server/notifier/NotifierImpl.java](https://app.codecov.io/gh/hiero-ledger/hiero-block-node/pull/797?src=pr&el=tree&filepath=server%2Fsrc%2Fmain%2Fjava%2Fcom%2Fhedera%2Fblock%2Fserver%2Fnotifier%2FNotifierImpl.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiero-ledger#diff-c2VydmVyL3NyYy9tYWluL2phdmEvY29tL2hlZGVyYS9ibG9jay9zZXJ2ZXIvbm90aWZpZXIvTm90aWZpZXJJbXBsLmphdmE=) | `97.82% <100.00%> (+53.38%)` | `10.00 <0.00> (+6.00)` | |

... and [10 files with indirect coverage changes](https://app.codecov.io/gh/hiero-ledger/hiero-block-node/pull/797/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiero-ledger)

<details><summary>🚀 New features to boost your workflow: </summary>

- ❄ [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE