Skip to content

Commit

Permalink
GH-245: Expose customizers for KCL configs
Browse files Browse the repository at this point in the history
Fixes: #245
Issue link: #245

* Add something like `setLeaseManagementConfigCustomizer(Consumer<LeaseManagementConfig> leaseManagementConfigCustomizer)`
to the `KclMessageDrivenChannelAdapter` and call them before creating a `Scheduler`
* Also add a simple `emptyRecordList` property for the `ProcessorConfig`
  • Loading branch information
artembilan committed Sep 24, 2024
1 parent 577c2e8 commit c113391
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 19 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ For example, users may want to fully read any parent shards before starting to r
return openShards.stream()
.filter(shard -> !openShardIds.contains(shard.getParentShardId())
&& !openShardIds.contains(shard.getAdjacentParentShardId()))
.collect(Collectors.toList());
.toList();
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.function.Consumer;

import javax.annotation.Nullable;

Expand All @@ -40,10 +40,12 @@
import software.amazon.kinesis.common.InitialPositionInStreamExtended;
import software.amazon.kinesis.common.StreamConfig;
import software.amazon.kinesis.common.StreamIdentifier;
import software.amazon.kinesis.coordinator.CoordinatorConfig;
import software.amazon.kinesis.coordinator.Scheduler;
import software.amazon.kinesis.exceptions.InvalidStateException;
import software.amazon.kinesis.exceptions.ShutdownException;
import software.amazon.kinesis.exceptions.ThrottlingException;
import software.amazon.kinesis.leases.LeaseManagementConfig;
import software.amazon.kinesis.lifecycle.LifecycleConfig;
import software.amazon.kinesis.lifecycle.events.InitializationInput;
import software.amazon.kinesis.lifecycle.events.LeaseLostInput;
Expand All @@ -55,6 +57,7 @@
import software.amazon.kinesis.metrics.NullMetricsFactory;
import software.amazon.kinesis.processor.FormerStreamsLeasesDeletionStrategy;
import software.amazon.kinesis.processor.MultiStreamTracker;
import software.amazon.kinesis.processor.ProcessorConfig;
import software.amazon.kinesis.processor.RecordProcessorCheckpointer;
import software.amazon.kinesis.processor.ShardRecordProcessor;
import software.amazon.kinesis.processor.ShardRecordProcessorFactory;
Expand Down Expand Up @@ -151,6 +154,20 @@ public class KclMessageDrivenChannelAdapter extends MessageProducerSupport

private MetricsLevel metricsLevel = MetricsLevel.DETAILED;

private Consumer<CoordinatorConfig> coordinatorConfigCustomizer = (config) -> {
};

private Consumer<LifecycleConfig> lifecycleConfigCustomizer = (config) -> {
};

private Consumer<MetricsConfig> metricsConfigCustomizer = (config) -> {
};

private Consumer<LeaseManagementConfig> leaseManagementConfigCustomizer = (config) -> {
};

private boolean emptyRecordList;

public KclMessageDrivenChannelAdapter(String... streams) {
this(KinesisAsyncClient.create(), CloudWatchAsyncClient.create(), DynamoDbAsyncClient.create(), streams);
}
Expand Down Expand Up @@ -283,9 +300,70 @@ public void setMetricsLevel(MetricsLevel metricsLevel) {
this.metricsLevel = metricsLevel;
}

/**
* Set a {@link Consumer} to configure a {@link CoordinatorConfig}.
* @param coordinatorConfigCustomizer the {@link Consumer} to configure a {@link CoordinatorConfig}.
* @since 3.0.8
* @see CoordinatorConfig
*/
public void setCoordinatorConfigCustomizer(Consumer<CoordinatorConfig> coordinatorConfigCustomizer) {
Assert.notNull(coordinatorConfigCustomizer, "'coordinatorConfigCustomizer' must not be null");
this.coordinatorConfigCustomizer = coordinatorConfigCustomizer;
}

/**
* Set a {@link Consumer} to configure a {@link LifecycleConfig}.
* @param lifecycleConfigCustomizer the {@link Consumer} to configure a {@link LifecycleConfig}.
* @since 3.0.8
* @see LifecycleConfig
*/
public void setLifecycleConfigCustomizer(Consumer<LifecycleConfig> lifecycleConfigCustomizer) {
Assert.notNull(lifecycleConfigCustomizer, "'lifecycleConfigCustomizer' must not be null");
this.lifecycleConfigCustomizer = lifecycleConfigCustomizer;
}

/**
* Set a {@link Consumer} to configure a {@link MetricsConfig}.
* May override whatever could be set individually, like {@link #setMetricsLevel(MetricsLevel)}.
* @param metricsConfigCustomizer the {@link Consumer} to configure a {@link MetricsConfig}.
* @since 3.0.8
* @see MetricsConfig
*/
public void setMetricsConfigCustomizer(Consumer<MetricsConfig> metricsConfigCustomizer) {
Assert.notNull(metricsConfigCustomizer, "'metricsConfigCustomizer' must not be null");
this.metricsConfigCustomizer = metricsConfigCustomizer;
}

