Skip to content

Commit 3757c75

Browse files
committed
Optimization improvements over the PR Review process
Signed-off-by: Alfredo Gutierrez Grajeda <alfredo@hashgraph.com>
1 parent 123a494 commit 3757c75

File tree

5 files changed

+98
-106
lines changed

5 files changed

+98
-106
lines changed

block-node/backfill/src/main/java/org/hiero/block/node/backfill/BackfillConfiguration.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
* @param perBlockProcessingTimeout Timeout in milliseconds for processing each block, to avoid blocking the backfill
2323
* process indefinitely in case something unexpected happens, this would allow for self-recovery
2424
* @param grpcOverallTimeout single timeout configuration for gRPC Client construction, connectTimeout, readTimeout and pollWaitTime
25+
* @param enableTLS if enabled will assume block-node client supports tls connection.
2526
*/
2627
@ConfigData("backfill")
2728
public record BackfillConfiguration(
@@ -35,4 +36,5 @@ public record BackfillConfiguration(
3536
@Loggable @ConfigProperty(defaultValue = "1000") @Min(100) int delayBetweenBatches,
3637
@Loggable @ConfigProperty(defaultValue = "15000") @Min(5) int initialDelay,
3738
@Loggable @ConfigProperty(defaultValue = "1000") @Min(500) int perBlockProcessingTimeout,
38-
@Loggable @ConfigProperty(defaultValue = "30000") @Min(10000) int grpcOverallTimeout) {}
39+
@Loggable @ConfigProperty(defaultValue = "30000") @Min(10000) int grpcOverallTimeout,
40+
@Loggable @ConfigProperty(defaultValue = "false") boolean enableTLS) {}

block-node/backfill/src/main/java/org/hiero/block/node/backfill/BackfillGrpcClient.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ public class BackfillGrpcClient {
5252
* This is used for exponential backoff in case of failures.
5353
*/
5454
private final int initialRetryDelayMs;
55-
5655
/** Connection timeout in milliseconds for gRPC calls to block nodes. */
5756
private final int connectionTimeoutSeconds;
58-
57+
/** Enable TLS for secure connections to block nodes. */
58+
private final boolean enableTls;
5959
/** Current status of the Block Node Clients */
6060
private ConcurrentHashMap<BackfillSourceConfig, Status> nodeStatusMap = new ConcurrentHashMap<>();
6161
/**
@@ -74,13 +74,15 @@ public BackfillGrpcClient(
7474
int maxRetries,
7575
Counter backfillRetriesCounter,
7676
int retryInitialDelayMs,
77-
int connectionTimeoutSeconds)
77+
int connectionTimeoutSeconds,
78+
boolean enableTls)
7879
throws IOException, ParseException {
7980
this.blockNodeSource = BackfillSource.JSON.parse(Bytes.wrap(Files.readAllBytes(blockNodePreferenceFilePath)));
8081
this.maxRetries = maxRetries;
8182
this.initialRetryDelayMs = retryInitialDelayMs;
8283
this.backfillRetries = backfillRetriesCounter;
8384
this.connectionTimeoutSeconds = connectionTimeoutSeconds;
85+
this.enableTls = enableTls;
8486

8587
for (BackfillSourceConfig node : blockNodeSource.nodes()) {
8688
LOGGER.log(INFO, "Address: {0}, Port: {1}, Priority: {2}", node.address(), node.port(), node.priority());
@@ -207,7 +209,7 @@ public List<BlockUnparsed> fetchBlocks(LongRange blockRange) {
207209
*/
208210
private BlockNodeClient getNodeClient(BackfillSourceConfig node) {
209211
return nodeClientMap.computeIfAbsent(
210-
node, BlockNodeClient -> new BlockNodeClient(node, connectionTimeoutSeconds));
212+
node, BlockNodeClient -> new BlockNodeClient(node, connectionTimeoutSeconds, enableTls));
211213
}
212214