/**
* Set a {@link Consumer} to configure a {@link LeaseManagementConfig}.
* @param leaseManagementConfigCustomizer the {@link Consumer} to configure a {@link LeaseManagementConfig}.
* @since 3.0.8
* @see LeaseManagementConfig
*/
public void setLeaseManagementConfigCustomizer(Consumer<LeaseManagementConfig> leaseManagementConfigCustomizer) {
Assert.notNull(leaseManagementConfigCustomizer, "'leaseManagementConfigCustomizer' must not be null");
this.leaseManagementConfigCustomizer = leaseManagementConfigCustomizer;
}

/**
* Whether to return an empty record list from consumer to the processor.
* Works only in {@link ListenerMode#batch} mode.
* The message will be sent into the output channel with an empty {@link List} as a payload.
* @param emptyRecordList true to return an empty record list.
* @since 3.0.8
* @see ProcessorConfig#callProcessRecordsEvenForEmptyRecordList(boolean)
*/
public void setEmptyRecordList(boolean emptyRecordList) {
this.emptyRecordList = emptyRecordList;
}

@Override
protected void onInit() {
super.onInit();
if (this.listenerMode.equals(ListenerMode.record) && this.emptyRecordList) {
this.emptyRecordList = false;
logger.warn("The 'emptyRecordList' is processed only in the [ListenerMode.batch].");
}
this.config =
new ConfigsBuilder(buildStreamTracker(),
this.consumerGroup,
Expand Down Expand Up @@ -316,8 +394,9 @@ protected void doStart() {
+ "because it does not make sense in case of [ListenerMode.batch].");
}

LifecycleConfig lifecycleConfig = this.config.lifecycleConfig();
LifecycleConfig lifecycleConfig = this.config.lifecycleConfig();
lifecycleConfig.taskBackoffTimeMillis(this.consumerBackoff);
this.lifecycleConfigCustomizer.accept(lifecycleConfig);

RetrievalSpecificConfig retrievalSpecificConfig;
String singleStreamName = this.streams.length == 1 ? this.streams[0] : null;
Expand All @@ -342,15 +421,25 @@ protected void doStart() {
if (MetricsLevel.NONE.equals(this.metricsLevel)) {
metricsConfig.metricsFactory(new NullMetricsFactory());
}
this.metricsConfigCustomizer.accept(metricsConfig);

CoordinatorConfig coordinatorConfig = this.config.coordinatorConfig();
this.coordinatorConfigCustomizer.accept(coordinatorConfig);

LeaseManagementConfig leaseManagementConfig = this.config.leaseManagementConfig();
this.leaseManagementConfigCustomizer.accept(leaseManagementConfig);

ProcessorConfig processorConfig = this.config.processorConfig()
.callProcessRecordsEvenForEmptyRecordList(this.emptyRecordList);

this.scheduler =
new Scheduler(
this.config.checkpointConfig(),
this.config.coordinatorConfig(),
this.config.leaseManagementConfig(),
coordinatorConfig,
leaseManagementConfig,
lifecycleConfig,
metricsConfig,
this.config.processorConfig(),
processorConfig,
retrievalConfig);

This comment has been minimized.

Copy link
@amitchidrewar1301

amitchidrewar1301 Sep 24, 2024

@artembilan

  • Can you also please add support for retrievalConfig and other configs as well?
  • Also, is there any way where we can override the lease table name

This comment has been minimized.

Copy link
@artembilan

artembilan Sep 24, 2024

Author Member

The CheckpointConfig is only about checkpointFactory. Which really cannot be anything else but DynamoDBCheckpointFactory. Since DynamoDbAsyncClient is mandatory for the Scheduler.

The RetrievalConfig is very specific for the KclMessageDrivenChannelAdapter logic.

If you would like to have a full control, then you might not need Spring Integration AWS at all, but use KCL directly.

The tableName is resolved like:

    public String tableName() {
        if (StringUtils.isEmpty(tableName)) {
            tableName = applicationName();
        }
        return tableName;
    }

Where it is set as a consumerGroup:

		this.config =
				new ConfigsBuilder(buildStreamTracker(),
						this.consumerGroup,
						this.kinesisClient,
						this.dynamoDBClient,
						this.cloudWatchClient,
						this.workerId,
						this.recordProcessorFactory);

This comment has been minimized.

Copy link
@amitchidrewar1301

amitchidrewar1301 Sep 24, 2024

@artembilan Thanks for explaining. I understand that by default we have applicationName as the table name but if the lease table already exists then it is good if we have a provision to refer to that, any thoughts?

This comment has been minimized.

Copy link
@artembilan

artembilan Sep 24, 2024

Author Member

I think you are right.
The leaseTableName could be exposed.
Please, raise a new GH issue on the matter.

This comment has been minimized.

Copy link
@artembilan

artembilan Sep 24, 2024

Author Member

OK. I decided to go ahead and exposed that property for you: f5704cd

This comment has been minimized.

Copy link
@amitchidrewar1301

amitchidrewar1301 Sep 25, 2024

Thanks @artembilan , much appreciated

This comment has been minimized.

Copy link
@artembilan

artembilan Sep 25, 2024

Author Member

So, let me know how that works for you when you use snapshots, and I'll go ahead and release both libraries!

This comment has been minimized.

Copy link
@amitchidrewar1301

amitchidrewar1301 Sep 25, 2024

I was in the middle of testing the changes, just one thing to mention here.
Though we will educate the teams to use fan-out mode for those who are using polling, we saw the different polling configurations present in the lower environments, moreover, if they have multiple instances maxRecordsPerPoll helps them to create a balance.

Do you have any thoughts on exposing that config for backward compatibility?

This comment has been minimized.

Copy link
@artembilan

artembilan Sep 25, 2024

Author Member

Yes, we can do that.
Something like pollingMaxRecords option on the channel adapter.
I just don't want to expose the whole PollingConfig for customization: might break something really specific for the channel adapter.

This comment has been minimized.

Copy link
@amitchidrewar1301

amitchidrewar1301 Sep 25, 2024

Thats great. yeah, I can understand, also have a look if you can provide support for IdleTimeBetweenReadsInMillis.

This comment has been minimized.

Copy link
@artembilan

artembilan Sep 25, 2024

Author Member

Yes. That is too - pollingIdleTime would do the trick.

This comment has been minimized.

Copy link
@amitchidrewar1301

amitchidrewar1301 Sep 25, 2024

I have tested the other configurers and no issues observed

This comment has been minimized.

Copy link
@artembilan

artembilan Sep 26, 2024

Author Member

OK. So, those are also in.
Let me know when it is good to release!

This comment has been minimized.

Copy link
@amitchidrewar1301

amitchidrewar1301 Sep 27, 2024

@artembilan Actually, Since morning facing some issues while testing these changes, though we are middle of checking whether it is an issue in our config, we still just wanted to give you a heads-up

So the below tests run fine but even after the successful test method execution, the scheduler continues to run in the background, it tries to initialize the lease coordinator, and it attempts to do that 20 times after which the test eventually completes.

The issue is that even if the method doStop() tries to shut down the scheduler. Instead of the scheduler stopping leaseCordinator, it continues to run in the background.

Logs:

2024-09-27T13:24:31.394+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.amazon.kinesis.coordinator.Scheduler   : Initializing LeaseCoordinator attempt 1
2024-09-27T13:24:31.406+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [           main] [                                                 ] c.g.m.a.kinesis.KplKclConfigurationTest  : Started KplKclConfigurationTest in 2.406 seconds (process running for 11.555)
2024-09-27T13:24:31.414+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.amazon.kinesis.coordinator.Scheduler   : Lease table is still empty. Checking again in 3000 ms
2024-09-27T13:24:34.374+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.amazon.kinesis.coordinator.Scheduler   : Lease table is still empty. Checking again in 3000 ms
2024-09-27T13:24:34.434+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.amazon.kinesis.coordinator.Scheduler   : Lease table is still empty. Checking again in 3000 ms
2024-09-27T13:24:37.400+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.amazon.kinesis.coordinator.Scheduler   : Lease table is still empty. Checking again in 3000 ms
2024-09-27T13:24:37.437+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.amazon.kinesis.coordinator.Scheduler   : Worker 6a611d55-65c2-4a61-a0e2-49511efbfaef is initiating the lease sync.
2024-09-27T13:24:37.438+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.a.k.c.PeriodicShardSyncManager         : Syncing Kinesis shard info for StreamConfig(streamIdentifier=msc.event.input, initialPositionInStreamExtended=InitialPositionInStreamExtended(position=TRIM_HORIZON, timestamp=null), consumerArn=null)
2024-09-27T13:24:37.467+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.a.kinesis.leases.KinesisShardDetector  : Stream msc.event.input: listing shards with list shards request ListShardsRequest(StreamName=msc.event.input, ShardFilter=ShardFilter(Type=AT_TRIM_HORIZON))
2024-09-27T13:24:37.498+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.a.k.leases.HierarchicalShardSyncer     : msc.event.input - Number of new leases to create: 4
2024-09-27T13:24:37.810+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.a.k.l.dynamodb.DynamoDBLeaseRefresher  : Created lease: Lease(leaseKey=shardId-000000000000, leaseOwner=null, leaseCounter=0, concurrencyToken=null, lastCounterIncrementNanos=null, checkpoint={SequenceNumber: TRIM_HORIZON,SubsequenceNumber: 0}, pendingCheckpoint=null, pendingCheckpointState=null, isMarkedForLeaseSteal=false, ownerSwitchesSinceCheckpoint=0, parentShardIds=[], childShardIds=[], hashKeyRangeForLease=HashKeyRangeForLease(startingHashKey=0, endingHashKey=85070591730234615865843651857942052862))
2024-09-27T13:24:37.822+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.a.k.l.dynamodb.DynamoDBLeaseRefresher  : Created lease: Lease(leaseKey=shardId-000000000001, leaseOwner=null, leaseCounter=0, concurrencyToken=null, lastCounterIncrementNanos=null, checkpoint={SequenceNumber: TRIM_HORIZON,SubsequenceNumber: 0}, pendingCheckpoint=null, pendingCheckpointState=null, isMarkedForLeaseSteal=false, ownerSwitchesSinceCheckpoint=0, parentShardIds=[], childShardIds=[], hashKeyRangeForLease=HashKeyRangeForLease(startingHashKey=85070591730234615865843651857942052863, endingHashKey=170141183460469231731687303715884105725))
2024-09-27T13:24:37.831+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.a.k.l.dynamodb.DynamoDBLeaseRefresher  : Created lease: Lease(leaseKey=shardId-000000000002, leaseOwner=null, leaseCounter=0, concurrencyToken=null, lastCounterIncrementNanos=null, checkpoint={SequenceNumber: TRIM_HORIZON,SubsequenceNumber: 0}, pendingCheckpoint=null, pendingCheckpointState=null, isMarkedForLeaseSteal=false, ownerSwitchesSinceCheckpoint=0, parentShardIds=[], childShardIds=[], hashKeyRangeForLease=HashKeyRangeForLease(startingHashKey=170141183460469231731687303715884105726, endingHashKey=255211775190703847597530955573826158588))
2024-09-27T13:24:37.837+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.a.k.l.dynamodb.DynamoDBLeaseRefresher  : Created lease: Lease(leaseKey=shardId-000000000003, leaseOwner=null, leaseCounter=0, concurrencyToken=null, lastCounterIncrementNanos=null, checkpoint={SequenceNumber: TRIM_HORIZON,SubsequenceNumber: 0}, pendingCheckpoint=null, pendingCheckpointState=null, isMarkedForLeaseSteal=false, ownerSwitchesSinceCheckpoint=0, parentShardIds=[], childShardIds=[], hashKeyRangeForLease=HashKeyRangeForLease(startingHashKey=255211775190703847597530955573826158589, endingHashKey=340282366920938463463374607431768211455))
2024-09-27T13:24:37.837+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.a.k.leases.HierarchicalShardSyncer     : msc.event.input - Newly created leases 4: [Lease(leaseKey=shardId-000000000002, leaseOwner=null, leaseCounter=0, concurrencyToken=null, lastCounterIncrementNanos=null, checkpoint={SequenceNumber: TRIM_HORIZON,SubsequenceNumber: 0}, pendingCheckpoint=null, pendingCheckpointState=null, isMarkedForLeaseSteal=false, ownerSwitchesSinceCheckpoint=0, parentShardIds=[], childShardIds=[], hashKeyRangeForLease=HashKeyRangeForLease(startingHashKey=170141183460469231731687303715884105726, endingHashKey=255211775190703847597530955573826158588)), Lease(leaseKey=shardId-000000000001, leaseOwner=null, leaseCounter=0, concurrencyToken=null, lastCounterIncrementNanos=null, checkpoint={SequenceNumber: TRIM_HORIZON,SubsequenceNumber: 0}, pendingCheckpoint=null, pendingCheckpointState=null, isMarkedForLeaseSteal=false, ownerSwitchesSinceCheckpoint=0, parentShardIds=[], childShardIds=[], hashKeyRangeForLease=HashKeyRangeForLease(startingHashKey=85070591730234615865843651857942052863, endingHashKey=170141183460469231731687303715884105725)), Lease(leaseKey=shardId-000000000003, leaseOwner=null, leaseCounter=0, concurrencyToken=null, lastCounterIncrementNanos=null, checkpoint={SequenceNumber: TRIM_HORIZON,SubsequenceNumber: 0}, pendingCheckpoint=null, pendingCheckpointState=null, isMarkedForLeaseSteal=false, ownerSwitchesSinceCheckpoint=0, parentShardIds=[], childShardIds=[], hashKeyRangeForLease=HashKeyRangeForLease(startingHashKey=255211775190703847597530955573826158589, endingHashKey=340282366920938463463374607431768211455)), Lease(leaseKey=shardId-000000000000, leaseOwner=null, leaseCounter=0, concurrencyToken=null, lastCounterIncrementNanos=null, checkpoint={SequenceNumber: TRIM_HORIZON,SubsequenceNumber: 0}, pendingCheckpoint=null, pendingCheckpointState=null, isMarkedForLeaseSteal=false, ownerSwitchesSinceCheckpoint=0, parentShardIds=[], childShardIds=[], hashKeyRangeForLease=HashKeyRangeForLease(startingHashKey=0, endingHashKey=85070591730234615865843651857942052862))]
2024-09-27T13:24:40.424+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.amazon.kinesis.coordinator.Scheduler   : Lease table is still empty. Checking again in 3000 ms
2024-09-27T13:24:43.447+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.amazon.kinesis.coordinator.Scheduler   : Lease table is still empty. Checking again in 3000 ms
2024-09-27T13:24:46.474+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.amazon.kinesis.coordinator.Scheduler   : Lease table is still empty. Checking again in 3000 ms
2024-09-27T13:24:49.479+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.amazon.kinesis.coordinator.Scheduler   : Worker 870e8de1-711d-44e4-9054-b1edd078e69a is initiating the lease sync.
2024-09-27T13:24:49.480+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.a.k.c.PeriodicShardSyncManager         : Syncing Kinesis shard info for StreamConfig(streamIdentifier=msc.json.input, initialPositionInStreamExtended=InitialPositionInStreamExtended(position=TRIM_HORIZON, timestamp=null), consumerArn=null)
2024-09-27T13:24:49.499+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.a.kinesis.leases.KinesisShardDetector  : Stream msc.json.input: listing shards with list shards request ListShardsRequest(StreamName=msc.json.input, ShardFilter=ShardFilter(Type=AT_TRIM_HORIZON))
2024-09-27T13:24:49.595+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.a.k.leases.HierarchicalShardSyncer     : msc.json.input - Number of new leases to create: 4
2024-09-27T13:24:49.607+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.a.k.l.dynamodb.DynamoDBLeaseRefresher  : Created lease: Lease(leaseKey=shardId-000000000000, leaseOwner=null, leaseCounter=0, concurrencyToken=null, lastCounterIncrementNanos=null, checkpoint={SequenceNumber: TRIM_HORIZON,SubsequenceNumber: 0}, pendingCheckpoint=null, pendingCheckpointState=null, isMarkedForLeaseSteal=false, ownerSwitchesSinceCheckpoint=0, parentShardIds=[], childShardIds=[], hashKeyRangeForLease=HashKeyRangeForLease(startingHashKey=0, endingHashKey=85070591730234615865843651857942052862))
2024-09-27T13:24:49.616+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.a.k.l.dynamodb.DynamoDBLeaseRefresher  : Created lease: Lease(leaseKey=shardId-000000000001, leaseOwner=null, leaseCounter=0, concurrencyToken=null, lastCounterIncrementNanos=null, checkpoint={SequenceNumber: TRIM_HORIZON,SubsequenceNumber: 0}, pendingCheckpoint=null, pendingCheckpointState=null, isMarkedForLeaseSteal=false, ownerSwitchesSinceCheckpoint=0, parentShardIds=[], childShardIds=[], hashKeyRangeForLease=HashKeyRangeForLease(startingHashKey=85070591730234615865843651857942052863, endingHashKey=170141183460469231731687303715884105725))
2024-09-27T13:24:49.624+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.a.k.l.dynamodb.DynamoDBLeaseRefresher  : Created lease: Lease(leaseKey=shardId-000000000002, leaseOwner=null, leaseCounter=0, concurrencyToken=null, lastCounterIncrementNanos=null, checkpoint={SequenceNumber: TRIM_HORIZON,SubsequenceNumber: 0}, pendingCheckpoint=null, pendingCheckpointState=null, isMarkedForLeaseSteal=false, ownerSwitchesSinceCheckpoint=0, parentShardIds=[], childShardIds=[], hashKeyRangeForLease=HashKeyRangeForLease(startingHashKey=170141183460469231731687303715884105726, endingHashKey=255211775190703847597530955573826158588))
2024-09-27T13:24:49.631+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.a.k.l.dynamodb.DynamoDBLeaseRefresher  : Created lease: Lease(leaseKey=shardId-000000000003, leaseOwner=null, leaseCounter=0, concurrencyToken=null, lastCounterIncrementNanos=null, checkpoint={SequenceNumber: TRIM_HORIZON,SubsequenceNumber: 0}, pendingCheckpoint=null, pendingCheckpointState=null, isMarkedForLeaseSteal=false, ownerSwitchesSinceCheckpoint=0, parentShardIds=[], childShardIds=[], hashKeyRangeForLease=HashKeyRangeForLease(startingHashKey=255211775190703847597530955573826158589, endingHashKey=340282366920938463463374607431768211455))
2024-09-27T13:24:49.631+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.a.k.leases.HierarchicalShardSyncer     : msc.json.input - Newly created leases 4: [Lease(leaseKey=shardId-000000000002, leaseOwner=null, leaseCounter=0, concurrencyToken=null, lastCounterIncrementNanos=null, checkpoint={SequenceNumber: TRIM_HORIZON,SubsequenceNumber: 0}, pendingCheckpoint=null, pendingCheckpointState=null, isMarkedForLeaseSteal=false, ownerSwitchesSinceCheckpoint=0, parentShardIds=[], childShardIds=[], hashKeyRangeForLease=HashKeyRangeForLease(startingHashKey=170141183460469231731687303715884105726, endingHashKey=255211775190703847597530955573826158588)), Lease(leaseKey=shardId-000000000001, leaseOwner=null, leaseCounter=0, concurrencyToken=null, lastCounterIncrementNanos=null, checkpoint={SequenceNumber: TRIM_HORIZON,SubsequenceNumber: 0}, pendingCheckpoint=null, pendingCheckpointState=null, isMarkedForLeaseSteal=false, ownerSwitchesSinceCheckpoint=0, parentShardIds=[], childShardIds=[], hashKeyRangeForLease=HashKeyRangeForLease(startingHashKey=85070591730234615865843651857942052863, endingHashKey=170141183460469231731687303715884105725)), Lease(leaseKey=shardId-000000000003, leaseOwner=null, leaseCounter=0, concurrencyToken=null, lastCounterIncrementNanos=null, checkpoint={SequenceNumber: TRIM_HORIZON,SubsequenceNumber: 0}, pendingCheckpoint=null, pendingCheckpointState=null, isMarkedForLeaseSteal=false, ownerSwitchesSinceCheckpoint=0, parentShardIds=[], childShardIds=[], hashKeyRangeForLease=HashKeyRangeForLease(startingHashKey=255211775190703847597530955573826158589, endingHashKey=340282366920938463463374607431768211455)), Lease(leaseKey=shardId-000000000000, leaseOwner=null, leaseCounter=0, concurrencyToken=null, lastCounterIncrementNanos=null, checkpoint={SequenceNumber: TRIM_HORIZON,SubsequenceNumber: 0}, pendingCheckpoint=null, pendingCheckpointState=null, isMarkedForLeaseSteal=false, ownerSwitchesSinceCheckpoint=0, parentShardIds=[], childShardIds=[], hashKeyRangeForLease=HashKeyRangeForLease(startingHashKey=0, endingHashKey=85070591730234615865843651857942052862))]
2024-09-27T13:25:37.841+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.a.kinesis.leases.LeaseCleanupManager   : Starting lease cleanup thread.
2024-09-27T13:25:37.843+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.amazon.kinesis.coordinator.Scheduler   : Starting LeaseCoordinator
2024-09-27T13:25:37.843+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [ool-21-thread-1] [                                                 ] s.a.kinesis.leases.LeaseCleanupManager   : Number of pending leases to clean before the scan : 0
2024-09-27T13:25:40.016+01:00 ERROR [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.amazon.kinesis.coordinator.Scheduler   : Caught exception when initializing LeaseCoordinator

java.lang.RuntimeException: software.amazon.awssdk.core.exception.SdkClientException: Unable to execute HTTP request: Connection refused: localhost/127.0.0.1:58480
	at software.amazon.kinesis.retrieval.AWSExceptionManager.apply(AWSExceptionManager.java:67) ~[amazon-kinesis-client-2.5.8.jar:na]
	at software.amazon.kinesis.leases.dynamodb.DynamoDBLeaseRefresher.list(DynamoDBLeaseRefresher.java:433) ~[amazon-kinesis-client-2.5.8.jar:na]
	at software.amazon.kinesis.leases.dynamodb.DynamoDBLeaseRefresher.list(DynamoDBLeaseRefresher.java:369) ~[amazon-kinesis-client-2.5.8.jar:na]
	at software.amazon.kinesis.leases.dynamodb.DynamoDBLeaseRefresher.listLeases(DynamoDBLeaseRefresher.java:345) ~[amazon-kinesis-client-2.5.8.jar:na]
	at software.amazon.kinesis.leases.dynamodb.DynamoDBLeaseRenewer.initialize(DynamoDBLeaseRenewer.java:400) ~[amazon-kinesis-client-2.5.8.jar:na]
	at software.amazon.kinesis.leases.dynamodb.DynamoDBLeaseCoordinator.start(DynamoDBLeaseCoordinator.java:230) ~[amazon-kinesis-client-2.5.8.jar:na]
	at software.amazon.kinesis.coordinator.Scheduler.initialize(Scheduler.java:360) ~[amazon-kinesis-client-2.5.8.jar:na]
	at software.amazon.kinesis.coordinator.Scheduler.run(Scheduler.java:316) ~[amazon-kinesis-client-2.5.8.jar:na]
	at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]
Caused by: software.amazon.awssdk.core.exception.SdkClientException: Unable to execute HTTP request: Connection refused: localhost/127.0.0.1:58480
	at software.amazon.awssdk.core.exception.SdkClientException$BuilderImpl.build(SdkClientException.java:111) ~[sdk-core-2.25.70.jar:na]
	at software.amazon.awssdk.core.exception.SdkClientException.create(SdkClientException.java:47) ~[sdk-core-2.25.70.jar:na]
	at software.amazon.awssdk.core.internal.http.pipeline.stages.utils.RetryableStageHelper.setLastException(RetryableStageHelper.java:223) ~[sdk-core-2.25.70.jar:na]
	at software.amazon.awssdk.core.internal.http.pipeline.stages.utils.RetryableStageHelper.setLastException(RetryableStageHelper.java:218) ~[sdk-core-2.25.70.jar:na]
	at software.amazon.awssdk.core.internal.http.pipeline.stages.AsyncRetryableStage$RetryingExecutor.maybeRetryExecute(AsyncRetryableStage.java:182) ~[sdk-core-2.25.70.jar:na]
	at software.amazon.awssdk.core.internal.http.pipeline.stages.AsyncRetryableStage$RetryingExecutor.lambda$attemptExecute$1(AsyncRetryableStage.java:159) ~[sdk-core-2.25.70.jar:na]
	at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2194) ~[na:na]
	at software.amazon.awssdk.utils.CompletableFutureUtils.lambda$forwardExceptionTo$0(CompletableFutureUtils.java:79) ~[utils-2.25.70.jar:na]
	at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2194) ~[na:na]
	at software.amazon.awssdk.core.internal.http.pipeline.stages.MakeAsyncHttpRequestStage.lambda$execute$0(MakeAsyncHttpRequestStage.java:108) ~[sdk-core-2.25.70.jar:na]
	at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2194) ~[na:na]
	at software.amazon.awssdk.core.internal.http.pipeline.stages.MakeAsyncHttpRequestStage.completeResponseFuture(MakeAsyncHttpRequestStage.java:255) ~[sdk-core-2.25.70.jar:na]
	at software.amazon.awssdk.core.internal.http.pipeline.stages.MakeAsyncHttpRequestStage.lambda$executeHttpRequest$3(MakeAsyncHttpRequestStage.java:167) ~[sdk-core-2.25.70.jar:na]
	at java.base/java.util.concurrent.CompletableFuture.uniHandle(CompletableFuture.java:934) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture$UniHandle.tryFire(CompletableFuture.java:911) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture$Completion.run(CompletableFuture.java:482) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[na:na]
	... 1 common frames omitted
	Suppressed: software.amazon.awssdk.core.exception.SdkClientException: Request attempt 1 failure: Unable to execute HTTP request: Connection refused: localhost/127.0.0.1:58480
	Suppressed: software.amazon.awssdk.core.exception.SdkClientException: Request attempt 2 failure: Unable to execute HTTP request: Connection refused: localhost/127.0.0.1:58480
	Suppressed: software.amazon.awssdk.core.exception.SdkClientException: Request attempt 3 failure: Unable to execute HTTP request: Connection refused: localhost/127.0.0.1:58480
	Suppressed: software.amazon.awssdk.core.exception.SdkClientException: Request attempt 4 failure: Unable to execute HTTP request: Connection refused: localhost/127.0.0.1:58480
	Suppressed: software.amazon.awssdk.core.exception.SdkClientException: Request attempt 5 failure: Unable to execute HTTP request: Connection refused: localhost/127.0.0.1:58480
	Suppressed: software.amazon.awssdk.core.exception.SdkClientException: Request attempt 6 failure: Unable to execute HTTP request: Connection refused: localhost/127.0.0.1:58480
	Suppressed: software.amazon.awssdk.core.exception.SdkClientException: Request attempt 7 failure: Unable to execute HTTP request: Connection refused: localhost/127.0.0.1:58480
	Suppressed: software.amazon.awssdk.core.exception.SdkClientException: Request attempt 8 failure: Unable to execute HTTP request: Connection refused: localhost/127.0.0.1:58480
Caused by: io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: localhost/127.0.0.1:58480
Caused by: java.net.ConnectException: Connection refused
	at java.base/sun.nio.ch.Net.pollConnect(Native Method) ~[na:na]
	at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:682) ~[na:na]
	at java.base/sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:973) ~[na:na]
	at io.netty.channel.socket.nio.NioSocketChannel.doFinishConnect(NioSocketChannel.java:336) ~[netty-transport-4.1.113.Final.jar:4.1.113.Final]
	at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:339) ~[netty-transport-4.1.113.Final.jar:4.1.113.Final]
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:776) ~[netty-transport-4.1.113.Final.jar:4.1.113.Final]
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724) ~[netty-transport-4.1.113.Final.jar:4.1.113.Final]
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650) ~[netty-transport-4.1.113.Final.jar:4.1.113.Final]
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562) ~[netty-transport-4.1.113.Final.jar:4.1.113.Final]
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.113.Final.jar:4.1.113.Final]
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.113.Final.jar:4.1.113.Final]
	at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]

2024-09-27T13:25:41.026+01:00  INFO [msc-test,,,] 64106 --- [msc-test] [cTaskExecutor-1] [                                                 ] s.amazon.kinesis.coordinator.Scheduler   : Initializing LeaseCoordinator attempt 2