213215
/**

block-node/backfill/src/main/java/org/hiero/block/node/backfill/BackfillPlugin.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-License-Identifier: Apache-2.0
22
package org.hiero.block.node.backfill;
33

4+
import static java.lang.System.Logger.Level.DEBUG;
45
import static java.lang.System.Logger.Level.INFO;
56
import static java.lang.System.Logger.Level.TRACE;
67

@@ -181,7 +182,8 @@ public void init(BlockNodeContext context, ServiceBuilder serviceBuilder) {
181182
backfillConfiguration.maxRetries(),
182183
this.backfillRetries,
183184
backfillConfiguration.initialRetryDelay(),
184-
backfillConfiguration.grpcOverallTimeout());
185+
backfillConfiguration.grpcOverallTimeout(),
186+
backfillConfiguration.enableTLS());
185187
LOGGER.log(TRACE, "Initialized gRPC client with sources path: {0}", blockNodeSourcesPath);
186188
} catch (Exception e) {
187189
LOGGER.log(INFO, "Failed to initialize gRPC client: {0}", e.getMessage());
@@ -334,7 +336,7 @@ private void backfillGap(LongRange gap, BackfillType backfillType) throws Interr
334336
getLatch(backfillType).set(new CountDownLatch(batchOfBlocks.size()));
335337

336338
if (batchOfBlocks.isEmpty()) {
337-
LOGGER.log(TRACE, "No blocks fetched for gap {0}, skipping", chunk);
339+
LOGGER.log(DEBUG, "No blocks fetched for gap {0}, skipping", chunk);
338340
continue; // Skip empty batches
339341
}
340342

block-node/backfill/src/main/java/org/hiero/block/node/backfill/client/BlockNodeClient.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414

1515
public class BlockNodeClient {
1616
// Options definition for all gRPC services in the block node client
17-
private record Options(Optional<String> authority, String contentType) implements ServiceInterface.RequestOptions {}
17+
private static record Options(Optional<String> authority, String contentType)
18+
implements ServiceInterface.RequestOptions {}
1819

1920
private static final BlockNodeClient.Options OPTIONS =
2021
new BlockNodeClient.Options(Optional.empty(), ServiceInterface.RequestOptions.APPLICATION_GRPC);
@@ -28,11 +29,11 @@ private record Options(Optional<String> authority, String contentType) implement
2829
*
2930
* @param blockNodeConfig the configuration for the block node, including address and port
3031
*/
31-
public BlockNodeClient(BackfillSourceConfig blockNodeConfig, int timeoutMs) {
32+
public BlockNodeClient(BackfillSourceConfig blockNodeConfig, int timeoutMs, boolean enableTls) {
3233

3334
final Duration timeoutDuration = Duration.ofMillis(timeoutMs);
3435

35-
final Tls tls = Tls.builder().enabled(false).build();
36+
final Tls tls = Tls.builder().enabled(enableTls).build();
3637
final PbjGrpcClientConfig grpcConfig =
3738
new PbjGrpcClientConfig(timeoutDuration, tls, Optional.of(""), "application/grpc");
3839

Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
// SPDX-License-Identifier: Apache-2.0
22
package org.hiero.block.node.backfill.client;
33

4+
import static java.lang.System.Logger.Level.DEBUG;
45
import static java.lang.System.Logger.Level.TRACE;
56
import static java.util.Objects.requireNonNull;
67
import static org.hiero.block.api.BlockStreamSubscribeServiceInterface.FULL_NAME;
78

89
import com.hedera.hapi.block.stream.output.BlockHeader;
9-
import com.hedera.pbj.runtime.Codec;
1010
import com.hedera.pbj.runtime.ParseException;
11-
import com.hedera.pbj.runtime.UncheckedParseException;
1211
import com.hedera.pbj.runtime.grpc.GrpcCall;
1312
import com.hedera.pbj.runtime.grpc.GrpcClient;
1413
import com.hedera.pbj.runtime.grpc.Pipeline;
@@ -44,12 +43,11 @@ public class BlockStreamSubscribeUnparsedClient {
4443

4544
// From constructor
4645
private final GrpcClient grpcClient;
47-
private final ServiceInterface.RequestOptions requestOptions;
4846

4947
public BlockStreamSubscribeUnparsedClient(
5048
@NonNull final GrpcClient grpcClient, @NonNull final ServiceInterface.RequestOptions requestOptions) {
5149
this.grpcClient = requireNonNull(grpcClient);
52-
this.requestOptions = requireNonNull(requestOptions);
50+
ServiceInterface.RequestOptions requestOptions1 = requireNonNull(requestOptions);
5351
}
5452

5553
/**
@@ -77,72 +75,13 @@ public List<BlockUnparsed> getBatchOfBlocks(long startBlockNumber, long endBlock
7775
.build();
7876

7977
// Create a per-request pipeline that closes over `ctx`
80-
final Pipeline<SubscribeStreamResponseUnparsed> pipeline = new Pipeline<>() {
81-
@Override
82-
public void onSubscribe(Flow.Subscription subscription) {
83-
LOGGER.log(TRACE, "received onSubscribe confirmation");
84-
// No backpressure negotiation needed for this pattern.
85-
}
86-
87-
@Override
88-
public void onNext(SubscribeStreamResponseUnparsed subscribeStreamResponse) {
89-
if (subscribeStreamResponse.hasBlockItems()) {
90-
final List<BlockItemUnparsed> blockItems =
91-
subscribeStreamResponse.blockItems().blockItems();
92-
93-
// Start of a new block
94-
if (blockItems.getFirst().hasBlockHeader()) {
95-
final long expected = ctx.expectedBlockNumber;
96-
final long actual = extractBlockNumberFromBlockHeader(blockItems.getFirst());
97-
if (actual != expected) {
98-
ctx.fail(new IllegalStateException(
99-
"Expected block number " + expected + " but received " + actual));
100-
return;
101-
}
102-
// Begin a new block with the header and following items in this frame
103-
ctx.currentBlockItems = new ArrayList<>(blockItems);
104-
} else {
105-
// Continuation of the current block
106-
ctx.currentBlockItems.addAll(blockItems);
107-
}
108-
109-
// End of block
110-
if (blockItems.getLast().hasBlockProof()) {
111-
ctx.blocks.add(BlockUnparsed.newBuilder()
112-
.blockItems(ctx.currentBlockItems)
113-
.build());
114-
ctx.currentBlockItems = new ArrayList<>();
115-
ctx.expectedBlockNumber++;
116-
}
117-
118-
} else if (subscribeStreamResponse.hasStatus()) {
119-
final SubscribeStreamResponse.Code code = subscribeStreamResponse.status();
120-
if (code != SubscribeStreamResponse.Code.SUCCESS) {
121-
ctx.fail(new RuntimeException("Received error code: " + code));
122-
}
123-
} else {
124-
ctx.fail(new RuntimeException("Received unexpected response without block items or code"));
125-
}
126-
}
127-
128-
@Override
129-
public void onError(Throwable throwable) {
130-
LOGGER.log(TRACE, "received onError", throwable);
131-
ctx.fail(throwable);
132-
}
133-
134-
@Override
135-
public void onComplete() {
136-
LOGGER.log(TRACE, "received onComplete");
137-
ctx.complete();
138-
}
139-
};
78+
final Pipeline<SubscribeStreamResponseUnparsed> pipeline = new SubscribePipeline(ctx);
14079

14180
// Issue the call using the per-request pipeline
14281
final GrpcCall<SubscribeStreamRequest, SubscribeStreamResponseUnparsed> call = grpcClient.createCall(
14382
FULL_NAME + "/subscribeBlockStream",
144-
getSubscribeStreamRequestCodec(requestOptions),
145-
getSubscribeStreamResponseUnparsedCodec(requestOptions),
83+
SubscribeStreamRequest.PROTOBUF,
84+
SubscribeStreamResponseUnparsed.PROTOBUF,
14685
pipeline);
14786

14887
call.sendRequest(request, true);
@@ -154,35 +93,8 @@ public void onComplete() {
15493
/**
15594
* Extracts the block number from a block header item.
15695
*/
157-
private static long extractBlockNumberFromBlockHeader(BlockItemUnparsed itemUnparsed) {
158-
try {
159-
return BlockHeader.PROTOBUF.parse(itemUnparsed.blockHeaderOrThrow()).number();
160-
} catch (ParseException e) {
161-
throw new UncheckedParseException(e);
162-
}
163-
}
164-
165-
private static Codec<SubscribeStreamRequest> getSubscribeStreamRequestCodec(
166-
@NonNull final ServiceInterface.RequestOptions options) {
167-
requireNonNull(options);
168-
// Default to protobuf, and don't error out if both are set:
169-
if (options.isJson() && !options.isProtobuf()) {
170-
return SubscribeStreamRequest.JSON;
171-
} else {
172-
return SubscribeStreamRequest.PROTOBUF;
173-
}
174-
}
175-
176-
@NonNull
177-
private static Codec<SubscribeStreamResponseUnparsed> getSubscribeStreamResponseUnparsedCodec(
178-
@NonNull final ServiceInterface.RequestOptions options) {
179-
requireNonNull(options);
180-
// Default to protobuf, and don't error out if both are set:
181-
if (options.isJson() && !options.isProtobuf()) {
182-
return SubscribeStreamResponseUnparsed.JSON;
183-
} else {
184-
return SubscribeStreamResponseUnparsed.PROTOBUF;
185-
}
96+
private static long extractBlockNumberFromBlockHeader(BlockItemUnparsed itemUnparsed) throws ParseException {
97+
return BlockHeader.PROTOBUF.parse(itemUnparsed.blockHeaderOrThrow()).number();
18698
}
18799

188100
/**
@@ -214,12 +126,85 @@ List<BlockUnparsed> await() {
214126
done.await();
215127
} catch (InterruptedException e) {
216128
Thread.currentThread().interrupt();
217-
throw new RuntimeException("Interrupted while waiting for blocks", e);
218129
}
219130
if (error != null) {
220131
throw new RuntimeException("Error fetching blocks", error);
221132
}
222133
return blocks;
223134
}
224135
}
136+
137+
private static final class SubscribePipeline implements Pipeline<SubscribeStreamResponseUnparsed> {
138+
private final RequestContext ctx;
139+
140+
SubscribePipeline(RequestContext ctx) {
141+
this.ctx = ctx;
142+
}
143+
144+
@Override
145+
public void onSubscribe(Flow.Subscription subscription) {
146+
LOGGER.log(TRACE, "received onSubscribe confirmation");
147+
// No backpressure negotiation needed for this pattern.
148+
}
149+
150+
@Override
151+
public void onNext(SubscribeStreamResponseUnparsed resp) {
152+
try {
153+
if (resp.hasBlockItems()) {
154+
final List<BlockItemUnparsed> frame = resp.blockItems().blockItems();
155+
156+
if (frame.getFirst().hasBlockHeader()) {
157+
final long expected = ctx.expectedBlockNumber;
158+
final long actual = extractBlockNumberFromBlockHeader(frame.getFirst());
159+
if (actual != expected) {
160+
ctx.fail(new IllegalStateException(
161+
"Expected block number " + expected + " but received " + actual));
162+
return;
163+
}
164+
// Start a new block: reuse the buffer and populate it.
165+
ctx.currentBlockItems.clear();
166+
ctx.currentBlockItems.addAll(frame);
167+
} else {
168+
// Continuation: append to the same buffer.
169+
ctx.currentBlockItems.addAll(frame);
170+
}
171+
172+
if (frame.getLast().hasBlockProof()) {
173+
// Snapshot the current items to avoid retaining the large buffer in the finished block.
174+
final List<BlockItemUnparsed> snapshot = List.copyOf(ctx.currentBlockItems);
175+
ctx.blocks.add(
176+
BlockUnparsed.newBuilder().blockItems(snapshot).build());
177+
ctx.currentBlockItems.clear();
178+
ctx.expectedBlockNumber++;
179+
}
180+
181+
} else if (resp.hasStatus()) {
182+
final SubscribeStreamResponse.Code code = resp.status();
183+
if (code != SubscribeStreamResponse.Code.SUCCESS) {
184+
ctx.fail(new RuntimeException("Received error code: " + code));
185+
}
186+
} else {
187+
ctx.fail(new RuntimeException("Received unexpected response without block items or code"));
188+
}
189+
} catch (ParseException e) {
190+
LOGGER.log(DEBUG, "Parse error in block item", e);
191+
ctx.fail(e);
192+
} catch (RuntimeException e) {
193+
LOGGER.log(DEBUG, "Runtime error processing SubscribeStreamResponseUnparsed", e);
194+
ctx.fail(e);
195+
}
196+
}
197+
198+
@Override
199+
public void onError(Throwable throwable) {
200+
LOGGER.log(TRACE, "received onError", throwable);
201+
ctx.fail(throwable);
202+
}
203+
204+
@Override
205+
public void onComplete() {
206+
LOGGER.log(TRACE, "received onComplete");
207+
ctx.complete();
208+
}
209+
}
225210
}

0 commit comments

Comments
 (0)