PFB the example test method

@Override
	protected void doStop() {
		super.doStop();
		this.scheduler.shutdown();
	}
@ExtendWith(SpringExtension.class)
@Testcontainers
@SpringBootTest(classes = Application.class)
class KplKclConfigurationTest {

  @Container
  private static final LocalStackContainer localStackContainer = new LocalStackContainer(
      DockerImageName.parse("nexus.giffgaff.co.uk/localstack:3")
          .asCompatibleSubstituteFor("localstack/localstack")
  )
      .withServices(LocalStackContainer.Service.KINESIS, LocalStackContainer.Service.DYNAMODB)
      .withExposedPorts(4566);

  @BeforeAll
  static void beforeAll() throws Exception {
    executeAwsCommand(format(CREATE_KINESIS_STREAM, "kpl-kcl-stream"));
    executeAwsCommand(
        format(CREATE_DYNAMODB_TABLE, KCL_STREAM_LEASES));
  }

  private static void executeAwsCommand(String awsCommand) throws IOException, InterruptedException {
    assertThat(
        localStackContainer.execInContainer(
            "/bin/sh",
            "-c",
            awsCommand
        ).getExitCode()
    ).isZero();
  }

  @DynamicPropertySource
  static void registerPgProperties(DynamicPropertyRegistry registry) {
    registry.add("localstack.port", () -> localStackContainer.getMappedPort(4566));
  }

  private static final String CREATE_KINESIS_STREAM = "awslocal kinesis create-stream --stream-name %s --shard-count 4 --region eu-west-1";
  private static final String CREATE_DYNAMODB_TABLE = "awslocal dynamodb create-table --table-name %s --region eu-west-1 --attribute-definitions AttributeName=leaseKey,AttributeType=S --key-schema AttributeName=leaseKey,KeyType=HASH --provisioned-throughput ReadCapacityUnits=10,WriteCapacityUnits=10";
  private static final String KCL_STREAM_LEASES = "test-lease-table";
  private static final String[] SHARD_LIST = {"shardId-000000000000", "shardId-000000000001",
      "shardId-000000000002", "shardId-000000000003"};

  @Resource
  private DynamoDbAsyncClient dynamoDB;


  @Test
  void verifyShards() {

    await()
        .pollInterval(ofSeconds(1))
        .atMost(ofSeconds(60))
        .untilAsserted(
            () -> {
              ScanResponse eventInputLease = dynamoDB.scan(
                  ScanRequest.builder().tableName(KCL_STREAM_LEASES).build()).join();
              assertThat(eventInputLease.items()
                  .stream().map(item -> item.get("leaseKey").s()))
                  .containsExactlyInAnyOrder(
                      SHARD_LIST
                  );
            });
  }
}

This comment has been minimized.

Copy link
@artembilan

artembilan Sep 27, 2024

Author Member

Sorry. This chat is not OK for such a lengthy message.
Please, consider to raise a new GH issue.

This comment has been minimized.

Copy link
@amitchidrewar1301

amitchidrewar1301 Sep 27, 2024

No worries, I have created new issue on amazon-kinesis-client board, as to be honest its something on the Scheduler side.

But please have a look at the issue and let us know if you have experienced something similar.


this.executor.execute(this.scheduler);
Expand Down Expand Up @@ -542,7 +631,7 @@ private void processMultipleRecords(List<KinesisClientRecord> records,
records.stream()
.map(this::prepareMessageForRecord)
.map(AbstractIntegrationMessageBuilder::build)
.collect(Collectors.toList());
.toList();

messageBuilder = getMessageBuilderFactory().withPayload(payload);
}
Expand All @@ -557,7 +646,7 @@ else if (KclMessageDrivenChannelAdapter.this.converter != null) {

return KclMessageDrivenChannelAdapter.this.converter.convert(r.data().array());
})
.collect(Collectors.toList());
.toList();

messageBuilder = getMessageBuilderFactory().withPayload(payload)
.setHeader(AwsHeaders.RECEIVED_PARTITION_KEY, partitionKeys)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ static void setup() {
.join();
}


@AfterAll
static void tearDown() {
AMAZON_KINESIS
Expand Down Expand Up @@ -126,24 +125,40 @@ void kclChannelAdapterReceivesRecords() {

@Test
public void metricsLevelOfMetricsConfigShouldBeSetToMetricsLevelOfAdapter() {
MetricsLevel metricsLevel = TestUtils.getPropertyValue(
this.kclMessageDrivenChannelAdapter,
"scheduler.metricsConfig.metricsLevel",
MetricsLevel.class
);
MetricsLevel metricsLevel =
TestUtils.getPropertyValue(this.kclMessageDrivenChannelAdapter,
"scheduler.metricsConfig.metricsLevel",
MetricsLevel.class);
assertThat(metricsLevel).isEqualTo(MetricsLevel.NONE);
}

@Test
public void metricsFactoryOfSchedulerShouldBeSetNullMetricsFactoryIfMetricsLevelIsNone() {
MetricsFactory metricsFactory = TestUtils.getPropertyValue(
this.kclMessageDrivenChannelAdapter,
"scheduler.metricsFactory",
MetricsFactory.class
);
MetricsFactory metricsFactory =
TestUtils.getPropertyValue(this.kclMessageDrivenChannelAdapter,
"scheduler.metricsFactory",
MetricsFactory.class);
assertThat(metricsFactory).isInstanceOf(NullMetricsFactory.class);
}

@Test
public void maxLeasesForWorkerOverriddenByCustomizer() {
Integer maxLeasesForWorker =
TestUtils.getPropertyValue(this.kclMessageDrivenChannelAdapter,
"scheduler.leaseCoordinator.leaseTaker.maxLeasesForWorker",
Integer.class);
assertThat(maxLeasesForWorker).isEqualTo(10);
}

@Test
public void shardConsumerDispatchPollIntervalMillisOverriddenByCustomizer() {
Long shardConsumerDispatchPollIntervalMillis =
TestUtils.getPropertyValue(this.kclMessageDrivenChannelAdapter,
"scheduler.shardConsumerDispatchPollIntervalMillis",
Long.class);
assertThat(shardConsumerDispatchPollIntervalMillis).isEqualTo(500L);
}

@Configuration
@EnableIntegration
public static class TestConfiguration {
Expand All @@ -159,7 +174,12 @@ public KclMessageDrivenChannelAdapter kclMessageDrivenChannelAdapter() {
adapter.setConsumerGroup("single_stream_group");
adapter.setFanOut(false);
adapter.setMetricsLevel(MetricsLevel.NONE);
adapter.setLeaseManagementConfigCustomizer(leaseManagementConfig ->
leaseManagementConfig.maxLeasesForWorker(10));
adapter.setCoordinatorConfigCustomizer(coordinatorConfig ->
coordinatorConfig.shardConsumerDispatchPollIntervalMillis(500L));
adapter.setBindSourceRecord(true);
adapter.setEmptyRecordList(true);
return adapter;
}

Expand Down

0 comments on commit c113391

Please sign in to comment.