Merge branch 'awslabs:v1.x' into v1.x
This commit is contained in:
commit
4077af47a7
58 changed files with 1845 additions and 528 deletions
32
.github/workflows/maven.yml
vendored
Normal file
32
.github/workflows/maven.yml
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
|
||||||
|
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven
|
||||||
|
|
||||||
|
# This workflow uses actions that are not certified by GitHub.
|
||||||
|
# They are provided by a third-party and are governed by
|
||||||
|
# separate terms of service, privacy policy, and support
|
||||||
|
# documentation.
|
||||||
|
|
||||||
|
name: Java CI with Maven
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'v1.x'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- 'v1.x'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Set up JDK 8
|
||||||
|
uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
java-version: '8'
|
||||||
|
distribution: 'corretto'
|
||||||
|
- name: Build with Maven
|
||||||
|
run: mvn -B package --file pom.xml -DskipITs
|
||||||
63
CHANGELOG.md
63
CHANGELOG.md
|
|
@ -1,6 +1,67 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
### Latest Release (1.15.1 - Feb 5, 2023)
|
||||||
|
* [#1214](https://github.com/awslabs/amazon-kinesis-client/pull/1214) Added backoff logic for ShardSyncTaskIntegrationTest
|
||||||
|
* [#1214](https://github.com/awslabs/amazon-kinesis-client/pull/1214) Upgrade Guava version from 31.0.1 to 32.1.1
|
||||||
|
* [#1252](https://github.com/awslabs/amazon-kinesis-client/pull/1252) Upgrade aws-java-sdk from 1.12.406 to 1.12.647
|
||||||
|
|
||||||
## Latest Release (1.14.0 - August 17, 2020)
|
### Release (1.15.0 - Jun 8, 2023)
|
||||||
|
* **[#1108](https://github.com/awslabs/amazon-kinesis-client/pull/1108) Add support for Stream ARNs**
|
||||||
|
* [#1111](https://github.com/awslabs/amazon-kinesis-client/pull/1111) More consistent testing behavior with HashRangesAreAlwaysComplete
|
||||||
|
* [#1054](https://github.com/awslabs/amazon-kinesis-client/pull/1054) Upgrade log4j-core from 2.17.1 to 2.20.0
|
||||||
|
* [#1103](https://github.com/awslabs/amazon-kinesis-client/pull/1103) Upgrade jackson-core from 2.13.0 to 2.15.0
|
||||||
|
* [#943](https://github.com/awslabs/amazon-kinesis-client/pull/943) Upgrade nexus-staging-maven-plugin from 1.6.8 to 1.6.13
|
||||||
|
* [#1044](https://github.com/awslabs/amazon-kinesis-client/pull/1044) Upgrade aws-java-sdk.version from 1.12.406 to 1.12.408
|
||||||
|
* [#1055](https://github.com/awslabs/amazon-kinesis-client/pull/1055) Upgrade maven-compiler-plugin from 3.10.0 to 3.11.0
|
||||||
|
|
||||||
|
### Release (1.14.10 - Feb 15, 2023)
|
||||||
|
* Updated aws-java-sdk from 1.12.130 to 1.12.406
|
||||||
|
* Updated com.google.protobuf from 3.19.4 to 3.19.6
|
||||||
|
* [Issue #1026](https://github.com/awslabs/amazon-kinesis-client/issues/1026)
|
||||||
|
* [PR #1042](https://github.com/awslabs/amazon-kinesis-client/pull/1042)
|
||||||
|
|
||||||
|
### Release (1.14.9 - Dec 14, 2022)
|
||||||
|
* [#995](https://github.com/awslabs/amazon-kinesis-client/commit/372f98b21a91487e36612d528c56765a44b0aa86) Every other change for DynamoDBStreamsKinesis Adapter Compatibility
|
||||||
|
* [#970](https://github.com/awslabs/amazon-kinesis-client/commit/251b331a2e0fd912b50f8b5a12d088bf0b3263b9) PeriodicShardSyncManager Changes Needed for DynamoDBStreamsKinesisAdapter
|
||||||
|
|
||||||
|
### Release (1.14.8 - Feb 24, 2022)
|
||||||
|
* [Bump log4j-core from 2.17.0 to 2.17.1](https://github.com/awslabs/amazon-kinesis-client/commit/94b138a9d9a502ee0f4f000bb0efd2766ebadc37)
|
||||||
|
* [Bump protobuf-java from 3.19.1 to 3.19.4](https://github.com/awslabs/amazon-kinesis-client/commit/a809b12c43c57a3d6ad3827feb60e4322614259c)
|
||||||
|
* [Bump maven-compiler-plugin from 3.8.1 to 3.10.0](https://github.com/awslabs/amazon-kinesis-client/commit/37b5d7b9a1ccad483469ef542a6a7237462b14f2)
|
||||||
|
|
||||||
|
### Release (1.14.7 - Dec 22, 2021)
|
||||||
|
* [#881](https://github.com/awslabs/amazon-kinesis-client/pull/881) Update log4j test dependency from 2.16.0 to 2.17.0 and some other dependencies
|
||||||
|
|
||||||
|
### Release (1.14.6 - Dec 15, 2021)
|
||||||
|
* [#876](https://github.com/awslabs/amazon-kinesis-client/pull/876) Update log4j test dependency from 2.15.0 to 2.16.0
|
||||||
|
|
||||||
|
### Release (1.14.5 - Dec 10, 2021)
|
||||||
|
* [#872](https://github.com/awslabs/amazon-kinesis-client/pull/872) Update log4j test dependency from 1.2.17 to 2.15.0
|
||||||
|
* [#873](https://github.com/awslabs/amazon-kinesis-client/pull/873) Upgrading version of AWS Java SDK to 1.12.128
|
||||||
|
|
||||||
|
### Release (1.14.4 - June 14, 2021)
|
||||||
|
* [Milestone#61](https://github.com/awslabs/amazon-kinesis-client/milestone/61)
|
||||||
|
* [#816](https://github.com/awslabs/amazon-kinesis-client/pull/816) Updated the Worker shutdown logic to make sure that the `LeaseCleanupManager` also terminates all the threads that it has started.
|
||||||
|
* [#821](https://github.com/awslabs/amazon-kinesis-client/pull/821) Upgrading version of AWS Java SDK to 1.12.3
|
||||||
|
|
||||||
|
### Release (1.14.3 - May 3, 2021)
|
||||||
|
* [Milestone#60](https://github.com/awslabs/amazon-kinesis-client/milestone/60)
|
||||||
|
* [#811](https://github.com/awslabs/amazon-kinesis-client/pull/811) Fixing a bug in `KinesisProxy` that can lead to undetermined behavior during partial failures.
|
||||||
|
* [#811](https://github.com/awslabs/amazon-kinesis-client/pull/811) Adding guardrails to handle duplicate shards from the service.
|
||||||
|
|
||||||
|
## Release (1.14.2 - February 24, 2021)
|
||||||
|
* [Milestone#57](https://github.com/awslabs/amazon-kinesis-client/milestone/57)
|
||||||
|
* [#790](https://github.com/awslabs/amazon-kinesis-client/pull/790) Fixing a bug that caused paginated `ListShards` calls with the `ShardFilter` parameter to fail when the lease table was being initialized.
|
||||||
|
|
||||||
|
## Release (1.14.1 - January 27, 2021)
|
||||||
|
* [Milestone#56](https://github.com/awslabs/amazon-kinesis-client/milestone/56)
|
||||||
|
|
||||||
|
* Fix for cross DDB table interference when multiple KCL applications are run in same JVM.
|
||||||
|
* Fix and guards to avoid potential checkpoint rewind during shard end, which may block children shard processing.
|
||||||
|
* Fix for thread cycle wastage on InitializeTask for deleted shard.
|
||||||
|
* Improved logging in LeaseCleanupManager that would indicate why certain shards are not cleaned up from the lease table.
|
||||||
|
|
||||||
|
|
||||||
|
## Release (1.14.0 - August 17, 2020)
|
||||||
|
|
||||||
* [Milestone#50](https://github.com/awslabs/amazon-kinesis-client/milestone/50)
|
* [Milestone#50](https://github.com/awslabs/amazon-kinesis-client/milestone/50)
|
||||||
|
|
||||||
|
|
|
||||||
66
README.md
66
README.md
|
|
@ -1,3 +1,6 @@
|
||||||
|
# Bugs in 1.14.0 version
|
||||||
|
We recommend customers to migrate to 1.14.1 to avoid [known bugs](https://github.com/awslabs/amazon-kinesis-client/issues/778) in 1.14.0 version
|
||||||
|
|
||||||
# Amazon Kinesis Client Library for Java
|
# Amazon Kinesis Client Library for Java
|
||||||
[](https://travis-ci.org/awslabs/amazon-kinesis-client) 
|
[](https://travis-ci.org/awslabs/amazon-kinesis-client) 
|
||||||
|
|
||||||
|
|
@ -31,7 +34,68 @@ To make it easier for developers to write record processors in other languages,
|
||||||
|
|
||||||
## Release Notes
|
## Release Notes
|
||||||
|
|
||||||
### Latest Release (1.14.0 - August 17, 2020)
|
### Latest Release (1.15.1 - Feb 5, 2023)
|
||||||
|
* [#1214](https://github.com/awslabs/amazon-kinesis-client/pull/1214) Added backoff logic for ShardSyncTaskIntegrationTest
|
||||||
|
* [#1214](https://github.com/awslabs/amazon-kinesis-client/pull/1214) Upgrade Guava version from 31.0.1 to 32.1.1
|
||||||
|
* [#1252](https://github.com/awslabs/amazon-kinesis-client/pull/1252) Upgrade aws-java-sdk from 1.12.406 to 1.12.647
|
||||||
|
|
||||||
|
### Release (1.15.0 - Jun 8, 2023)
|
||||||
|
* **[#1108](https://github.com/awslabs/amazon-kinesis-client/pull/1108) Add support for Stream ARNs**
|
||||||
|
* [#1111](https://github.com/awslabs/amazon-kinesis-client/pull/1111) More consistent testing behavior with HashRangesAreAlwaysComplete
|
||||||
|
* [#1054](https://github.com/awslabs/amazon-kinesis-client/pull/1054) Upgrade log4j-core from 2.17.1 to 2.20.0
|
||||||
|
* [#1103](https://github.com/awslabs/amazon-kinesis-client/pull/1103) Upgrade jackson-core from 2.13.0 to 2.15.0
|
||||||
|
* [#943](https://github.com/awslabs/amazon-kinesis-client/pull/943) Upgrade nexus-staging-maven-plugin from 1.6.8 to 1.6.13
|
||||||
|
* [#1044](https://github.com/awslabs/amazon-kinesis-client/pull/1044) Upgrade aws-java-sdk.version from 1.12.406 to 1.12.408
|
||||||
|
* [#1055](https://github.com/awslabs/amazon-kinesis-client/pull/1055) Upgrade maven-compiler-plugin from 3.10.0 to 3.11.0
|
||||||
|
|
||||||
|
### Release (1.14.10 - Feb 15, 2023)
|
||||||
|
* Updated aws-java-sdk from 1.12.130 to 1.12.406
|
||||||
|
* Updated com.google.protobuf from 3.19.4 to 3.19.6
|
||||||
|
* [Issue #1026](https://github.com/awslabs/amazon-kinesis-client/issues/1026)
|
||||||
|
* [PR #1042](https://github.com/awslabs/amazon-kinesis-client/pull/1042)
|
||||||
|
|
||||||
|
### Release (1.14.9 - Dec 14, 2022)
|
||||||
|
* [#995](https://github.com/awslabs/amazon-kinesis-client/commit/372f98b21a91487e36612d528c56765a44b0aa86) Every other change for DynamoDBStreamsKinesis Adapter Compatibility
|
||||||
|
* [#970](https://github.com/awslabs/amazon-kinesis-client/commit/251b331a2e0fd912b50f8b5a12d088bf0b3263b9) PeriodicShardSyncManager Changes Needed for DynamoDBStreamsKinesisAdapter
|
||||||
|
|
||||||
|
### Release (1.14.8 - Feb 24, 2022)
|
||||||
|
* [Bump log4j-core from 2.17.0 to 2.17.1](https://github.com/awslabs/amazon-kinesis-client/commit/94b138a9d9a502ee0f4f000bb0efd2766ebadc37)
|
||||||
|
* [Bump protobuf-java from 3.19.1 to 3.19.4](https://github.com/awslabs/amazon-kinesis-client/commit/a809b12c43c57a3d6ad3827feb60e4322614259c)
|
||||||
|
* [Bump maven-compiler-plugin from 3.8.1 to 3.10.0](https://github.com/awslabs/amazon-kinesis-client/commit/37b5d7b9a1ccad483469ef542a6a7237462b14f2)
|
||||||
|
|
||||||
|
### Release (1.14.7 - Dec 22, 2021)
|
||||||
|
* [#881](https://github.com/awslabs/amazon-kinesis-client/pull/881) Update log4j test dependency from 2.16.0 to 2.17.0 and some other dependencies
|
||||||
|
|
||||||
|
### Release (1.14.6 - Dec 15, 2021)
|
||||||
|
* [#876](https://github.com/awslabs/amazon-kinesis-client/pull/876) Update log4j test dependency from 2.15.0 to 2.16.0
|
||||||
|
|
||||||
|
### Release (1.14.5 - Dec 10, 2021)
|
||||||
|
* [#872](https://github.com/awslabs/amazon-kinesis-client/pull/872) Update log4j test dependency from 1.2.17 to 2.15.0
|
||||||
|
* [#873](https://github.com/awslabs/amazon-kinesis-client/pull/873) Upgrading version of AWS Java SDK to 1.12.128
|
||||||
|
|
||||||
|
### Release (1.14.4 - June 14, 2021)
|
||||||
|
* [Milestone#61](https://github.com/awslabs/amazon-kinesis-client/milestone/61)
|
||||||
|
* [#816](https://github.com/awslabs/amazon-kinesis-client/pull/816) Updated the Worker shutdown logic to make sure that the `LeaseCleanupManager` also terminates all the threads that it has started.
|
||||||
|
* [#821](https://github.com/awslabs/amazon-kinesis-client/pull/821) Upgrading version of AWS Java SDK to 1.12.3
|
||||||
|
|
||||||
|
### Release (1.14.3 - May 3, 2021)
|
||||||
|
* [Milestone#60](https://github.com/awslabs/amazon-kinesis-client/milestone/60)
|
||||||
|
* [#811](https://github.com/awslabs/amazon-kinesis-client/pull/811) Fixing a bug in `KinesisProxy` that can lead to undetermined behavior during partial failures.
|
||||||
|
* [#811](https://github.com/awslabs/amazon-kinesis-client/pull/811) Adding guardrails to handle duplicate shards from the service.
|
||||||
|
|
||||||
|
## Release (1.14.2 - February 24, 2021)
|
||||||
|
* [Milestone#57](https://github.com/awslabs/amazon-kinesis-client/milestone/57)
|
||||||
|
* [#790](https://github.com/awslabs/amazon-kinesis-client/pull/790) Fixing a bug that caused paginated `ListShards` calls with the `ShardFilter` parameter to fail when the lease table was being initialized.
|
||||||
|
|
||||||
|
### Release (1.14.1 - January 27, 2021)
|
||||||
|
* [Milestone#56](https://github.com/awslabs/amazon-kinesis-client/milestone/56)
|
||||||
|
|
||||||
|
* Fix for cross DDB table interference when multiple KCL applications are run in same JVM.
|
||||||
|
* Fix and guards to avoid potential checkpoint rewind during shard end, which may block children shard processing.
|
||||||
|
* Fix for thread cycle wastage on InitializeTask for deleted shard.
|
||||||
|
* Improved logging in LeaseCleanupManager that would indicate why certain shards are not cleaned up from the lease table.
|
||||||
|
|
||||||
|
### Release (1.14.0 - August 17, 2020)
|
||||||
|
|
||||||
* [Milestone#50](https://github.com/awslabs/amazon-kinesis-client/milestone/50)
|
* [Milestone#50](https://github.com/awslabs/amazon-kinesis-client/milestone/50)
|
||||||
|
|
||||||
|
|
|
||||||
58
pom.xml
58
pom.xml
|
|
@ -6,7 +6,7 @@
|
||||||
<artifactId>amazon-kinesis-client</artifactId>
|
<artifactId>amazon-kinesis-client</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<name>Amazon Kinesis Client Library for Java</name>
|
<name>Amazon Kinesis Client Library for Java</name>
|
||||||
<version>1.14.0</version>
|
<version>1.15.1-SNAPSHOT</version>
|
||||||
<description>The Amazon Kinesis Client Library for Java enables Java developers to easily consume and process data
|
<description>The Amazon Kinesis Client Library for Java enables Java developers to easily consume and process data
|
||||||
from Amazon Kinesis.
|
from Amazon Kinesis.
|
||||||
</description>
|
</description>
|
||||||
|
|
@ -25,13 +25,18 @@
|
||||||
</licenses>
|
</licenses>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<aws-java-sdk.version>1.11.844</aws-java-sdk.version>
|
<aws-java-sdk.version>1.12.647</aws-java-sdk.version>
|
||||||
<sqlite4java.version>1.0.392</sqlite4java.version>
|
<sqlite4java.version>1.0.392</sqlite4java.version>
|
||||||
<sqlite4java.native>libsqlite4java</sqlite4java.native>
|
<sqlite4java.native>libsqlite4java</sqlite4java.native>
|
||||||
<sqlite4java.libpath>${project.build.directory}/test-lib</sqlite4java.libpath>
|
<sqlite4java.libpath>${project.build.directory}/test-lib</sqlite4java.libpath>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.amazonaws</groupId>
|
||||||
|
<artifactId>aws-java-sdk-core</artifactId>
|
||||||
|
<version>${aws-java-sdk.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.amazonaws</groupId>
|
<groupId>com.amazonaws</groupId>
|
||||||
<artifactId>aws-java-sdk-dynamodb</artifactId>
|
<artifactId>aws-java-sdk-dynamodb</artifactId>
|
||||||
|
|
@ -50,27 +55,27 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.guava</groupId>
|
<groupId>com.google.guava</groupId>
|
||||||
<artifactId>guava</artifactId>
|
<artifactId>guava</artifactId>
|
||||||
<version>26.0-jre</version>
|
<version>32.1.1-jre</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.protobuf</groupId>
|
<groupId>com.google.protobuf</groupId>
|
||||||
<artifactId>protobuf-java</artifactId>
|
<artifactId>protobuf-java</artifactId>
|
||||||
<version>3.11.4</version>
|
<version>3.19.6</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-lang3</artifactId>
|
<artifactId>commons-lang3</artifactId>
|
||||||
<version>3.7</version>
|
<version>3.12.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-logging</groupId>
|
<groupId>commons-logging</groupId>
|
||||||
<artifactId>commons-logging</artifactId>
|
<artifactId>commons-logging</artifactId>
|
||||||
<version>1.1.3</version>
|
<version>1.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
<version>1.16.10</version>
|
<version>1.18.22</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
@ -78,7 +83,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<version>4.11</version>
|
<version>4.13.2</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
@ -99,16 +104,29 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.amazonaws</groupId>
|
<groupId>com.amazonaws</groupId>
|
||||||
<artifactId>DynamoDBLocal</artifactId>
|
<artifactId>DynamoDBLocal</artifactId>
|
||||||
<version>1.11.86</version>
|
<version>1.17.2</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>log4j</groupId>
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
<artifactId>log4j</artifactId>
|
<artifactId>log4j-core</artifactId>
|
||||||
<version>1.2.17</version>
|
<version>2.20.0</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-api</artifactId>
|
||||||
|
<version>2.20.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-core</artifactId>
|
||||||
|
<version>2.15.0</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
|
|
@ -136,7 +154,7 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>3.2</version>
|
<version>3.11.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<source>1.8</source>
|
<source>1.8</source>
|
||||||
<target>1.8</target>
|
<target>1.8</target>
|
||||||
|
|
@ -150,7 +168,7 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<version>2.19.1</version>
|
<version>2.22.2</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<excludes>
|
<excludes>
|
||||||
<exclude>**/*IntegrationTest.java</exclude>
|
<exclude>**/*IntegrationTest.java</exclude>
|
||||||
|
|
@ -166,7 +184,7 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-failsafe-plugin</artifactId>
|
<artifactId>maven-failsafe-plugin</artifactId>
|
||||||
<version>2.19.1</version>
|
<version>2.22.2</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<includes>
|
<includes>
|
||||||
<include>**/*IntegrationTest.java</include>
|
<include>**/*IntegrationTest.java</include>
|
||||||
|
|
@ -252,7 +270,7 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-javadoc-plugin</artifactId>
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
<version>2.10.3</version>
|
<version>3.4.1</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<excludePackageNames>com.amazonaws.services.kinesis.producer.protobuf</excludePackageNames>
|
<excludePackageNames>com.amazonaws.services.kinesis.producer.protobuf</excludePackageNames>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|
@ -268,7 +286,7 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-source-plugin</artifactId>
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
<version>3.0.1</version>
|
<version>3.2.1</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>attach-sources</id>
|
<id>attach-sources</id>
|
||||||
|
|
@ -300,7 +318,7 @@
|
||||||
<jdk>[1.8,)</jdk>
|
<jdk>[1.8,)</jdk>
|
||||||
</activation>
|
</activation>
|
||||||
<properties>
|
<properties>
|
||||||
<additionalparam>-Xdoclint:none</additionalparam>
|
<doclint>none</doclint>
|
||||||
</properties>
|
</properties>
|
||||||
</profile>
|
</profile>
|
||||||
|
|
||||||
|
|
@ -311,7 +329,7 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-gpg-plugin</artifactId>
|
<artifactId>maven-gpg-plugin</artifactId>
|
||||||
<version>1.6</version>
|
<version>3.0.1</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>sign-artifacts</id>
|
<id>sign-artifacts</id>
|
||||||
|
|
@ -325,7 +343,7 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.sonatype.plugins</groupId>
|
<groupId>org.sonatype.plugins</groupId>
|
||||||
<artifactId>nexus-staging-maven-plugin</artifactId>
|
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||||
<version>1.6.8</version>
|
<version>1.6.13</version>
|
||||||
<extensions>true</extensions>
|
<extensions>true</extensions>
|
||||||
<configuration>
|
<configuration>
|
||||||
<serverId>sonatype-nexus-staging</serverId>
|
<serverId>sonatype-nexus-staging</serverId>
|
||||||
|
|
|
||||||
|
|
@ -45,24 +45,24 @@ public class AsynchronousGetRecordsRetrievalStrategy implements GetRecordsRetrie
|
||||||
private static final int TIME_TO_KEEP_ALIVE = 5;
|
private static final int TIME_TO_KEEP_ALIVE = 5;
|
||||||
private static final int CORE_THREAD_POOL_COUNT = 1;
|
private static final int CORE_THREAD_POOL_COUNT = 1;
|
||||||
|
|
||||||
private final KinesisDataFetcher dataFetcher;
|
private final IDataFetcher dataFetcher;
|
||||||
private final ExecutorService executorService;
|
private final ExecutorService executorService;
|
||||||
private final int retryGetRecordsInSeconds;
|
private final int retryGetRecordsInSeconds;
|
||||||
private final String shardId;
|
private final String shardId;
|
||||||
final Supplier<CompletionService<DataFetcherResult>> completionServiceSupplier;
|
final Supplier<CompletionService<DataFetcherResult>> completionServiceSupplier;
|
||||||
|
|
||||||
public AsynchronousGetRecordsRetrievalStrategy(@NonNull final KinesisDataFetcher dataFetcher,
|
public AsynchronousGetRecordsRetrievalStrategy(@NonNull final IDataFetcher dataFetcher,
|
||||||
final int retryGetRecordsInSeconds, final int maxGetRecordsThreadPool, String shardId) {
|
final int retryGetRecordsInSeconds, final int maxGetRecordsThreadPool, String shardId) {
|
||||||
this(dataFetcher, buildExector(maxGetRecordsThreadPool, shardId), retryGetRecordsInSeconds, shardId);
|
this(dataFetcher, buildExector(maxGetRecordsThreadPool, shardId), retryGetRecordsInSeconds, shardId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AsynchronousGetRecordsRetrievalStrategy(final KinesisDataFetcher dataFetcher,
|
public AsynchronousGetRecordsRetrievalStrategy(final IDataFetcher dataFetcher,
|
||||||
final ExecutorService executorService, final int retryGetRecordsInSeconds, String shardId) {
|
final ExecutorService executorService, final int retryGetRecordsInSeconds, String shardId) {
|
||||||
this(dataFetcher, executorService, retryGetRecordsInSeconds, () -> new ExecutorCompletionService<>(executorService),
|
this(dataFetcher, executorService, retryGetRecordsInSeconds, () -> new ExecutorCompletionService<>(executorService),
|
||||||
shardId);
|
shardId);
|
||||||
}
|
}
|
||||||
|
|
||||||
AsynchronousGetRecordsRetrievalStrategy(KinesisDataFetcher dataFetcher, ExecutorService executorService,
|
AsynchronousGetRecordsRetrievalStrategy(IDataFetcher dataFetcher, ExecutorService executorService,
|
||||||
int retryGetRecordsInSeconds, Supplier<CompletionService<DataFetcherResult>> completionServiceSupplier,
|
int retryGetRecordsInSeconds, Supplier<CompletionService<DataFetcherResult>> completionServiceSupplier,
|
||||||
String shardId) {
|
String shardId) {
|
||||||
this.dataFetcher = dataFetcher;
|
this.dataFetcher = dataFetcher;
|
||||||
|
|
@ -148,7 +148,7 @@ public class AsynchronousGetRecordsRetrievalStrategy implements GetRecordsRetrie
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KinesisDataFetcher getDataFetcher() {
|
public IDataFetcher getDataFetcher() {
|
||||||
return dataFetcher;
|
return dataFetcher;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ import com.amazonaws.services.kinesis.leases.interfaces.ILeaseManager;
|
||||||
* If we don't find a checkpoint for the parent shard(s), we assume they have been trimmed and directly
|
* If we don't find a checkpoint for the parent shard(s), we assume they have been trimmed and directly
|
||||||
* proceed with processing data from the shard.
|
* proceed with processing data from the shard.
|
||||||
*/
|
*/
|
||||||
class BlockOnParentShardTask implements ITask {
|
public class BlockOnParentShardTask implements ITask {
|
||||||
|
|
||||||
private static final Log LOG = LogFactory.getLog(BlockOnParentShardTask.class);
|
private static final Log LOG = LogFactory.getLog(BlockOnParentShardTask.class);
|
||||||
private final ShardInfo shardInfo;
|
private final ShardInfo shardInfo;
|
||||||
|
|
@ -45,7 +45,7 @@ class BlockOnParentShardTask implements ITask {
|
||||||
* @param leaseManager Used to fetch the lease and checkpoint info for parent shards
|
* @param leaseManager Used to fetch the lease and checkpoint info for parent shards
|
||||||
* @param parentShardPollIntervalMillis Sleep time if the parent shard has not completed processing
|
* @param parentShardPollIntervalMillis Sleep time if the parent shard has not completed processing
|
||||||
*/
|
*/
|
||||||
BlockOnParentShardTask(ShardInfo shardInfo,
|
public BlockOnParentShardTask(ShardInfo shardInfo,
|
||||||
ILeaseManager<KinesisClientLease> leaseManager,
|
ILeaseManager<KinesisClientLease> leaseManager,
|
||||||
long parentShardPollIntervalMillis) {
|
long parentShardPollIntervalMillis) {
|
||||||
this.shardInfo = shardInfo;
|
this.shardInfo = shardInfo;
|
||||||
|
|
|
||||||
|
|
@ -46,9 +46,9 @@ public interface GetRecordsRetrievalStrategy {
|
||||||
boolean isShutdown();
|
boolean isShutdown();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the KinesisDataFetcher used to getRecords from Kinesis.
|
* Returns the IDataFetcher used to getRecords
|
||||||
*
|
*
|
||||||
* @return KinesisDataFetcher
|
* @return IDataFetcher
|
||||||
*/
|
*/
|
||||||
KinesisDataFetcher getDataFetcher();
|
IDataFetcher getDataFetcher();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.amazonaws.services.kinesis.clientlibrary.lib.worker;
|
||||||
|
|
||||||
|
import com.amazonaws.services.kinesis.clientlibrary.types.ExtendedSequenceNumber;
|
||||||
|
import com.amazonaws.services.kinesis.model.ChildShard;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface IDataFetcher {
|
||||||
|
|
||||||
|
DataFetcherResult getRecords(int maxRecords);
|
||||||
|
|
||||||
|
void initialize(String initialCheckpoint, InitialPositionInStreamExtended initialPositionInStream);
|
||||||
|
|
||||||
|
void initialize(ExtendedSequenceNumber initialCheckpoint, InitialPositionInStreamExtended initialPositionInStream);
|
||||||
|
|
||||||
|
void advanceIteratorTo(String sequenceNumber, InitialPositionInStreamExtended initialPositionInStream);
|
||||||
|
|
||||||
|
void restartIterator();
|
||||||
|
|
||||||
|
boolean isShardEndReached();
|
||||||
|
|
||||||
|
List<ChildShard> getChildShards();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.amazonaws.services.kinesis.clientlibrary.lib.worker;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import lombok.Value;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
public interface IPeriodicShardSyncManager {
|
||||||
|
|
||||||
|
TaskResult start();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs ShardSync once, without scheduling further periodic ShardSyncs.
|
||||||
|
* @return TaskResult from shard sync
|
||||||
|
*/
|
||||||
|
TaskResult syncShardsOnce();
|
||||||
|
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
@Value
|
||||||
|
@Accessors(fluent = true)
|
||||||
|
@VisibleForTesting
|
||||||
|
class ShardSyncResponse {
|
||||||
|
private final boolean shouldDoShardSync;
|
||||||
|
private final boolean isHoleDetected;
|
||||||
|
private final String reasonForDecision;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.amazonaws.services.kinesis.clientlibrary.lib.worker;
|
||||||
|
|
||||||
|
public interface IShardConsumer {
|
||||||
|
|
||||||
|
boolean isSkipShardSyncAtWorkerInitializationIfLeasesExist();
|
||||||
|
|
||||||
|
enum TaskOutcome {
|
||||||
|
SUCCESSFUL, END_OF_SHARD, NOT_COMPLETE, FAILURE, LEASE_NOT_FOUND
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean consumeShard();
|
||||||
|
|
||||||
|
boolean isShutdown();
|
||||||
|
|
||||||
|
ShutdownReason getShutdownReason();
|
||||||
|
|
||||||
|
boolean beginShutdown();
|
||||||
|
|
||||||
|
void notifyShutdownRequested(ShutdownNotification shutdownNotification);
|
||||||
|
|
||||||
|
KinesisConsumerStates.ShardConsumerState getCurrentState();
|
||||||
|
|
||||||
|
boolean isShutdownRequested();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.amazonaws.services.kinesis.clientlibrary.lib.worker;
|
||||||
|
|
||||||
|
import com.amazonaws.services.kinesis.clientlibrary.interfaces.ICheckpoint;
|
||||||
|
import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessor;
|
||||||
|
import com.amazonaws.services.kinesis.leases.impl.LeaseCleanupManager;
|
||||||
|
import com.amazonaws.services.kinesis.metrics.interfaces.IMetricsFactory;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
|
public interface IShardConsumerFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a shard consumer to be used for consuming a (assigned) shard.
|
||||||
|
*
|
||||||
|
* @return Returns a shard consumer object.
|
||||||
|
*/
|
||||||
|
IShardConsumer createShardConsumer(ShardInfo shardInfo,
|
||||||
|
StreamConfig streamConfig,
|
||||||
|
ICheckpoint checkpointTracker,
|
||||||
|
IRecordProcessor recordProcessor,
|
||||||
|
RecordProcessorCheckpointer recordProcessorCheckpointer,
|
||||||
|
KinesisClientLibLeaseCoordinator leaseCoordinator,
|
||||||
|
long parentShardPollIntervalMillis,
|
||||||
|
boolean cleanupLeasesUponShardCompletion,
|
||||||
|
ExecutorService executorService,
|
||||||
|
IMetricsFactory metricsFactory,
|
||||||
|
long taskBackoffTimeMillis,
|
||||||
|
boolean skipShardSyncAtWorkerInitializationIfLeasesExist,
|
||||||
|
Optional<Integer> retryGetRecordsInSeconds,
|
||||||
|
Optional<Integer> maxGetRecordsThreadPool,
|
||||||
|
KinesisClientLibConfiguration config, ShardSyncer shardSyncer, ShardSyncStrategy shardSyncStrategy,
|
||||||
|
LeaseCleanupManager leaseCleanupManager);
|
||||||
|
}
|
||||||
|
|
@ -20,7 +20,7 @@ import java.util.concurrent.Callable;
|
||||||
* Interface for shard processing tasks.
|
* Interface for shard processing tasks.
|
||||||
* A task may execute an application callback (e.g. initialize, process, shutdown).
|
* A task may execute an application callback (e.g. initialize, process, shutdown).
|
||||||
*/
|
*/
|
||||||
interface ITask extends Callable<TaskResult> {
|
public interface ITask extends Callable<TaskResult> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform task logic.
|
* Perform task logic.
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
*/
|
*/
|
||||||
package com.amazonaws.services.kinesis.clientlibrary.lib.worker;
|
package com.amazonaws.services.kinesis.clientlibrary.lib.worker;
|
||||||
|
|
||||||
|
import com.amazonaws.services.kinesis.clientlibrary.exceptions.KinesisClientLibNonRetryableException;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
|
@ -28,7 +29,7 @@ import com.amazonaws.services.kinesis.metrics.interfaces.MetricsLevel;
|
||||||
/**
|
/**
|
||||||
* Task for initializing shard position and invoking the RecordProcessor initialize() API.
|
* Task for initializing shard position and invoking the RecordProcessor initialize() API.
|
||||||
*/
|
*/
|
||||||
class InitializeTask implements ITask {
|
public class InitializeTask implements ITask {
|
||||||
|
|
||||||
private static final Log LOG = LogFactory.getLog(InitializeTask.class);
|
private static final Log LOG = LogFactory.getLog(InitializeTask.class);
|
||||||
|
|
||||||
|
|
@ -36,7 +37,7 @@ class InitializeTask implements ITask {
|
||||||
|
|
||||||
private final ShardInfo shardInfo;
|
private final ShardInfo shardInfo;
|
||||||
private final IRecordProcessor recordProcessor;
|
private final IRecordProcessor recordProcessor;
|
||||||
private final KinesisDataFetcher dataFetcher;
|
private final IDataFetcher dataFetcher;
|
||||||
private final TaskType taskType = TaskType.INITIALIZE;
|
private final TaskType taskType = TaskType.INITIALIZE;
|
||||||
private final ICheckpoint checkpoint;
|
private final ICheckpoint checkpoint;
|
||||||
private final RecordProcessorCheckpointer recordProcessorCheckpointer;
|
private final RecordProcessorCheckpointer recordProcessorCheckpointer;
|
||||||
|
|
@ -48,11 +49,11 @@ class InitializeTask implements ITask {
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*/
|
*/
|
||||||
InitializeTask(ShardInfo shardInfo,
|
public InitializeTask(ShardInfo shardInfo,
|
||||||
IRecordProcessor recordProcessor,
|
IRecordProcessor recordProcessor,
|
||||||
ICheckpoint checkpoint,
|
ICheckpoint checkpoint,
|
||||||
RecordProcessorCheckpointer recordProcessorCheckpointer,
|
RecordProcessorCheckpointer recordProcessorCheckpointer,
|
||||||
KinesisDataFetcher dataFetcher,
|
IDataFetcher dataFetcher,
|
||||||
long backoffTimeMillis,
|
long backoffTimeMillis,
|
||||||
StreamConfig streamConfig,
|
StreamConfig streamConfig,
|
||||||
GetRecordsCache getRecordsCache) {
|
GetRecordsCache getRecordsCache) {
|
||||||
|
|
@ -79,7 +80,15 @@ class InitializeTask implements ITask {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
LOG.debug("Initializing ShardId " + shardInfo.getShardId());
|
LOG.debug("Initializing ShardId " + shardInfo.getShardId());
|
||||||
Checkpoint initialCheckpointObject = checkpoint.getCheckpointObject(shardInfo.getShardId());
|
Checkpoint initialCheckpointObject;
|
||||||
|
try {
|
||||||
|
initialCheckpointObject = checkpoint.getCheckpointObject(shardInfo.getShardId());
|
||||||
|
} catch (KinesisClientLibNonRetryableException e) {
|
||||||
|
LOG.error("Caught exception while fetching checkpoint for " + shardInfo.getShardId(), e);
|
||||||
|
final TaskResult result = new TaskResult(e);
|
||||||
|
result.leaseNotFound();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
ExtendedSequenceNumber initialCheckpoint = initialCheckpointObject.getCheckpoint();
|
ExtendedSequenceNumber initialCheckpoint = initialCheckpointObject.getCheckpoint();
|
||||||
|
|
||||||
dataFetcher.initialize(initialCheckpoint.getSequenceNumber(), streamConfig.getInitialPositionInStream());
|
dataFetcher.initialize(initialCheckpoint.getSequenceNumber(), streamConfig.getInitialPositionInStream());
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,14 @@ package com.amazonaws.services.kinesis.clientlibrary.lib.worker;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.amazonaws.services.dynamodbv2.model.BillingMode;
|
import com.amazonaws.services.dynamodbv2.model.BillingMode;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
import com.amazonaws.ClientConfiguration;
|
import com.amazonaws.ClientConfiguration;
|
||||||
|
import com.amazonaws.arn.Arn;
|
||||||
import com.amazonaws.auth.AWSCredentialsProvider;
|
import com.amazonaws.auth.AWSCredentialsProvider;
|
||||||
import com.amazonaws.services.kinesis.metrics.impl.MetricsHelper;
|
import com.amazonaws.services.kinesis.metrics.impl.MetricsHelper;
|
||||||
import com.amazonaws.services.kinesis.metrics.interfaces.IMetricsScope;
|
import com.amazonaws.services.kinesis.metrics.interfaces.IMetricsScope;
|
||||||
|
|
@ -61,7 +63,7 @@ public class KinesisClientLibConfiguration {
|
||||||
public static final int DEFAULT_MAX_RECORDS = 10000;
|
public static final int DEFAULT_MAX_RECORDS = 10000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default value for how long the {@link ShardConsumer} should sleep if no records are returned from the call to
|
* The default value for how long the {@link KinesisShardConsumer} should sleep if no records are returned from the call to
|
||||||
* {@link com.amazonaws.services.kinesis.AmazonKinesis#getRecords(com.amazonaws.services.kinesis.model.GetRecordsRequest)}.
|
* {@link com.amazonaws.services.kinesis.AmazonKinesis#getRecords(com.amazonaws.services.kinesis.model.GetRecordsRequest)}.
|
||||||
*/
|
*/
|
||||||
public static final long DEFAULT_IDLETIME_BETWEEN_READS_MILLIS = 1000L;
|
public static final long DEFAULT_IDLETIME_BETWEEN_READS_MILLIS = 1000L;
|
||||||
|
|
@ -91,7 +93,7 @@ public class KinesisClientLibConfiguration {
|
||||||
public static final boolean DEFAULT_CLEANUP_LEASES_UPON_SHARDS_COMPLETION = true;
|
public static final boolean DEFAULT_CLEANUP_LEASES_UPON_SHARDS_COMPLETION = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interval to run lease cleanup thread in {@link LeaseCleanupManager}.
|
* Interval to run lease cleanup thread in {@link com.amazonaws.services.kinesis.leases.impl.LeaseCleanupManager}.
|
||||||
*/
|
*/
|
||||||
private static final long DEFAULT_LEASE_CLEANUP_INTERVAL_MILLIS = Duration.ofMinutes(1).toMillis();
|
private static final long DEFAULT_LEASE_CLEANUP_INTERVAL_MILLIS = Duration.ofMinutes(1).toMillis();
|
||||||
|
|
||||||
|
|
@ -147,7 +149,7 @@ public class KinesisClientLibConfiguration {
|
||||||
/**
|
/**
|
||||||
* User agent set when Amazon Kinesis Client Library makes AWS requests.
|
* User agent set when Amazon Kinesis Client Library makes AWS requests.
|
||||||
*/
|
*/
|
||||||
public static final String KINESIS_CLIENT_LIB_USER_AGENT = "amazon-kinesis-client-library-java-1.14.0";
|
public static final String KINESIS_CLIENT_LIB_USER_AGENT = "amazon-kinesis-client-library-java-1.15.1";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* KCL will validate client provided sequence numbers with a call to Amazon Kinesis before checkpointing for calls
|
* KCL will validate client provided sequence numbers with a call to Amazon Kinesis before checkpointing for calls
|
||||||
|
|
@ -233,11 +235,25 @@ public class KinesisClientLibConfiguration {
|
||||||
*/
|
*/
|
||||||
public static final int DEFAULT_MAX_INITIALIZATION_ATTEMPTS = 20;
|
public static final int DEFAULT_MAX_INITIALIZATION_ATTEMPTS = 20;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pattern for a stream ARN. The valid format is
|
||||||
|
* {@code arn:aws:kinesis:<region>:<accountId>:stream/<streamName>}
|
||||||
|
* where {@code region} is the id representation of a {@link Region}.
|
||||||
|
*/
|
||||||
|
private static final Pattern STREAM_ARN_PATTERN = Pattern.compile(
|
||||||
|
"arn:aws[^:]*:kinesis:(?<region>[-a-z0-9]+):(?<accountId>[0-9]{12}):stream/(?<streamName>.+)");
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private BillingMode billingMode;
|
private BillingMode billingMode;
|
||||||
private String applicationName;
|
private String applicationName;
|
||||||
private String tableName;
|
private String tableName;
|
||||||
private String streamName;
|
private String streamName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kinesis stream ARN
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
private Arn streamArn;
|
||||||
private String kinesisEndpoint;
|
private String kinesisEndpoint;
|
||||||
private String dynamoDBEndpoint;
|
private String dynamoDBEndpoint;
|
||||||
private InitialPositionInStream initialPositionInStream;
|
private InitialPositionInStream initialPositionInStream;
|
||||||
|
|
@ -719,14 +735,105 @@ public class KinesisClientLibConfiguration {
|
||||||
this.billingMode = billingMode;
|
this.billingMode = billingMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duplicate constructor to support stream ARN's in place of stream names.
|
||||||
|
*
|
||||||
|
* @param applicationName Name of the Kinesis application
|
||||||
|
* By default the application name is included in the user agent string used to make AWS requests. This
|
||||||
|
* can assist with troubleshooting (e.g. distinguish requests made by separate applications).
|
||||||
|
* @param streamArn Kinesis stream ARN
|
||||||
|
* @param kinesisEndpoint Kinesis endpoint
|
||||||
|
* @param dynamoDBEndpoint DynamoDB endpoint
|
||||||
|
* @param initialPositionInStream One of LATEST or TRIM_HORIZON. The KinesisClientLibrary will start fetching
|
||||||
|
* records from that location in the stream when an application starts up for the first time and there
|
||||||
|
* are no checkpoints. If there are checkpoints, then we start from the checkpoint position.
|
||||||
|
* @param kinesisCredentialsProvider Provides credentials used to access Kinesis
|
||||||
|
* @param dynamoDBCredentialsProvider Provides credentials used to access DynamoDB
|
||||||
|
* @param cloudWatchCredentialsProvider Provides credentials used to access CloudWatch
|
||||||
|
* @param failoverTimeMillis Lease duration (leases not renewed within this period will be claimed by others)
|
||||||
|
* @param workerId Used to distinguish different workers/processes of a Kinesis application
|
||||||
|
* @param maxRecords Max records to read per Kinesis getRecords() call
|
||||||
|
* @param idleTimeBetweenReadsInMillis Idle time between calls to fetch data from Kinesis
|
||||||
|
* @param callProcessRecordsEvenForEmptyRecordList Call the IRecordProcessor::processRecords() API even if
|
||||||
|
* GetRecords returned an empty record list.
|
||||||
|
* @param parentShardPollIntervalMillis Wait for this long between polls to check if parent shards are done
|
||||||
|
* @param shardSyncIntervalMillis Time between tasks to sync leases and Kinesis shards
|
||||||
|
* @param cleanupTerminatedShardsBeforeExpiry Clean up shards we've finished processing (don't wait for expiration
|
||||||
|
* in Kinesis)
|
||||||
|
* @param kinesisClientConfig Client Configuration used by Kinesis client
|
||||||
|
* @param dynamoDBClientConfig Client Configuration used by DynamoDB client
|
||||||
|
* @param cloudWatchClientConfig Client Configuration used by CloudWatch client
|
||||||
|
* @param taskBackoffTimeMillis Backoff period when tasks encounter an exception
|
||||||
|
* @param metricsBufferTimeMillis Metrics are buffered for at most this long before publishing to CloudWatch
|
||||||
|
* @param metricsMaxQueueSize Max number of metrics to buffer before publishing to CloudWatch
|
||||||
|
* @param validateSequenceNumberBeforeCheckpointing whether KCL should validate client provided sequence numbers
|
||||||
|
* with a call to Amazon Kinesis before checkpointing for calls to
|
||||||
|
* {@link RecordProcessorCheckpointer#checkpoint(String)}
|
||||||
|
* @param regionName The region name for the service
|
||||||
|
* @param shutdownGraceMillis Time before gracefully shutdown forcefully terminates
|
||||||
|
* @param billingMode The DDB Billing mode to set for lease table creation.
|
||||||
|
* @param recordsFetcherFactory Factory to create the records fetcher to retrieve data from Kinesis for a given shard.
|
||||||
|
* @param leaseCleanupIntervalMillis Rate at which to run lease cleanup thread in
|
||||||
|
* {@link com.amazonaws.services.kinesis.leases.impl.LeaseCleanupManager}
|
||||||
|
* @param completedLeaseCleanupThresholdMillis Threshold in millis at which to check if there are any completed leases
|
||||||
|
* (leases for shards which have been closed as a result of a resharding operation) that need to be cleaned up.
|
||||||
|
* @param garbageLeaseCleanupThresholdMillis Threshold in millis at which to check if there are any garbage leases
|
||||||
|
* (leases for shards which no longer exist in the stream) that need to be cleaned up.
|
||||||
|
*/
|
||||||
|
public KinesisClientLibConfiguration(String applicationName,
|
||||||
|
Arn streamArn,
|
||||||
|
String kinesisEndpoint,
|
||||||
|
String dynamoDBEndpoint,
|
||||||
|
InitialPositionInStream initialPositionInStream,
|
||||||
|
AWSCredentialsProvider kinesisCredentialsProvider,
|
||||||
|
AWSCredentialsProvider dynamoDBCredentialsProvider,
|
||||||
|
AWSCredentialsProvider cloudWatchCredentialsProvider,
|
||||||
|
long failoverTimeMillis,
|
||||||
|
String workerId,
|
||||||
|
int maxRecords,
|
||||||
|
long idleTimeBetweenReadsInMillis,
|
||||||
|
boolean callProcessRecordsEvenForEmptyRecordList,
|
||||||
|
long parentShardPollIntervalMillis,
|
||||||
|
long shardSyncIntervalMillis,
|
||||||
|
boolean cleanupTerminatedShardsBeforeExpiry,
|
||||||
|
ClientConfiguration kinesisClientConfig,
|
||||||
|
ClientConfiguration dynamoDBClientConfig,
|
||||||
|
ClientConfiguration cloudWatchClientConfig,
|
||||||
|
long taskBackoffTimeMillis,
|
||||||
|
long metricsBufferTimeMillis,
|
||||||
|
int metricsMaxQueueSize,
|
||||||
|
boolean validateSequenceNumberBeforeCheckpointing,
|
||||||
|
String regionName,
|
||||||
|
long shutdownGraceMillis,
|
||||||
|
BillingMode billingMode,
|
||||||
|
RecordsFetcherFactory recordsFetcherFactory,
|
||||||
|
long leaseCleanupIntervalMillis,
|
||||||
|
long completedLeaseCleanupThresholdMillis,
|
||||||
|
long garbageLeaseCleanupThresholdMillis) {
|
||||||
|
this(applicationName, streamArn.getResource().getResource(), kinesisEndpoint, dynamoDBEndpoint, initialPositionInStream, kinesisCredentialsProvider,
|
||||||
|
dynamoDBCredentialsProvider, cloudWatchCredentialsProvider, failoverTimeMillis, workerId, maxRecords, idleTimeBetweenReadsInMillis,
|
||||||
|
callProcessRecordsEvenForEmptyRecordList, parentShardPollIntervalMillis, shardSyncIntervalMillis, cleanupTerminatedShardsBeforeExpiry,
|
||||||
|
kinesisClientConfig, dynamoDBClientConfig, cloudWatchClientConfig, taskBackoffTimeMillis, metricsBufferTimeMillis,
|
||||||
|
metricsMaxQueueSize, validateSequenceNumberBeforeCheckpointing, regionName, shutdownGraceMillis, billingMode,
|
||||||
|
recordsFetcherFactory, leaseCleanupIntervalMillis, completedLeaseCleanupThresholdMillis, garbageLeaseCleanupThresholdMillis);
|
||||||
|
checkIsValidStreamArn(streamArn);
|
||||||
|
this.streamArn = streamArn;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if value is positive, otherwise throw an exception
|
// Check if value is positive, otherwise throw an exception
|
||||||
private void checkIsValuePositive(String key, long value) {
|
private static void checkIsValuePositive(String key, long value) {
|
||||||
if (value <= 0) {
|
if (value <= 0) {
|
||||||
throw new IllegalArgumentException("Value of " + key
|
throw new IllegalArgumentException("Value of " + key
|
||||||
+ " should be positive, but current value is " + value);
|
+ " should be positive, but current value is " + value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void checkIsValidStreamArn(Arn streamArn) {
|
||||||
|
if (!STREAM_ARN_PATTERN.matcher(streamArn.toString()).matches()) {
|
||||||
|
throw new IllegalArgumentException("Invalid streamArn " + streamArn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if user agent in configuration is the default agent.
|
// Check if user agent in configuration is the default agent.
|
||||||
// If so, replace it with application name plus KINESIS_CLIENT_LIB_USER_AGENT.
|
// If so, replace it with application name plus KINESIS_CLIENT_LIB_USER_AGENT.
|
||||||
// If not, append KINESIS_CLIENT_LIB_USER_AGENT to the end.
|
// If not, append KINESIS_CLIENT_LIB_USER_AGENT to the end.
|
||||||
|
|
@ -1030,7 +1137,7 @@ public class KinesisClientLibConfiguration {
|
||||||
* Keeping it protected to forbid outside callers from depending on this internal object.
|
* Keeping it protected to forbid outside callers from depending on this internal object.
|
||||||
* @return The initialPositionInStreamExtended object.
|
* @return The initialPositionInStreamExtended object.
|
||||||
*/
|
*/
|
||||||
protected InitialPositionInStreamExtended getInitialPositionInStreamExtended() {
|
public InitialPositionInStreamExtended getInitialPositionInStreamExtended() {
|
||||||
return initialPositionInStreamExtended;
|
return initialPositionInStreamExtended;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1056,6 +1163,25 @@ public class KinesisClientLibConfiguration {
|
||||||
return shutdownGraceMillis;
|
return shutdownGraceMillis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param streamName Kinesis stream name
|
||||||
|
* @return KinesisClientLibConfiguration
|
||||||
|
*/
|
||||||
|
public KinesisClientLibConfiguration withStreamName(String streamName) {
|
||||||
|
this.streamName = streamName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param streamArn Kinesis stream ARN
|
||||||
|
* @return KinesisClientLibConfiguration
|
||||||
|
*/
|
||||||
|
public KinesisClientLibConfiguration withStreamArn(Arn streamArn) {
|
||||||
|
checkIsValidStreamArn(streamArn);
|
||||||
|
this.streamArn = streamArn;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// CHECKSTYLE:IGNORE HiddenFieldCheck FOR NEXT 190 LINES
|
// CHECKSTYLE:IGNORE HiddenFieldCheck FOR NEXT 190 LINES
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ import com.amazonaws.services.kinesis.metrics.interfaces.IMetricsFactory;
|
||||||
/**
|
/**
|
||||||
* This class is used to coordinate/manage leases owned by this worker process and to get/set checkpoints.
|
* This class is used to coordinate/manage leases owned by this worker process and to get/set checkpoints.
|
||||||
*/
|
*/
|
||||||
class KinesisClientLibLeaseCoordinator extends LeaseCoordinator<KinesisClientLease> implements ICheckpoint {
|
public class KinesisClientLibLeaseCoordinator extends LeaseCoordinator<KinesisClientLease> implements ICheckpoint {
|
||||||
|
|
||||||
private static final Log LOG = LogFactory.getLog(KinesisClientLibLeaseCoordinator.class);
|
private static final Log LOG = LogFactory.getLog(KinesisClientLibLeaseCoordinator.class);
|
||||||
|
|
||||||
|
|
@ -283,7 +283,8 @@ class KinesisClientLibLeaseCoordinator extends LeaseCoordinator<KinesisClientLea
|
||||||
try {
|
try {
|
||||||
KinesisClientLease lease = leaseManager.getLease(shardId);
|
KinesisClientLease lease = leaseManager.getLease(shardId);
|
||||||
if (lease == null) {
|
if (lease == null) {
|
||||||
throw new KinesisClientLibIOException(errorMessage);
|
// This is a KinesisClientLibNonRetryableException
|
||||||
|
throw new com.amazonaws.services.kinesis.clientlibrary.exceptions.InvalidStateException(errorMessage);
|
||||||
}
|
}
|
||||||
return new Checkpoint(lease.getCheckpoint(), lease.getPendingCheckpoint());
|
return new Checkpoint(lease.getCheckpoint(), lease.getPendingCheckpoint());
|
||||||
} catch (DependencyException | InvalidStateException | ProvisionedThroughputException e) {
|
} catch (DependencyException | InvalidStateException | ProvisionedThroughputException e) {
|
||||||
|
|
@ -367,7 +368,7 @@ class KinesisClientLibLeaseCoordinator extends LeaseCoordinator<KinesisClientLea
|
||||||
*
|
*
|
||||||
* @return LeaseManager
|
* @return LeaseManager
|
||||||
*/
|
*/
|
||||||
ILeaseManager<KinesisClientLease> getLeaseManager() {
|
public ILeaseManager<KinesisClientLease> getLeaseManager() {
|
||||||
return leaseManager;
|
return leaseManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
package com.amazonaws.services.kinesis.clientlibrary.lib.worker;
|
package com.amazonaws.services.kinesis.clientlibrary.lib.worker;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Top level container for all the possible states a {@link ShardConsumer} can be in. The logic for creation of tasks,
|
* Top level container for all the possible states a {@link KinesisShardConsumer} can be in. The logic for creation of tasks,
|
||||||
* and state transitions is contained within the {@link ConsumerState} objects.
|
* and state transitions is contained within the {@link ConsumerState} objects.
|
||||||
*
|
*
|
||||||
* <h2>State Diagram</h2>
|
* <h2>State Diagram</h2>
|
||||||
|
|
@ -64,12 +64,12 @@ package com.amazonaws.services.kinesis.clientlibrary.lib.worker;
|
||||||
* +-------------------+
|
* +-------------------+
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
class ConsumerStates {
|
public class KinesisConsumerStates {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enumerates processing states when working on a shard.
|
* Enumerates processing states when working on a shard.
|
||||||
*/
|
*/
|
||||||
enum ShardConsumerState {
|
public enum ShardConsumerState {
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
WAITING_ON_PARENT_SHARDS(new BlockedOnParentState()),
|
WAITING_ON_PARENT_SHARDS(new BlockedOnParentState()),
|
||||||
INITIALIZING(new InitializingState()),
|
INITIALIZING(new InitializingState()),
|
||||||
|
|
@ -96,7 +96,7 @@ class ConsumerStates {
|
||||||
* do when a transition occurs.
|
* do when a transition occurs.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
interface ConsumerState {
|
public interface ConsumerState {
|
||||||
/**
|
/**
|
||||||
* Creates a new task for this state using the passed in consumer to build the task. If there is no task
|
* Creates a new task for this state using the passed in consumer to build the task. If there is no task
|
||||||
* required for this state it may return a null value. {@link ConsumerState}'s are allowed to modify the
|
* required for this state it may return a null value. {@link ConsumerState}'s are allowed to modify the
|
||||||
|
|
@ -106,11 +106,11 @@ class ConsumerStates {
|
||||||
* the consumer to use build the task, or execute state.
|
* the consumer to use build the task, or execute state.
|
||||||
* @return a valid task for this state or null if there is no task required.
|
* @return a valid task for this state or null if there is no task required.
|
||||||
*/
|
*/
|
||||||
ITask createTask(ShardConsumer consumer);
|
ITask createTask(KinesisShardConsumer consumer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the next state of the consumer upon success of the task return by
|
* Provides the next state of the consumer upon success of the task return by
|
||||||
* {@link ConsumerState#createTask(ShardConsumer)}.
|
* {@link ConsumerState#createTask(KinesisShardConsumer)}.
|
||||||
*
|
*
|
||||||
* @return the next state that the consumer should transition to, this may be the same object as the current
|
* @return the next state that the consumer should transition to, this may be the same object as the current
|
||||||
* state.
|
* state.
|
||||||
|
|
@ -129,7 +129,7 @@ class ConsumerStates {
|
||||||
ConsumerState shutdownTransition(ShutdownReason shutdownReason);
|
ConsumerState shutdownTransition(ShutdownReason shutdownReason);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of task that {@link ConsumerState#createTask(ShardConsumer)} would return. This is always a valid state
|
* The type of task that {@link ConsumerState#createTask(KinesisShardConsumer)} would return. This is always a valid state
|
||||||
* even if createTask would return a null value.
|
* even if createTask would return a null value.
|
||||||
*
|
*
|
||||||
* @return the type of task that this state represents.
|
* @return the type of task that this state represents.
|
||||||
|
|
@ -149,7 +149,7 @@ class ConsumerStates {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The initial state that any {@link ShardConsumer} should start in.
|
* The initial state that any {@link KinesisShardConsumer} should start in.
|
||||||
*/
|
*/
|
||||||
static final ConsumerState INITIAL_STATE = ShardConsumerState.WAITING_ON_PARENT_SHARDS.getConsumerState();
|
static final ConsumerState INITIAL_STATE = ShardConsumerState.WAITING_ON_PARENT_SHARDS.getConsumerState();
|
||||||
|
|
||||||
|
|
@ -187,7 +187,7 @@ class ConsumerStates {
|
||||||
static class BlockedOnParentState implements ConsumerState {
|
static class BlockedOnParentState implements ConsumerState {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ITask createTask(ShardConsumer consumer) {
|
public ITask createTask(KinesisShardConsumer consumer) {
|
||||||
return new BlockOnParentShardTask(consumer.getShardInfo(), consumer.getLeaseManager(),
|
return new BlockOnParentShardTask(consumer.getShardInfo(), consumer.getLeaseManager(),
|
||||||
consumer.getParentShardPollIntervalMillis());
|
consumer.getParentShardPollIntervalMillis());
|
||||||
}
|
}
|
||||||
|
|
@ -247,10 +247,10 @@ class ConsumerStates {
|
||||||
* </dd>
|
* </dd>
|
||||||
* </dl>
|
* </dl>
|
||||||
*/
|
*/
|
||||||
static class InitializingState implements ConsumerState {
|
public static class InitializingState implements ConsumerState {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ITask createTask(ShardConsumer consumer) {
|
public ITask createTask(KinesisShardConsumer consumer) {
|
||||||
return new InitializeTask(consumer.getShardInfo(),
|
return new InitializeTask(consumer.getShardInfo(),
|
||||||
consumer.getRecordProcessor(),
|
consumer.getRecordProcessor(),
|
||||||
consumer.getCheckpoint(),
|
consumer.getCheckpoint(),
|
||||||
|
|
@ -311,7 +311,7 @@ class ConsumerStates {
|
||||||
static class ProcessingState implements ConsumerState {
|
static class ProcessingState implements ConsumerState {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ITask createTask(ShardConsumer consumer) {
|
public ITask createTask(KinesisShardConsumer consumer) {
|
||||||
return new ProcessTask(consumer.getShardInfo(),
|
return new ProcessTask(consumer.getShardInfo(),
|
||||||
consumer.getStreamConfig(),
|
consumer.getStreamConfig(),
|
||||||
consumer.getRecordProcessor(),
|
consumer.getRecordProcessor(),
|
||||||
|
|
@ -358,10 +358,10 @@ class ConsumerStates {
|
||||||
* <h2>Valid Transitions</h2>
|
* <h2>Valid Transitions</h2>
|
||||||
* <dl>
|
* <dl>
|
||||||
* <dt>Success</dt>
|
* <dt>Success</dt>
|
||||||
* <dd>Success shouldn't normally be called since the {@link ShardConsumer} is marked for shutdown.</dd>
|
* <dd>Success shouldn't normally be called since the {@link KinesisShardConsumer} is marked for shutdown.</dd>
|
||||||
* <dt>Shutdown</dt>
|
* <dt>Shutdown</dt>
|
||||||
* <dd>At this point records are being retrieved, and processed. An explicit shutdown will allow the record
|
* <dd>At this point records are being retrieved, and processed. An explicit shutdown will allow the record
|
||||||
* processor one last chance to checkpoint, and then the {@link ShardConsumer} will be held in an idle state.
|
* processor one last chance to checkpoint, and then the {@link KinesisShardConsumer} will be held in an idle state.
|
||||||
* <dl>
|
* <dl>
|
||||||
* <dt>{@link ShutdownReason#REQUESTED}</dt>
|
* <dt>{@link ShutdownReason#REQUESTED}</dt>
|
||||||
* <dd>Remains in the {@link ShardConsumerState#SHUTDOWN_REQUESTED}, but the state implementation changes to
|
* <dd>Remains in the {@link ShardConsumerState#SHUTDOWN_REQUESTED}, but the state implementation changes to
|
||||||
|
|
@ -377,7 +377,7 @@ class ConsumerStates {
|
||||||
static class ShutdownNotificationState implements ConsumerState {
|
static class ShutdownNotificationState implements ConsumerState {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ITask createTask(ShardConsumer consumer) {
|
public ITask createTask(KinesisShardConsumer consumer) {
|
||||||
return new ShutdownNotificationTask(consumer.getRecordProcessor(),
|
return new ShutdownNotificationTask(consumer.getRecordProcessor(),
|
||||||
consumer.getRecordProcessorCheckpointer(),
|
consumer.getRecordProcessorCheckpointer(),
|
||||||
consumer.getShutdownNotification(),
|
consumer.getShutdownNotification(),
|
||||||
|
|
@ -414,24 +414,24 @@ class ConsumerStates {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Once the {@link ShutdownNotificationState} has been completed the {@link ShardConsumer} must not re-enter any of the
|
* Once the {@link ShutdownNotificationState} has been completed the {@link KinesisShardConsumer} must not re-enter any of the
|
||||||
* processing states. This state idles the {@link ShardConsumer} until the worker triggers the final shutdown state.
|
* processing states. This state idles the {@link KinesisShardConsumer} until the worker triggers the final shutdown state.
|
||||||
*
|
*
|
||||||
* <h2>Valid Transitions</h2>
|
* <h2>Valid Transitions</h2>
|
||||||
* <dl>
|
* <dl>
|
||||||
* <dt>Success</dt>
|
* <dt>Success</dt>
|
||||||
* <dd>
|
* <dd>
|
||||||
* <p>
|
* <p>
|
||||||
* Success shouldn't normally be called since the {@link ShardConsumer} is marked for shutdown.
|
* Success shouldn't normally be called since the {@link KinesisShardConsumer} is marked for shutdown.
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* Remains in the {@link ShutdownNotificationCompletionState}
|
* Remains in the {@link ShutdownNotificationCompletionState}
|
||||||
* </p>
|
* </p>
|
||||||
* </dd>
|
* </dd>
|
||||||
* <dt>Shutdown</dt>
|
* <dt>Shutdown</dt>
|
||||||
* <dd>At this point the {@link ShardConsumer} has notified the record processor of the impending shutdown, and is
|
* <dd>At this point the {@link KinesisShardConsumer} has notified the record processor of the impending shutdown, and is
|
||||||
* waiting that notification. While waiting for the notification no further processing should occur on the
|
* waiting that notification. While waiting for the notification no further processing should occur on the
|
||||||
* {@link ShardConsumer}.
|
* {@link KinesisShardConsumer}.
|
||||||
* <dl>
|
* <dl>
|
||||||
* <dt>{@link ShutdownReason#REQUESTED}</dt>
|
* <dt>{@link ShutdownReason#REQUESTED}</dt>
|
||||||
* <dd>Remains in the {@link ShardConsumerState#SHUTDOWN_REQUESTED}, and the state implementation remains
|
* <dd>Remains in the {@link ShardConsumerState#SHUTDOWN_REQUESTED}, and the state implementation remains
|
||||||
|
|
@ -447,7 +447,7 @@ class ConsumerStates {
|
||||||
static class ShutdownNotificationCompletionState implements ConsumerState {
|
static class ShutdownNotificationCompletionState implements ConsumerState {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ITask createTask(ShardConsumer consumer) {
|
public ITask createTask(KinesisShardConsumer consumer) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -481,14 +481,14 @@ class ConsumerStates {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This state is entered if the {@link ShardConsumer} loses its lease, or reaches the end of the shard.
|
* This state is entered if the {@link KinesisShardConsumer} loses its lease, or reaches the end of the shard.
|
||||||
*
|
*
|
||||||
* <h2>Valid Transitions</h2>
|
* <h2>Valid Transitions</h2>
|
||||||
* <dl>
|
* <dl>
|
||||||
* <dt>Success</dt>
|
* <dt>Success</dt>
|
||||||
* <dd>
|
* <dd>
|
||||||
* <p>
|
* <p>
|
||||||
* Success shouldn't normally be called since the {@link ShardConsumer} is marked for shutdown.
|
* Success shouldn't normally be called since the {@link KinesisShardConsumer} is marked for shutdown.
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* Transitions to the {@link ShutdownCompleteState}
|
* Transitions to the {@link ShutdownCompleteState}
|
||||||
|
|
@ -497,7 +497,7 @@ class ConsumerStates {
|
||||||
* <dt>Shutdown</dt>
|
* <dt>Shutdown</dt>
|
||||||
* <dd>At this point the record processor has processed the final shutdown indication, and depending on the shutdown
|
* <dd>At this point the record processor has processed the final shutdown indication, and depending on the shutdown
|
||||||
* reason taken the correct course of action. From this point on there should be no more interactions with the
|
* reason taken the correct course of action. From this point on there should be no more interactions with the
|
||||||
* record processor or {@link ShardConsumer}.
|
* record processor or {@link KinesisShardConsumer}.
|
||||||
* <dl>
|
* <dl>
|
||||||
* <dt>{@link ShutdownReason#REQUESTED}</dt>
|
* <dt>{@link ShutdownReason#REQUESTED}</dt>
|
||||||
* <dd>
|
* <dd>
|
||||||
|
|
@ -519,8 +519,8 @@ class ConsumerStates {
|
||||||
static class ShuttingDownState implements ConsumerState {
|
static class ShuttingDownState implements ConsumerState {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ITask createTask(ShardConsumer consumer) {
|
public ITask createTask(KinesisShardConsumer consumer) {
|
||||||
return new ShutdownTask(consumer.getShardInfo(),
|
return new KinesisShutdownTask(consumer.getShardInfo(),
|
||||||
consumer.getRecordProcessor(),
|
consumer.getRecordProcessor(),
|
||||||
consumer.getRecordProcessorCheckpointer(),
|
consumer.getRecordProcessorCheckpointer(),
|
||||||
consumer.getShutdownReason(),
|
consumer.getShutdownReason(),
|
||||||
|
|
@ -562,21 +562,21 @@ class ConsumerStates {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the final state for the {@link ShardConsumer}. This occurs once all shutdown activities are completed.
|
* This is the final state for the {@link KinesisShardConsumer}. This occurs once all shutdown activities are completed.
|
||||||
*
|
*
|
||||||
* <h2>Valid Transitions</h2>
|
* <h2>Valid Transitions</h2>
|
||||||
* <dl>
|
* <dl>
|
||||||
* <dt>Success</dt>
|
* <dt>Success</dt>
|
||||||
* <dd>
|
* <dd>
|
||||||
* <p>
|
* <p>
|
||||||
* Success shouldn't normally be called since the {@link ShardConsumer} is marked for shutdown.
|
* Success shouldn't normally be called since the {@link KinesisShardConsumer} is marked for shutdown.
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* Remains in the {@link ShutdownCompleteState}
|
* Remains in the {@link ShutdownCompleteState}
|
||||||
* </p>
|
* </p>
|
||||||
* </dd>
|
* </dd>
|
||||||
* <dt>Shutdown</dt>
|
* <dt>Shutdown</dt>
|
||||||
* <dd>At this point the all shutdown activites are completed, and the {@link ShardConsumer} should not take any
|
* <dd>At this point the all shutdown activites are completed, and the {@link KinesisShardConsumer} should not take any
|
||||||
* further actions.
|
* further actions.
|
||||||
* <dl>
|
* <dl>
|
||||||
* <dt>{@link ShutdownReason#REQUESTED}</dt>
|
* <dt>{@link ShutdownReason#REQUESTED}</dt>
|
||||||
|
|
@ -599,7 +599,7 @@ class ConsumerStates {
|
||||||
static class ShutdownCompleteState implements ConsumerState {
|
static class ShutdownCompleteState implements ConsumerState {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ITask createTask(ShardConsumer consumer) {
|
public ITask createTask(KinesisShardConsumer consumer) {
|
||||||
if (consumer.getShutdownNotification() != null) {
|
if (consumer.getShutdownNotification() != null) {
|
||||||
consumer.getShutdownNotification().shutdownComplete();
|
consumer.getShutdownNotification().shutdownComplete();
|
||||||
}
|
}
|
||||||
|
|
@ -39,7 +39,7 @@ import lombok.Data;
|
||||||
/**
|
/**
|
||||||
* Used to get data from Amazon Kinesis. Tracks iterator state internally.
|
* Used to get data from Amazon Kinesis. Tracks iterator state internally.
|
||||||
*/
|
*/
|
||||||
class KinesisDataFetcher {
|
public class KinesisDataFetcher implements IDataFetcher{
|
||||||
|
|
||||||
private static final Log LOG = LogFactory.getLog(KinesisDataFetcher.class);
|
private static final Log LOG = LogFactory.getLog(KinesisDataFetcher.class);
|
||||||
|
|
||||||
|
|
@ -185,7 +185,7 @@ class KinesisDataFetcher {
|
||||||
* @param sequenceNumber advance the iterator to the record at this sequence number.
|
* @param sequenceNumber advance the iterator to the record at this sequence number.
|
||||||
* @param initialPositionInStream The initialPositionInStream.
|
* @param initialPositionInStream The initialPositionInStream.
|
||||||
*/
|
*/
|
||||||
void advanceIteratorTo(String sequenceNumber, InitialPositionInStreamExtended initialPositionInStream) {
|
public void advanceIteratorTo(String sequenceNumber, InitialPositionInStreamExtended initialPositionInStream) {
|
||||||
if (sequenceNumber == null) {
|
if (sequenceNumber == null) {
|
||||||
throw new IllegalArgumentException("SequenceNumber should not be null: shardId " + shardId);
|
throw new IllegalArgumentException("SequenceNumber should not be null: shardId " + shardId);
|
||||||
} else if (sequenceNumber.equals(SentinelCheckpoint.LATEST.toString())) {
|
} else if (sequenceNumber.equals(SentinelCheckpoint.LATEST.toString())) {
|
||||||
|
|
@ -276,11 +276,11 @@ class KinesisDataFetcher {
|
||||||
/**
|
/**
|
||||||
* @return the shardEndReached
|
* @return the shardEndReached
|
||||||
*/
|
*/
|
||||||
protected boolean isShardEndReached() {
|
public boolean isShardEndReached() {
|
||||||
return isShardEndReached;
|
return isShardEndReached;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<ChildShard> getChildShards() {
|
public List<ChildShard> getChildShards() {
|
||||||
return childShards;
|
return childShards;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -290,5 +290,4 @@ class KinesisDataFetcher {
|
||||||
String getNextIterator() {
|
String getNextIterator() {
|
||||||
return nextIterator;
|
return nextIterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,403 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 Amazon.com, Inc. or its affiliates.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.amazonaws.services.kinesis.clientlibrary.lib.worker;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.amazonaws.services.cloudwatch.model.StandardUnit;
|
||||||
|
import com.amazonaws.services.kinesis.clientlibrary.proxies.IKinesisProxy;
|
||||||
|
import com.amazonaws.services.kinesis.leases.exceptions.DependencyException;
|
||||||
|
import com.amazonaws.services.kinesis.leases.exceptions.InvalidStateException;
|
||||||
|
import com.amazonaws.services.kinesis.leases.exceptions.ProvisionedThroughputException;
|
||||||
|
import com.amazonaws.services.kinesis.leases.impl.HashKeyRangeForLease;
|
||||||
|
import com.amazonaws.services.kinesis.leases.impl.KinesisClientLease;
|
||||||
|
import com.amazonaws.services.kinesis.leases.impl.UpdateField;
|
||||||
|
import com.amazonaws.services.kinesis.leases.interfaces.ILeaseManager;
|
||||||
|
import com.amazonaws.services.kinesis.metrics.impl.MetricsHelper;
|
||||||
|
import com.amazonaws.services.kinesis.metrics.interfaces.IMetricsFactory;
|
||||||
|
import com.amazonaws.services.kinesis.metrics.interfaces.MetricsLevel;
|
||||||
|
import com.amazonaws.services.kinesis.model.Shard;
|
||||||
|
import com.amazonaws.util.CollectionUtils;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.collect.ComparisonChain;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import lombok.Value;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import static com.amazonaws.services.kinesis.leases.impl.HashKeyRangeForLease.fromHashKeyRange;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The top level orchestrator for coordinating the periodic shard sync related activities. If the configured
|
||||||
|
* {@link ShardSyncStrategyType} is PERIODIC, this class will be the main shard sync orchestrator. For non-PERIODIC
|
||||||
|
* strategies, this class will serve as an internal auditor that periodically checks if the full hash range is covered
|
||||||
|
* by currently held leases, and initiates a recovery shard sync if not.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@EqualsAndHashCode
|
||||||
|
class KinesisPeriodicShardSyncManager implements IPeriodicShardSyncManager{
|
||||||
|
private static final Log LOG = LogFactory.getLog(KinesisPeriodicShardSyncManager.class);
|
||||||
|
private static final long INITIAL_DELAY = 0;
|
||||||
|
|
||||||
|
/** DEFAULT interval is used for PERIODIC {@link ShardSyncStrategyType}. */
|
||||||
|
|
||||||
|
private static final long DEFAULT_PERIODIC_SHARD_SYNC_INTERVAL_MILLIS = 1000L;
|
||||||
|
|
||||||
|
/** Parameters for validating hash range completeness when running in auditor mode. */
|
||||||
|
@VisibleForTesting
|
||||||
|
static final BigInteger MIN_HASH_KEY = BigInteger.ZERO;
|
||||||
|
@VisibleForTesting
|
||||||
|
static final BigInteger MAX_HASH_KEY = new BigInteger("2").pow(128).subtract(BigInteger.ONE);
|
||||||
|
static final String PERIODIC_SHARD_SYNC_MANAGER = "PeriodicShardSyncManager";
|
||||||
|
private final HashRangeHoleTracker hashRangeHoleTracker = new HashRangeHoleTracker();
|
||||||
|
|
||||||
|
private final String workerId;
|
||||||
|
private final LeaderDecider leaderDecider;
|
||||||
|
private final ITask metricsEmittingShardSyncTask;
|
||||||
|
private final ScheduledExecutorService shardSyncThreadPool;
|
||||||
|
private final ILeaseManager<KinesisClientLease> leaseManager;
|
||||||
|
private final IKinesisProxy kinesisProxy;
|
||||||
|
private final boolean isAuditorMode;
|
||||||
|
private final long periodicShardSyncIntervalMillis;
|
||||||
|
private boolean isRunning;
|
||||||
|
private final IMetricsFactory metricsFactory;
|
||||||
|
private final int leasesRecoveryAuditorInconsistencyConfidenceThreshold;
|
||||||
|
|
||||||
|
|
||||||
|
KinesisPeriodicShardSyncManager(String workerId,
|
||||||
|
LeaderDecider leaderDecider,
|
||||||
|
ShardSyncTask shardSyncTask,
|
||||||
|
IMetricsFactory metricsFactory,
|
||||||
|
ILeaseManager<KinesisClientLease> leaseManager,
|
||||||
|
IKinesisProxy kinesisProxy,
|
||||||
|
boolean isAuditorMode,
|
||||||
|
long leasesRecoveryAuditorExecutionFrequencyMillis,
|
||||||
|
int leasesRecoveryAuditorInconsistencyConfidenceThreshold) {
|
||||||
|
this(workerId, leaderDecider, shardSyncTask, Executors.newSingleThreadScheduledExecutor(), metricsFactory,
|
||||||
|
leaseManager, kinesisProxy, isAuditorMode, leasesRecoveryAuditorExecutionFrequencyMillis,
|
||||||
|
leasesRecoveryAuditorInconsistencyConfidenceThreshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
KinesisPeriodicShardSyncManager(String workerId,
|
||||||
|
LeaderDecider leaderDecider,
|
||||||
|
ShardSyncTask shardSyncTask,
|
||||||
|
ScheduledExecutorService shardSyncThreadPool,
|
||||||
|
IMetricsFactory metricsFactory,
|
||||||
|
ILeaseManager<KinesisClientLease> leaseManager,
|
||||||
|
IKinesisProxy kinesisProxy,
|
||||||
|
boolean isAuditorMode,
|
||||||
|
long leasesRecoveryAuditorExecutionFrequencyMillis,
|
||||||
|
int leasesRecoveryAuditorInconsistencyConfidenceThreshold) {
|
||||||
|
Validate.notBlank(workerId, "WorkerID is required to initialize PeriodicShardSyncManager.");
|
||||||
|
Validate.notNull(leaderDecider, "LeaderDecider is required to initialize PeriodicShardSyncManager.");
|
||||||
|
Validate.notNull(shardSyncTask, "ShardSyncTask is required to initialize PeriodicShardSyncManager.");
|
||||||
|
this.workerId = workerId;
|
||||||
|
this.leaderDecider = leaderDecider;
|
||||||
|
this.metricsEmittingShardSyncTask = new MetricsCollectingTaskDecorator(shardSyncTask, metricsFactory);
|
||||||
|
this.shardSyncThreadPool = shardSyncThreadPool;
|
||||||
|
this.leaseManager = leaseManager;
|
||||||
|
this.kinesisProxy = kinesisProxy;
|
||||||
|
this.metricsFactory = metricsFactory;
|
||||||
|
this.isAuditorMode = isAuditorMode;
|
||||||
|
this.leasesRecoveryAuditorInconsistencyConfidenceThreshold = leasesRecoveryAuditorInconsistencyConfidenceThreshold;
|
||||||
|
if (isAuditorMode) {
|
||||||
|
Validate.notNull(this.leaseManager, "LeaseManager is required for non-PERIODIC shard sync strategies.");
|
||||||
|
Validate.notNull(this.kinesisProxy, "KinesisProxy is required for non-PERIODIC shard sync strategies.");
|
||||||
|
this.periodicShardSyncIntervalMillis = leasesRecoveryAuditorExecutionFrequencyMillis;
|
||||||
|
} else {
|
||||||
|
this.periodicShardSyncIntervalMillis = DEFAULT_PERIODIC_SHARD_SYNC_INTERVAL_MILLIS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized TaskResult start() {
|
||||||
|
if (!isRunning) {
|
||||||
|
final Runnable periodicShardSyncer = () -> {
|
||||||
|
try {
|
||||||
|
runShardSync();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
LOG.error("Error running shard sync.", t);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
shardSyncThreadPool
|
||||||
|
.scheduleWithFixedDelay(periodicShardSyncer, INITIAL_DELAY, periodicShardSyncIntervalMillis,
|
||||||
|
TimeUnit.MILLISECONDS);
|
||||||
|
isRunning = true;
|
||||||
|
}
|
||||||
|
return new TaskResult(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs ShardSync once, without scheduling further periodic ShardSyncs.
|
||||||
|
* @return TaskResult from shard sync
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public synchronized TaskResult syncShardsOnce() {
|
||||||
|
LOG.info("Syncing shards once from worker " + workerId);
|
||||||
|
return metricsEmittingShardSyncTask.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
if (isRunning) {
|
||||||
|
LOG.info(String.format("Shutting down leader decider on worker %s", workerId));
|
||||||
|
leaderDecider.shutdown();
|
||||||
|
LOG.info(String.format("Shutting down periodic shard sync task scheduler on worker %s", workerId));
|
||||||
|
shardSyncThreadPool.shutdown();
|
||||||
|
isRunning = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runShardSync() {
|
||||||
|
if (leaderDecider.isLeader(workerId)) {
|
||||||
|
LOG.debug("WorkerId " + workerId + " is a leader, running the shard sync task");
|
||||||
|
|
||||||
|
MetricsHelper.startScope(metricsFactory, PERIODIC_SHARD_SYNC_MANAGER);
|
||||||
|
boolean isRunSuccess = false;
|
||||||
|
final long runStartMillis = System.currentTimeMillis();
|
||||||
|
|
||||||
|
try {
|
||||||
|
final ShardSyncResponse shardSyncResponse = checkForShardSync();
|
||||||
|
MetricsHelper.getMetricsScope().addData("NumStreamsToSync", shardSyncResponse.shouldDoShardSync() ? 1 : 0, StandardUnit.Count, MetricsLevel.SUMMARY);
|
||||||
|
MetricsHelper.getMetricsScope().addData("NumStreamsWithPartialLeases", shardSyncResponse.isHoleDetected() ? 1 : 0, StandardUnit.Count, MetricsLevel.SUMMARY);
|
||||||
|
if (shardSyncResponse.shouldDoShardSync()) {
|
||||||
|
LOG.info("Periodic shard syncer initiating shard sync due to the reason - " +
|
||||||
|
shardSyncResponse.reasonForDecision());
|
||||||
|
metricsEmittingShardSyncTask.call();
|
||||||
|
} else {
|
||||||
|
LOG.info("Skipping shard sync due to the reason - " + shardSyncResponse.reasonForDecision());
|
||||||
|
}
|
||||||
|
isRunSuccess = true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Caught exception while running periodic shard syncer.", e);
|
||||||
|
} finally {
|
||||||
|
MetricsHelper.addSuccessAndLatency(runStartMillis, isRunSuccess, MetricsLevel.SUMMARY);
|
||||||
|
MetricsHelper.endScope();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG.debug("WorkerId " + workerId + " is not a leader, not running the shard sync task");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
ShardSyncResponse checkForShardSync() throws DependencyException, InvalidStateException,
|
||||||
|
ProvisionedThroughputException {
|
||||||
|
|
||||||
|
if (!isAuditorMode) {
|
||||||
|
// If we are running with PERIODIC shard sync strategy, we should sync every time.
|
||||||
|
return new ShardSyncResponse(true, false, "Syncing every time with PERIODIC shard sync strategy.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current leases from DynamoDB.
|
||||||
|
final List<KinesisClientLease> currentLeases = leaseManager.listLeases();
|
||||||
|
|
||||||
|
if (CollectionUtils.isNullOrEmpty(currentLeases)) {
|
||||||
|
// If the current leases are null or empty, then we need to initiate a shard sync.
|
||||||
|
LOG.info("No leases found. Will trigger a shard sync.");
|
||||||
|
return new ShardSyncResponse(true, false, "No leases found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if there are any holes in the hash range covered by current leases. Return the first hole if present.
|
||||||
|
Optional<HashRangeHole> hashRangeHoleOpt = hasHoleInLeases(currentLeases);
|
||||||
|
if (hashRangeHoleOpt.isPresent()) {
|
||||||
|
// If hole is present, check if the hole is detected consecutively in previous occurrences. If hole is
|
||||||
|
// determined with high confidence, return true; return false otherwise. We use the high confidence factor
|
||||||
|
// to avoid shard sync on any holes during resharding and lease cleanups, or other intermittent issues.
|
||||||
|
final boolean hasHoleWithHighConfidence =
|
||||||
|
hashRangeHoleTracker.hashHighConfidenceOfHoleWith(hashRangeHoleOpt.get());
|
||||||
|
|
||||||
|
return new ShardSyncResponse(hasHoleWithHighConfidence, true,
|
||||||
|
"Detected the same hole for " + hashRangeHoleTracker.getNumConsecutiveHoles() + " times. " +
|
||||||
|
"Will initiate shard sync after reaching threshold: " + leasesRecoveryAuditorInconsistencyConfidenceThreshold);
|
||||||
|
} else {
|
||||||
|
// If hole is not present, clear any previous hole tracking and return false.
|
||||||
|
hashRangeHoleTracker.reset();
|
||||||
|
return new ShardSyncResponse(false, false, "Hash range is complete.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
Optional<HashRangeHole> hasHoleInLeases(List<KinesisClientLease> leases) {
|
||||||
|
// Filter out any leases with checkpoints other than SHARD_END
|
||||||
|
final List<KinesisClientLease> activeLeases = leases.stream()
|
||||||
|
.filter(lease -> lease.getCheckpoint() != null && !lease.getCheckpoint().isShardEnd())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
final List<KinesisClientLease> activeLeasesWithHashRanges = fillWithHashRangesIfRequired(activeLeases);
|
||||||
|
return checkForHoleInHashKeyRanges(activeLeasesWithHashRanges);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<KinesisClientLease> fillWithHashRangesIfRequired(List<KinesisClientLease> activeLeases) {
|
||||||
|
final List<KinesisClientLease> activeLeasesWithNoHashRanges = activeLeases.stream()
|
||||||
|
.filter(lease -> lease.getHashKeyRange() == null).collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (activeLeasesWithNoHashRanges.isEmpty()) {
|
||||||
|
return activeLeases;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch shards from Kinesis to fill in the in-memory hash ranges
|
||||||
|
final Map<String, Shard> kinesisShards = kinesisProxy.getShardList().stream()
|
||||||
|
.collect(Collectors.toMap(Shard::getShardId, shard -> shard));
|
||||||
|
|
||||||
|
return activeLeases.stream().map(lease -> {
|
||||||
|
if (lease.getHashKeyRange() == null) {
|
||||||
|
final String shardId = lease.getLeaseKey();
|
||||||
|
final Shard shard = kinesisShards.get(shardId);
|
||||||
|
if (shard == null) {
|
||||||
|
return lease;
|
||||||
|
}
|
||||||
|
lease.setHashKeyRange(fromHashKeyRange(shard.getHashKeyRange()));
|
||||||
|
|
||||||
|
try {
|
||||||
|
leaseManager.updateLeaseWithMetaInfo(lease, UpdateField.HASH_KEY_RANGE);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.warn("Unable to update hash range information for lease " + lease.getLeaseKey() +
|
||||||
|
". This may result in explicit lease sync.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lease;
|
||||||
|
}).filter(lease -> lease.getHashKeyRange() != null).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static Optional<HashRangeHole> checkForHoleInHashKeyRanges(List<KinesisClientLease> leasesWithHashKeyRanges) {
|
||||||
|
// Sort the hash ranges by starting hash key
|
||||||
|
final List<KinesisClientLease> sortedLeasesWithHashKeyRanges = sortLeasesByHashRange(leasesWithHashKeyRanges);
|
||||||
|
if (sortedLeasesWithHashKeyRanges.isEmpty()) {
|
||||||
|
LOG.error("No leases with valid hash ranges found.");
|
||||||
|
return Optional.of(new HashRangeHole());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the hash range bounds
|
||||||
|
final KinesisClientLease minHashKeyLease = sortedLeasesWithHashKeyRanges.get(0);
|
||||||
|
final KinesisClientLease maxHashKeyLease =
|
||||||
|
sortedLeasesWithHashKeyRanges.get(sortedLeasesWithHashKeyRanges.size() - 1);
|
||||||
|
if (!minHashKeyLease.getHashKeyRange().startingHashKey().equals(MIN_HASH_KEY) ||
|
||||||
|
!maxHashKeyLease.getHashKeyRange().endingHashKey().equals(MAX_HASH_KEY)) {
|
||||||
|
LOG.error("Incomplete hash range found between " + minHashKeyLease + " and " + maxHashKeyLease);
|
||||||
|
return Optional.of(new HashRangeHole(minHashKeyLease.getHashKeyRange(), maxHashKeyLease.getHashKeyRange()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for any holes in the sorted hash range intervals
|
||||||
|
if (sortedLeasesWithHashKeyRanges.size() > 1) {
|
||||||
|
KinesisClientLease leftmostLeaseToReportInCaseOfHole = minHashKeyLease;
|
||||||
|
HashKeyRangeForLease leftLeaseHashRange = leftmostLeaseToReportInCaseOfHole.getHashKeyRange();
|
||||||
|
|
||||||
|
for (int i = 1; i < sortedLeasesWithHashKeyRanges.size(); i++) {
|
||||||
|
final KinesisClientLease rightLease = sortedLeasesWithHashKeyRanges.get(i);
|
||||||
|
final HashKeyRangeForLease rightLeaseHashRange = rightLease.getHashKeyRange();
|
||||||
|
final BigInteger rangeDiff =
|
||||||
|
rightLeaseHashRange.startingHashKey().subtract(leftLeaseHashRange.endingHashKey());
|
||||||
|
// We have overlapping leases when rangeDiff is 0 or negative.
|
||||||
|
// signum() will be -1 for negative and 0 if value is 0.
|
||||||
|
// Merge the ranges for further tracking.
|
||||||
|
if (rangeDiff.signum() <= 0) {
|
||||||
|
leftLeaseHashRange = new HashKeyRangeForLease(leftLeaseHashRange.startingHashKey(),
|
||||||
|
leftLeaseHashRange.endingHashKey().max(rightLeaseHashRange.endingHashKey()));
|
||||||
|
} else {
|
||||||
|
// We have non-overlapping leases when rangeDiff is positive. signum() will be 1 in this case.
|
||||||
|
// If rangeDiff is 1, then it is a continuous hash range. If not, there is a hole.
|
||||||
|
if (!rangeDiff.equals(BigInteger.ONE)) {
|
||||||
|
LOG.error("Incomplete hash range found between " + leftmostLeaseToReportInCaseOfHole +
|
||||||
|
" and " + rightLease);
|
||||||
|
return Optional.of(new HashRangeHole(leftmostLeaseToReportInCaseOfHole.getHashKeyRange(),
|
||||||
|
rightLease.getHashKeyRange()));
|
||||||
|
}
|
||||||
|
|
||||||
|
leftmostLeaseToReportInCaseOfHole = rightLease;
|
||||||
|
leftLeaseHashRange = rightLeaseHashRange;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static List<KinesisClientLease> sortLeasesByHashRange(List<KinesisClientLease> leasesWithHashKeyRanges) {
|
||||||
|
if (leasesWithHashKeyRanges.size() == 0 || leasesWithHashKeyRanges.size() == 1) {
|
||||||
|
return leasesWithHashKeyRanges;
|
||||||
|
}
|
||||||
|
Collections.sort(leasesWithHashKeyRanges, new HashKeyRangeComparator());
|
||||||
|
return leasesWithHashKeyRanges;
|
||||||
|
}
|
||||||
|
@Value
|
||||||
|
private static class HashRangeHole {
|
||||||
|
private final HashKeyRangeForLease hashRangeAtStartOfPossibleHole;
|
||||||
|
private final HashKeyRangeForLease hashRangeAtEndOfPossibleHole;
|
||||||
|
|
||||||
|
HashRangeHole() {
|
||||||
|
hashRangeAtStartOfPossibleHole = hashRangeAtEndOfPossibleHole = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashRangeHole(HashKeyRangeForLease hashRangeAtStartOfPossibleHole,
|
||||||
|
HashKeyRangeForLease hashRangeAtEndOfPossibleHole) {
|
||||||
|
this.hashRangeAtStartOfPossibleHole = hashRangeAtStartOfPossibleHole;
|
||||||
|
this.hashRangeAtEndOfPossibleHole = hashRangeAtEndOfPossibleHole;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class HashRangeHoleTracker {
|
||||||
|
private HashRangeHole hashRangeHole;
|
||||||
|
@Getter
|
||||||
|
private Integer numConsecutiveHoles;
|
||||||
|
|
||||||
|
public boolean hashHighConfidenceOfHoleWith(@NonNull HashRangeHole hashRangeHole) {
|
||||||
|
if (hashRangeHole.equals(this.hashRangeHole)) {
|
||||||
|
++this.numConsecutiveHoles;
|
||||||
|
} else {
|
||||||
|
this.hashRangeHole = hashRangeHole;
|
||||||
|
this.numConsecutiveHoles = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return numConsecutiveHoles >= leasesRecoveryAuditorInconsistencyConfidenceThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
this.hashRangeHole = null;
|
||||||
|
this.numConsecutiveHoles = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class HashKeyRangeComparator implements Comparator<KinesisClientLease>, Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(KinesisClientLease lease, KinesisClientLease otherLease) {
|
||||||
|
Validate.notNull(lease);
|
||||||
|
Validate.notNull(otherLease);
|
||||||
|
Validate.notNull(lease.getHashKeyRange());
|
||||||
|
Validate.notNull(otherLease.getHashKeyRange());
|
||||||
|
return ComparisonChain.start()
|
||||||
|
.compare(lease.getHashKeyRange().startingHashKey(), otherLease.getHashKeyRange().startingHashKey())
|
||||||
|
.compare(lease.getHashKeyRange().endingHashKey(), otherLease.getHashKeyRange().endingHashKey())
|
||||||
|
.result();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,7 +14,6 @@
|
||||||
*/
|
*/
|
||||||
package com.amazonaws.services.kinesis.clientlibrary.lib.worker;
|
package com.amazonaws.services.kinesis.clientlibrary.lib.worker;
|
||||||
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
@ -35,7 +34,6 @@ import com.amazonaws.services.kinesis.leases.impl.KinesisClientLease;
|
||||||
import com.amazonaws.services.kinesis.leases.interfaces.ILeaseManager;
|
import com.amazonaws.services.kinesis.leases.interfaces.ILeaseManager;
|
||||||
import com.amazonaws.services.kinesis.metrics.interfaces.IMetricsFactory;
|
import com.amazonaws.services.kinesis.metrics.interfaces.IMetricsFactory;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -43,9 +41,9 @@ import lombok.Getter;
|
||||||
* The instance should be shutdown when we lose the primary responsibility for a shard.
|
* The instance should be shutdown when we lose the primary responsibility for a shard.
|
||||||
* A new instance should be created if the primary responsibility is reassigned back to this process.
|
* A new instance should be created if the primary responsibility is reassigned back to this process.
|
||||||
*/
|
*/
|
||||||
class ShardConsumer {
|
public class KinesisShardConsumer implements IShardConsumer{
|
||||||
|
|
||||||
private static final Log LOG = LogFactory.getLog(ShardConsumer.class);
|
private static final Log LOG = LogFactory.getLog(KinesisShardConsumer.class);
|
||||||
|
|
||||||
private final StreamConfig streamConfig;
|
private final StreamConfig streamConfig;
|
||||||
private final IRecordProcessor recordProcessor;
|
private final IRecordProcessor recordProcessor;
|
||||||
|
|
@ -78,7 +76,7 @@ class ShardConsumer {
|
||||||
@Getter
|
@Getter
|
||||||
private final GetRecordsCache getRecordsCache;
|
private final GetRecordsCache getRecordsCache;
|
||||||
|
|
||||||
private static final GetRecordsRetrievalStrategy makeStrategy(KinesisDataFetcher dataFetcher,
|
private static final GetRecordsRetrievalStrategy makeStrategy(IDataFetcher dataFetcher,
|
||||||
Optional<Integer> retryGetRecordsInSeconds,
|
Optional<Integer> retryGetRecordsInSeconds,
|
||||||
Optional<Integer> maxGetRecordsThreadPool,
|
Optional<Integer> maxGetRecordsThreadPool,
|
||||||
ShardInfo shardInfo) {
|
ShardInfo shardInfo) {
|
||||||
|
|
@ -93,7 +91,7 @@ class ShardConsumer {
|
||||||
* Tracks current state. It is only updated via the consumeStream/shutdown APIs. Therefore we don't do
|
* Tracks current state. It is only updated via the consumeStream/shutdown APIs. Therefore we don't do
|
||||||
* much coordination/synchronization to handle concurrent reads/updates.
|
* much coordination/synchronization to handle concurrent reads/updates.
|
||||||
*/
|
*/
|
||||||
private ConsumerStates.ConsumerState currentState = ConsumerStates.INITIAL_STATE;
|
private KinesisConsumerStates.ConsumerState currentState = KinesisConsumerStates.INITIAL_STATE;
|
||||||
/*
|
/*
|
||||||
* Used to track if we lost the primary responsibility. Once set to true, we will start shutting down.
|
* Used to track if we lost the primary responsibility. Once set to true, we will start shutting down.
|
||||||
* If we regain primary responsibility before shutdown is complete, Worker should create a new ShardConsumer object.
|
* If we regain primary responsibility before shutdown is complete, Worker should create a new ShardConsumer object.
|
||||||
|
|
@ -116,7 +114,7 @@ class ShardConsumer {
|
||||||
*/
|
*/
|
||||||
// CHECKSTYLE:IGNORE ParameterNumber FOR NEXT 10 LINES
|
// CHECKSTYLE:IGNORE ParameterNumber FOR NEXT 10 LINES
|
||||||
@Deprecated
|
@Deprecated
|
||||||
ShardConsumer(ShardInfo shardInfo,
|
KinesisShardConsumer(ShardInfo shardInfo,
|
||||||
StreamConfig streamConfig,
|
StreamConfig streamConfig,
|
||||||
ICheckpoint checkpoint,
|
ICheckpoint checkpoint,
|
||||||
IRecordProcessor recordProcessor,
|
IRecordProcessor recordProcessor,
|
||||||
|
|
@ -162,7 +160,7 @@ class ShardConsumer {
|
||||||
*/
|
*/
|
||||||
// CHECKSTYLE:IGNORE ParameterNumber FOR NEXT 10 LINES
|
// CHECKSTYLE:IGNORE ParameterNumber FOR NEXT 10 LINES
|
||||||
@Deprecated
|
@Deprecated
|
||||||
ShardConsumer(ShardInfo shardInfo,
|
KinesisShardConsumer(ShardInfo shardInfo,
|
||||||
StreamConfig streamConfig,
|
StreamConfig streamConfig,
|
||||||
ICheckpoint checkpoint,
|
ICheckpoint checkpoint,
|
||||||
IRecordProcessor recordProcessor,
|
IRecordProcessor recordProcessor,
|
||||||
|
|
@ -223,7 +221,7 @@ class ShardConsumer {
|
||||||
* @param shardSyncer shardSyncer instance used to check and create new leases
|
* @param shardSyncer shardSyncer instance used to check and create new leases
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
ShardConsumer(ShardInfo shardInfo,
|
KinesisShardConsumer(ShardInfo shardInfo,
|
||||||
StreamConfig streamConfig,
|
StreamConfig streamConfig,
|
||||||
ICheckpoint checkpoint,
|
ICheckpoint checkpoint,
|
||||||
IRecordProcessor recordProcessor,
|
IRecordProcessor recordProcessor,
|
||||||
|
|
@ -243,7 +241,7 @@ class ShardConsumer {
|
||||||
this(shardInfo, streamConfig, checkpoint, recordProcessor, recordProcessorCheckpointer, leaseCoordinator,
|
this(shardInfo, streamConfig, checkpoint, recordProcessor, recordProcessorCheckpointer, leaseCoordinator,
|
||||||
parentShardPollIntervalMillis, cleanupLeasesOfCompletedShards, executorService, metricsFactory,
|
parentShardPollIntervalMillis, cleanupLeasesOfCompletedShards, executorService, metricsFactory,
|
||||||
backoffTimeMillis, skipShardSyncAtWorkerInitializationIfLeasesExist, kinesisDataFetcher, retryGetRecordsInSeconds,
|
backoffTimeMillis, skipShardSyncAtWorkerInitializationIfLeasesExist, kinesisDataFetcher, retryGetRecordsInSeconds,
|
||||||
maxGetRecordsThreadPool, config, shardSyncer, shardSyncStrategy, LeaseCleanupManager.createOrGetInstance(streamConfig.getStreamProxy(), leaseCoordinator.getLeaseManager(),
|
maxGetRecordsThreadPool, config, shardSyncer, shardSyncStrategy, LeaseCleanupManager.newInstance(streamConfig.getStreamProxy(), leaseCoordinator.getLeaseManager(),
|
||||||
Executors.newSingleThreadScheduledExecutor(), metricsFactory, config.shouldCleanupLeasesUponShardCompletion(),
|
Executors.newSingleThreadScheduledExecutor(), metricsFactory, config.shouldCleanupLeasesUponShardCompletion(),
|
||||||
config.leaseCleanupIntervalMillis(), config.completedLeaseCleanupThresholdMillis(),
|
config.leaseCleanupIntervalMillis(), config.completedLeaseCleanupThresholdMillis(),
|
||||||
config.garbageLeaseCleanupThresholdMillis(), config.getMaxRecords()));
|
config.garbageLeaseCleanupThresholdMillis(), config.getMaxRecords()));
|
||||||
|
|
@ -269,23 +267,23 @@ class ShardConsumer {
|
||||||
* @param shardSyncer shardSyncer instance used to check and create new leases
|
* @param shardSyncer shardSyncer instance used to check and create new leases
|
||||||
* @param leaseCleanupManager used to clean up leases in lease table.
|
* @param leaseCleanupManager used to clean up leases in lease table.
|
||||||
*/
|
*/
|
||||||
ShardConsumer(ShardInfo shardInfo,
|
KinesisShardConsumer(ShardInfo shardInfo,
|
||||||
StreamConfig streamConfig,
|
StreamConfig streamConfig,
|
||||||
ICheckpoint checkpoint,
|
ICheckpoint checkpoint,
|
||||||
IRecordProcessor recordProcessor,
|
IRecordProcessor recordProcessor,
|
||||||
RecordProcessorCheckpointer recordProcessorCheckpointer,
|
RecordProcessorCheckpointer recordProcessorCheckpointer,
|
||||||
KinesisClientLibLeaseCoordinator leaseCoordinator,
|
KinesisClientLibLeaseCoordinator leaseCoordinator,
|
||||||
long parentShardPollIntervalMillis,
|
long parentShardPollIntervalMillis,
|
||||||
boolean cleanupLeasesOfCompletedShards,
|
boolean cleanupLeasesOfCompletedShards,
|
||||||
ExecutorService executorService,
|
ExecutorService executorService,
|
||||||
IMetricsFactory metricsFactory,
|
IMetricsFactory metricsFactory,
|
||||||
long backoffTimeMillis,
|
long backoffTimeMillis,
|
||||||
boolean skipShardSyncAtWorkerInitializationIfLeasesExist,
|
boolean skipShardSyncAtWorkerInitializationIfLeasesExist,
|
||||||
KinesisDataFetcher kinesisDataFetcher,
|
KinesisDataFetcher kinesisDataFetcher,
|
||||||
Optional<Integer> retryGetRecordsInSeconds,
|
Optional<Integer> retryGetRecordsInSeconds,
|
||||||
Optional<Integer> maxGetRecordsThreadPool,
|
Optional<Integer> maxGetRecordsThreadPool,
|
||||||
KinesisClientLibConfiguration config, ShardSyncer shardSyncer, ShardSyncStrategy shardSyncStrategy,
|
KinesisClientLibConfiguration config, ShardSyncer shardSyncer, ShardSyncStrategy shardSyncStrategy,
|
||||||
LeaseCleanupManager leaseCleanupManager) {
|
LeaseCleanupManager leaseCleanupManager) {
|
||||||
this.shardInfo = shardInfo;
|
this.shardInfo = shardInfo;
|
||||||
this.streamConfig = streamConfig;
|
this.streamConfig = streamConfig;
|
||||||
this.checkpoint = checkpoint;
|
this.checkpoint = checkpoint;
|
||||||
|
|
@ -314,7 +312,7 @@ class ShardConsumer {
|
||||||
*
|
*
|
||||||
* @return true if a new process task was submitted, false otherwise
|
* @return true if a new process task was submitted, false otherwise
|
||||||
*/
|
*/
|
||||||
synchronized boolean consumeShard() {
|
public synchronized boolean consumeShard() {
|
||||||
return checkAndSubmitNextTask();
|
return checkAndSubmitNextTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -373,10 +371,6 @@ class ShardConsumer {
|
||||||
return skipShardSyncAtWorkerInitializationIfLeasesExist;
|
return skipShardSyncAtWorkerInitializationIfLeasesExist;
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum TaskOutcome {
|
|
||||||
SUCCESSFUL, END_OF_SHARD, NOT_COMPLETE, FAILURE
|
|
||||||
}
|
|
||||||
|
|
||||||
private TaskOutcome determineTaskOutcome() {
|
private TaskOutcome determineTaskOutcome() {
|
||||||
try {
|
try {
|
||||||
TaskResult result = future.get();
|
TaskResult result = future.get();
|
||||||
|
|
@ -391,6 +385,10 @@ class ShardConsumer {
|
||||||
return TaskOutcome.SUCCESSFUL;
|
return TaskOutcome.SUCCESSFUL;
|
||||||
}
|
}
|
||||||
logTaskException(result);
|
logTaskException(result);
|
||||||
|
// This is the case of result with exception
|
||||||
|
if (result.isLeaseNotFound()) {
|
||||||
|
return TaskOutcome.LEASE_NOT_FOUND;
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
} finally {
|
} finally {
|
||||||
|
|
@ -419,7 +417,7 @@ class ShardConsumer {
|
||||||
*
|
*
|
||||||
* @param shutdownNotification used to signal that the record processor has been given the chance to shutdown.
|
* @param shutdownNotification used to signal that the record processor has been given the chance to shutdown.
|
||||||
*/
|
*/
|
||||||
void notifyShutdownRequested(ShutdownNotification shutdownNotification) {
|
public void notifyShutdownRequested(ShutdownNotification shutdownNotification) {
|
||||||
this.shutdownNotification = shutdownNotification;
|
this.shutdownNotification = shutdownNotification;
|
||||||
markForShutdown(ShutdownReason.REQUESTED);
|
markForShutdown(ShutdownReason.REQUESTED);
|
||||||
}
|
}
|
||||||
|
|
@ -430,7 +428,7 @@ class ShardConsumer {
|
||||||
*
|
*
|
||||||
* @return true if shutdown is complete (false if shutdown is still in progress)
|
* @return true if shutdown is complete (false if shutdown is still in progress)
|
||||||
*/
|
*/
|
||||||
synchronized boolean beginShutdown() {
|
public synchronized boolean beginShutdown() {
|
||||||
markForShutdown(ShutdownReason.ZOMBIE);
|
markForShutdown(ShutdownReason.ZOMBIE);
|
||||||
checkAndSubmitNextTask();
|
checkAndSubmitNextTask();
|
||||||
|
|
||||||
|
|
@ -450,14 +448,14 @@ class ShardConsumer {
|
||||||
*
|
*
|
||||||
* @return true if shutdown is complete
|
* @return true if shutdown is complete
|
||||||
*/
|
*/
|
||||||
boolean isShutdown() {
|
public boolean isShutdown() {
|
||||||
return currentState.isTerminal();
|
return currentState.isTerminal();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the shutdownReason
|
* @return the shutdownReason
|
||||||
*/
|
*/
|
||||||
ShutdownReason getShutdownReason() {
|
public ShutdownReason getShutdownReason() {
|
||||||
return shutdownReason;
|
return shutdownReason;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -487,9 +485,13 @@ class ShardConsumer {
|
||||||
markForShutdown(ShutdownReason.TERMINATE);
|
markForShutdown(ShutdownReason.TERMINATE);
|
||||||
LOG.info("Shard " + shardInfo.getShardId() + ": Mark for shutdown with reason TERMINATE");
|
LOG.info("Shard " + shardInfo.getShardId() + ": Mark for shutdown with reason TERMINATE");
|
||||||
}
|
}
|
||||||
|
if (taskOutcome == TaskOutcome.LEASE_NOT_FOUND) {
|
||||||
|
markForShutdown(ShutdownReason.ZOMBIE);
|
||||||
|
LOG.info("Shard " + shardInfo.getShardId() + ": Mark for shutdown with reason ZOMBIE as lease was not found");
|
||||||
|
}
|
||||||
if (isShutdownRequested() && taskOutcome != TaskOutcome.FAILURE) {
|
if (isShutdownRequested() && taskOutcome != TaskOutcome.FAILURE) {
|
||||||
currentState = currentState.shutdownTransition(shutdownReason);
|
currentState = currentState.shutdownTransition(shutdownReason);
|
||||||
} else if (isShutdownRequested() && ConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS.equals(currentState.getState())) {
|
} else if (isShutdownRequested() && KinesisConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS.equals(currentState.getState())) {
|
||||||
currentState = currentState.shutdownTransition(shutdownReason);
|
currentState = currentState.shutdownTransition(shutdownReason);
|
||||||
} else if (taskOutcome == TaskOutcome.SUCCESSFUL) {
|
} else if (taskOutcome == TaskOutcome.SUCCESSFUL) {
|
||||||
if (currentState.getTaskType() == currentTask.getTaskType()) {
|
if (currentState.getTaskType() == currentTask.getTaskType()) {
|
||||||
|
|
@ -508,7 +510,7 @@ class ShardConsumer {
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
boolean isShutdownRequested() {
|
public boolean isShutdownRequested() {
|
||||||
return shutdownReason != null;
|
return shutdownReason != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -517,7 +519,7 @@ class ShardConsumer {
|
||||||
*
|
*
|
||||||
* @return the currentState
|
* @return the currentState
|
||||||
*/
|
*/
|
||||||
ConsumerStates.ShardConsumerState getCurrentState() {
|
public KinesisConsumerStates.ShardConsumerState getCurrentState() {
|
||||||
return currentState.getState();
|
return currentState.getState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
package com.amazonaws.services.kinesis.clientlibrary.lib.worker;
|
||||||
|
|
||||||
|
import com.amazonaws.services.kinesis.clientlibrary.interfaces.ICheckpoint;
|
||||||
|
import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessor;
|
||||||
|
import com.amazonaws.services.kinesis.leases.impl.LeaseCleanupManager;
|
||||||
|
import com.amazonaws.services.kinesis.metrics.interfaces.IMetricsFactory;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
|
public class KinesisShardConsumerFactory implements IShardConsumerFactory{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IShardConsumer createShardConsumer(ShardInfo shardInfo,
|
||||||
|
StreamConfig streamConfig,
|
||||||
|
ICheckpoint checkpointTracker,
|
||||||
|
IRecordProcessor recordProcessor,
|
||||||
|
RecordProcessorCheckpointer recordProcessorCheckpointer,
|
||||||
|
KinesisClientLibLeaseCoordinator leaseCoordinator,
|
||||||
|
long parentShardPollIntervalMillis,
|
||||||
|
boolean cleanupLeasesUponShardCompletion,
|
||||||
|
ExecutorService executorService,
|
||||||
|
IMetricsFactory metricsFactory,
|
||||||
|
long taskBackoffTimeMillis,
|
||||||
|
boolean skipShardSyncAtWorkerInitializationIfLeasesExist,
|
||||||
|
Optional<Integer> retryGetRecordsInSeconds,
|
||||||
|
Optional<Integer> maxGetRecordsThreadPool,
|
||||||
|
KinesisClientLibConfiguration config, ShardSyncer shardSyncer, ShardSyncStrategy shardSyncStrategy,
|
||||||
|
LeaseCleanupManager leaseCleanupManager) {
|
||||||
|
return new KinesisShardConsumer(shardInfo,
|
||||||
|
streamConfig,
|
||||||
|
checkpointTracker,
|
||||||
|
recordProcessor,
|
||||||
|
recordProcessorCheckpointer,
|
||||||
|
leaseCoordinator,
|
||||||
|
parentShardPollIntervalMillis,
|
||||||
|
cleanupLeasesUponShardCompletion,
|
||||||
|
executorService,
|
||||||
|
metricsFactory,
|
||||||
|
taskBackoffTimeMillis,
|
||||||
|
skipShardSyncAtWorkerInitializationIfLeasesExist,
|
||||||
|
new KinesisDataFetcher(streamConfig.getStreamProxy(), shardInfo),
|
||||||
|
retryGetRecordsInSeconds,
|
||||||
|
maxGetRecordsThreadPool,
|
||||||
|
config, shardSyncer, shardSyncStrategy,
|
||||||
|
leaseCleanupManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -45,6 +45,8 @@ import com.amazonaws.services.kinesis.metrics.impl.MetricsHelper;
|
||||||
import com.amazonaws.services.kinesis.metrics.interfaces.MetricsLevel;
|
import com.amazonaws.services.kinesis.metrics.interfaces.MetricsLevel;
|
||||||
import com.amazonaws.services.kinesis.model.Shard;
|
import com.amazonaws.services.kinesis.model.Shard;
|
||||||
|
|
||||||
|
import static com.amazonaws.services.kinesis.leases.impl.HashKeyRangeForLease.fromHashKeyRange;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class to sync leases with shards of the Kinesis stream.
|
* Helper class to sync leases with shards of the Kinesis stream.
|
||||||
* It will create new leases/activities when it discovers new Kinesis shards (bootstrap/resharding).
|
* It will create new leases/activities when it discovers new Kinesis shards (bootstrap/resharding).
|
||||||
|
|
@ -617,7 +619,7 @@ class KinesisShardSyncer implements ShardSyncer {
|
||||||
}
|
}
|
||||||
newLease.setParentShardIds(parentShardIds);
|
newLease.setParentShardIds(parentShardIds);
|
||||||
newLease.setOwnerSwitchesSinceCheckpoint(0L);
|
newLease.setOwnerSwitchesSinceCheckpoint(0L);
|
||||||
|
newLease.setHashKeyRange(fromHashKeyRange(shard.getHashKeyRange()));
|
||||||
return newLease;
|
return newLease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -641,6 +643,7 @@ class KinesisShardSyncer implements ShardSyncer {
|
||||||
newLease.setParentShardIds(parentShardIds);
|
newLease.setParentShardIds(parentShardIds);
|
||||||
newLease.setOwnerSwitchesSinceCheckpoint(0L);
|
newLease.setOwnerSwitchesSinceCheckpoint(0L);
|
||||||
newLease.setCheckpoint(ExtendedSequenceNumber.TRIM_HORIZON);
|
newLease.setCheckpoint(ExtendedSequenceNumber.TRIM_HORIZON);
|
||||||
|
newLease.setHashKeyRange(fromHashKeyRange(childShard.getHashKeyRange()));
|
||||||
return newLease;
|
return newLease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,25 +14,24 @@
|
||||||
*/
|
*/
|
||||||
package com.amazonaws.services.kinesis.clientlibrary.lib.worker;
|
package com.amazonaws.services.kinesis.clientlibrary.lib.worker;
|
||||||
|
|
||||||
import com.amazonaws.services.kinesis.leases.LeasePendingDeletion;
|
|
||||||
import com.amazonaws.services.kinesis.clientlibrary.exceptions.internal.BlockedOnParentShardException;
|
import com.amazonaws.services.kinesis.clientlibrary.exceptions.internal.BlockedOnParentShardException;
|
||||||
import com.amazonaws.services.kinesis.leases.exceptions.CustomerApplicationException;
|
|
||||||
import com.amazonaws.services.kinesis.leases.exceptions.DependencyException;
|
|
||||||
import com.amazonaws.services.kinesis.leases.exceptions.InvalidStateException;
|
|
||||||
import com.amazonaws.services.kinesis.leases.exceptions.ProvisionedThroughputException;
|
|
||||||
import com.amazonaws.services.kinesis.leases.impl.LeaseCleanupManager;
|
|
||||||
import com.amazonaws.services.kinesis.leases.impl.UpdateField;
|
|
||||||
import com.amazonaws.services.kinesis.model.ChildShard;
|
|
||||||
import com.amazonaws.util.CollectionUtils;
|
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
|
|
||||||
import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessor;
|
import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessor;
|
||||||
import com.amazonaws.services.kinesis.clientlibrary.proxies.IKinesisProxy;
|
import com.amazonaws.services.kinesis.clientlibrary.proxies.IKinesisProxy;
|
||||||
import com.amazonaws.services.kinesis.clientlibrary.types.ExtendedSequenceNumber;
|
import com.amazonaws.services.kinesis.clientlibrary.types.ExtendedSequenceNumber;
|
||||||
import com.amazonaws.services.kinesis.clientlibrary.types.ShutdownInput;
|
import com.amazonaws.services.kinesis.clientlibrary.types.ShutdownInput;
|
||||||
|
import com.amazonaws.services.kinesis.leases.LeasePendingDeletion;
|
||||||
|
import com.amazonaws.services.kinesis.leases.exceptions.CustomerApplicationException;
|
||||||
|
import com.amazonaws.services.kinesis.leases.exceptions.DependencyException;
|
||||||
|
import com.amazonaws.services.kinesis.leases.exceptions.InvalidStateException;
|
||||||
|
import com.amazonaws.services.kinesis.leases.exceptions.ProvisionedThroughputException;
|
||||||
import com.amazonaws.services.kinesis.leases.impl.KinesisClientLease;
|
import com.amazonaws.services.kinesis.leases.impl.KinesisClientLease;
|
||||||
|
import com.amazonaws.services.kinesis.leases.impl.LeaseCleanupManager;
|
||||||
|
import com.amazonaws.services.kinesis.leases.impl.UpdateField;
|
||||||
|
import com.amazonaws.services.kinesis.model.ChildShard;
|
||||||
|
import com.amazonaws.util.CollectionUtils;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
@ -44,9 +43,9 @@ import java.util.stream.Collectors;
|
||||||
/**
|
/**
|
||||||
* Task for invoking the RecordProcessor shutdown() callback.
|
* Task for invoking the RecordProcessor shutdown() callback.
|
||||||
*/
|
*/
|
||||||
class ShutdownTask implements ITask {
|
public class KinesisShutdownTask implements ITask {
|
||||||
|
|
||||||
private static final Log LOG = LogFactory.getLog(ShutdownTask.class);
|
private static final Log LOG = LogFactory.getLog(KinesisShutdownTask.class);
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final int RETRY_RANDOM_MAX_RANGE = 50;
|
static final int RETRY_RANDOM_MAX_RANGE = 50;
|
||||||
|
|
@ -72,7 +71,7 @@ class ShutdownTask implements ITask {
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*/
|
*/
|
||||||
// CHECKSTYLE:IGNORE ParameterNumber FOR NEXT 10 LINES
|
// CHECKSTYLE:IGNORE ParameterNumber FOR NEXT 10 LINES
|
||||||
ShutdownTask(ShardInfo shardInfo,
|
KinesisShutdownTask(ShardInfo shardInfo,
|
||||||
IRecordProcessor recordProcessor,
|
IRecordProcessor recordProcessor,
|
||||||
RecordProcessorCheckpointer recordProcessorCheckpointer,
|
RecordProcessorCheckpointer recordProcessorCheckpointer,
|
||||||
ShutdownReason reason,
|
ShutdownReason reason,
|
||||||
|
|
@ -222,11 +221,29 @@ class ShutdownTask implements ITask {
|
||||||
.withCheckpointer(recordProcessorCheckpointer);
|
.withCheckpointer(recordProcessorCheckpointer);
|
||||||
recordProcessor.shutdown(shardEndShutdownInput);
|
recordProcessor.shutdown(shardEndShutdownInput);
|
||||||
|
|
||||||
final ExtendedSequenceNumber lastCheckpointValue = recordProcessorCheckpointer.getLastCheckpointValue();
|
boolean successfullyCheckpointedShardEnd = false;
|
||||||
|
|
||||||
final boolean successfullyCheckpointedShardEnd = lastCheckpointValue.equals(ExtendedSequenceNumber.SHARD_END);
|
KinesisClientLease leaseFromDdb = null;
|
||||||
|
try {
|
||||||
|
leaseFromDdb = leaseCoordinator.getLeaseManager().getLease(shardInfo.getShardId());
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Shard " + shardInfo.getShardId() + " : Unable to get lease entry for shard to verify shard end checkpointing.", e);
|
||||||
|
}
|
||||||
|
|
||||||
if ((lastCheckpointValue == null) || (!successfullyCheckpointedShardEnd)) {
|
if (leaseFromDdb != null && leaseFromDdb.getCheckpoint() != null) {
|
||||||
|
successfullyCheckpointedShardEnd = leaseFromDdb.getCheckpoint().equals(ExtendedSequenceNumber.SHARD_END);
|
||||||
|
final ExtendedSequenceNumber lastCheckpointValue = recordProcessorCheckpointer.getLastCheckpointValue();
|
||||||
|
if (!leaseFromDdb.getCheckpoint().equals(lastCheckpointValue)) {
|
||||||
|
LOG.error("Shard " + shardInfo.getShardId() +
|
||||||
|
" : Checkpoint information mismatch between authoritative source and local cache. " +
|
||||||
|
"This does not affect the application flow, but cut a ticket to Kinesis when you see this. " +
|
||||||
|
"Authoritative entry : " + leaseFromDdb.getCheckpoint() + " Cache entry : " + lastCheckpointValue);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG.error("Shard " + shardInfo.getShardId() + " : No lease checkpoint entry for shard to verify shard end checkpointing. Lease Entry : " + leaseFromDdb);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!successfullyCheckpointedShardEnd) {
|
||||||
throw new IllegalArgumentException("Application didn't checkpoint at end of shard "
|
throw new IllegalArgumentException("Application didn't checkpoint at end of shard "
|
||||||
+ shardInfo.getShardId() + ". Application must checkpoint upon shutdown. " +
|
+ shardInfo.getShardId() + ". Application must checkpoint upon shutdown. " +
|
||||||
"See IRecordProcessor.shutdown javadocs for more information.");
|
"See IRecordProcessor.shutdown javadocs for more information.");
|
||||||
|
|
@ -21,7 +21,7 @@ import com.amazonaws.services.kinesis.metrics.interfaces.MetricsLevel;
|
||||||
/**
|
/**
|
||||||
* Decorates an ITask and reports metrics about its timing and success/failure.
|
* Decorates an ITask and reports metrics about its timing and success/failure.
|
||||||
*/
|
*/
|
||||||
class MetricsCollectingTaskDecorator implements ITask {
|
public class MetricsCollectingTaskDecorator implements ITask {
|
||||||
|
|
||||||
private final ITask other;
|
private final ITask other;
|
||||||
private final IMetricsFactory factory;
|
private final IMetricsFactory factory;
|
||||||
|
|
|
||||||
|
|
@ -407,4 +407,4 @@ class PeriodicShardSyncManager {
|
||||||
.result();
|
.result();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6,9 +6,9 @@ package com.amazonaws.services.kinesis.clientlibrary.lib.worker;
|
||||||
*/
|
*/
|
||||||
class PeriodicShardSyncStrategy implements ShardSyncStrategy {
|
class PeriodicShardSyncStrategy implements ShardSyncStrategy {
|
||||||
|
|
||||||
private PeriodicShardSyncManager periodicShardSyncManager;
|
private IPeriodicShardSyncManager periodicShardSyncManager;
|
||||||
|
|
||||||
PeriodicShardSyncStrategy(PeriodicShardSyncManager periodicShardSyncManager) {
|
PeriodicShardSyncStrategy(IPeriodicShardSyncManager periodicShardSyncManager) {
|
||||||
this.periodicShardSyncManager = periodicShardSyncManager;
|
this.periodicShardSyncManager = periodicShardSyncManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ public class PrefetchGetRecordsCache implements GetRecordsCache {
|
||||||
private PrefetchCounters prefetchCounters;
|
private PrefetchCounters prefetchCounters;
|
||||||
private boolean started = false;
|
private boolean started = false;
|
||||||
private final String operation;
|
private final String operation;
|
||||||
private final KinesisDataFetcher dataFetcher;
|
private final IDataFetcher dataFetcher;
|
||||||
private final String shardId;
|
private final String shardId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ import com.amazonaws.services.kinesis.model.Shard;
|
||||||
/**
|
/**
|
||||||
* Task for fetching data records and invoking processRecords() on the record processor instance.
|
* Task for fetching data records and invoking processRecords() on the record processor instance.
|
||||||
*/
|
*/
|
||||||
class ProcessTask implements ITask {
|
public class ProcessTask implements ITask {
|
||||||
|
|
||||||
private static final Log LOG = LogFactory.getLog(ProcessTask.class);
|
private static final Log LOG = LogFactory.getLog(ProcessTask.class);
|
||||||
|
|
||||||
|
|
@ -55,7 +55,7 @@ class ProcessTask implements ITask {
|
||||||
private final ShardInfo shardInfo;
|
private final ShardInfo shardInfo;
|
||||||
private final IRecordProcessor recordProcessor;
|
private final IRecordProcessor recordProcessor;
|
||||||
private final RecordProcessorCheckpointer recordProcessorCheckpointer;
|
private final RecordProcessorCheckpointer recordProcessorCheckpointer;
|
||||||
private final KinesisDataFetcher dataFetcher;
|
private final IDataFetcher dataFetcher;
|
||||||
private final TaskType taskType = TaskType.PROCESS;
|
private final TaskType taskType = TaskType.PROCESS;
|
||||||
private final StreamConfig streamConfig;
|
private final StreamConfig streamConfig;
|
||||||
private final long backoffTimeMillis;
|
private final long backoffTimeMillis;
|
||||||
|
|
@ -81,7 +81,7 @@ class ProcessTask implements ITask {
|
||||||
* The retrieval strategy for fetching records from kinesis
|
* The retrieval strategy for fetching records from kinesis
|
||||||
*/
|
*/
|
||||||
public ProcessTask(ShardInfo shardInfo, StreamConfig streamConfig, IRecordProcessor recordProcessor,
|
public ProcessTask(ShardInfo shardInfo, StreamConfig streamConfig, IRecordProcessor recordProcessor,
|
||||||
RecordProcessorCheckpointer recordProcessorCheckpointer, KinesisDataFetcher dataFetcher,
|
RecordProcessorCheckpointer recordProcessorCheckpointer, IDataFetcher dataFetcher,
|
||||||
long backoffTimeMillis, boolean skipShardSyncAtWorkerInitializationIfLeasesExist,
|
long backoffTimeMillis, boolean skipShardSyncAtWorkerInitializationIfLeasesExist,
|
||||||
GetRecordsCache getRecordsCache) {
|
GetRecordsCache getRecordsCache) {
|
||||||
this(shardInfo, streamConfig, recordProcessor, recordProcessorCheckpointer, dataFetcher, backoffTimeMillis,
|
this(shardInfo, streamConfig, recordProcessor, recordProcessorCheckpointer, dataFetcher, backoffTimeMillis,
|
||||||
|
|
@ -107,7 +107,7 @@ class ProcessTask implements ITask {
|
||||||
* determines how throttling events should be reported in the log.
|
* determines how throttling events should be reported in the log.
|
||||||
*/
|
*/
|
||||||
public ProcessTask(ShardInfo shardInfo, StreamConfig streamConfig, IRecordProcessor recordProcessor,
|
public ProcessTask(ShardInfo shardInfo, StreamConfig streamConfig, IRecordProcessor recordProcessor,
|
||||||
RecordProcessorCheckpointer recordProcessorCheckpointer, KinesisDataFetcher dataFetcher,
|
RecordProcessorCheckpointer recordProcessorCheckpointer, IDataFetcher dataFetcher,
|
||||||
long backoffTimeMillis, boolean skipShardSyncAtWorkerInitializationIfLeasesExist,
|
long backoffTimeMillis, boolean skipShardSyncAtWorkerInitializationIfLeasesExist,
|
||||||
ThrottlingReporter throttlingReporter, GetRecordsCache getRecordsCache) {
|
ThrottlingReporter throttlingReporter, GetRecordsCache getRecordsCache) {
|
||||||
super();
|
super();
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ import com.amazonaws.services.kinesis.model.Record;
|
||||||
* The Amazon Kinesis Client Library will instantiate an object and provide a reference to the application
|
* The Amazon Kinesis Client Library will instantiate an object and provide a reference to the application
|
||||||
* RecordProcessor instance. Amazon Kinesis Client Library will create one instance per shard assignment.
|
* RecordProcessor instance. Amazon Kinesis Client Library will create one instance per shard assignment.
|
||||||
*/
|
*/
|
||||||
class RecordProcessorCheckpointer implements IRecordProcessorCheckpointer {
|
public class RecordProcessorCheckpointer implements IRecordProcessorCheckpointer {
|
||||||
|
|
||||||
private static final Log LOG = LogFactory.getLog(RecordProcessorCheckpointer.class);
|
private static final Log LOG = LogFactory.getLog(RecordProcessorCheckpointer.class);
|
||||||
|
|
||||||
|
|
@ -62,7 +62,7 @@ class RecordProcessorCheckpointer implements IRecordProcessorCheckpointer {
|
||||||
* @param checkpoint Used to checkpoint progress of a RecordProcessor
|
* @param checkpoint Used to checkpoint progress of a RecordProcessor
|
||||||
* @param validator Used for validating sequence numbers
|
* @param validator Used for validating sequence numbers
|
||||||
*/
|
*/
|
||||||
RecordProcessorCheckpointer(ShardInfo shardInfo,
|
public RecordProcessorCheckpointer(ShardInfo shardInfo,
|
||||||
ICheckpoint checkpoint,
|
ICheckpoint checkpoint,
|
||||||
SequenceNumberValidator validator,
|
SequenceNumberValidator validator,
|
||||||
IMetricsFactory metricsFactory) {
|
IMetricsFactory metricsFactory) {
|
||||||
|
|
@ -231,7 +231,7 @@ class RecordProcessorCheckpointer implements IRecordProcessorCheckpointer {
|
||||||
/**
|
/**
|
||||||
* @return the lastCheckpointValue
|
* @return the lastCheckpointValue
|
||||||
*/
|
*/
|
||||||
ExtendedSequenceNumber getLastCheckpointValue() {
|
public ExtendedSequenceNumber getLastCheckpointValue() {
|
||||||
return lastCheckpointValue;
|
return lastCheckpointValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -244,14 +244,14 @@ class RecordProcessorCheckpointer implements IRecordProcessorCheckpointer {
|
||||||
*
|
*
|
||||||
* @return the largest permitted checkpoint
|
* @return the largest permitted checkpoint
|
||||||
*/
|
*/
|
||||||
synchronized ExtendedSequenceNumber getLargestPermittedCheckpointValue() {
|
public synchronized ExtendedSequenceNumber getLargestPermittedCheckpointValue() {
|
||||||
return largestPermittedCheckpointValue;
|
return largestPermittedCheckpointValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param largestPermittedCheckpointValue the largest permitted checkpoint
|
* @param largestPermittedCheckpointValue the largest permitted checkpoint
|
||||||
*/
|
*/
|
||||||
synchronized void setLargestPermittedCheckpointValue(ExtendedSequenceNumber largestPermittedCheckpointValue) {
|
public synchronized void setLargestPermittedCheckpointValue(ExtendedSequenceNumber largestPermittedCheckpointValue) {
|
||||||
this.largestPermittedCheckpointValue = largestPermittedCheckpointValue;
|
this.largestPermittedCheckpointValue = largestPermittedCheckpointValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -262,7 +262,7 @@ class RecordProcessorCheckpointer implements IRecordProcessorCheckpointer {
|
||||||
*
|
*
|
||||||
* @param extendedSequenceNumber
|
* @param extendedSequenceNumber
|
||||||
*/
|
*/
|
||||||
synchronized void setSequenceNumberAtShardEnd(ExtendedSequenceNumber extendedSequenceNumber) {
|
public synchronized void setSequenceNumberAtShardEnd(ExtendedSequenceNumber extendedSequenceNumber) {
|
||||||
this.sequenceNumberAtShardEnd = extendedSequenceNumber;
|
this.sequenceNumberAtShardEnd = extendedSequenceNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ public class SequenceNumberValidator {
|
||||||
* @param validateWithGetIterator Whether to attempt to get an iterator for this shard id and the sequence numbers
|
* @param validateWithGetIterator Whether to attempt to get an iterator for this shard id and the sequence numbers
|
||||||
* being validated
|
* being validated
|
||||||
*/
|
*/
|
||||||
SequenceNumberValidator(IKinesisProxy proxy, String shardId, boolean validateWithGetIterator) {
|
public SequenceNumberValidator(IKinesisProxy proxy, String shardId, boolean validateWithGetIterator) {
|
||||||
this.proxy = proxy;
|
this.proxy = proxy;
|
||||||
this.shardId = shardId;
|
this.shardId = shardId;
|
||||||
this.validateWithGetIterator = validateWithGetIterator;
|
this.validateWithGetIterator = validateWithGetIterator;
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@ class ShardEndShardSyncStrategy implements ShardSyncStrategy {
|
||||||
private ShardSyncTaskManager shardSyncTaskManager;
|
private ShardSyncTaskManager shardSyncTaskManager;
|
||||||
|
|
||||||
/** Runs periodic shard sync jobs in the background as an auditor process for shard-end syncs. */
|
/** Runs periodic shard sync jobs in the background as an auditor process for shard-end syncs. */
|
||||||
private PeriodicShardSyncManager periodicShardSyncManager;
|
private IPeriodicShardSyncManager periodicShardSyncManager;
|
||||||
|
|
||||||
ShardEndShardSyncStrategy(ShardSyncTaskManager shardSyncTaskManager,
|
ShardEndShardSyncStrategy(ShardSyncTaskManager shardSyncTaskManager,
|
||||||
PeriodicShardSyncManager periodicShardSyncManager) {
|
IPeriodicShardSyncManager periodicShardSyncManager) {
|
||||||
this.shardSyncTaskManager = shardSyncTaskManager;
|
this.shardSyncTaskManager = shardSyncTaskManager;
|
||||||
this.periodicShardSyncManager = periodicShardSyncManager;
|
this.periodicShardSyncManager = periodicShardSyncManager;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ import java.util.List;
|
||||||
* It will clean up leases/activities for shards that have been completely processed (if
|
* It will clean up leases/activities for shards that have been completely processed (if
|
||||||
* cleanupLeasesUponShardCompletion is true).
|
* cleanupLeasesUponShardCompletion is true).
|
||||||
*/
|
*/
|
||||||
class ShardSyncTask implements ITask {
|
public class ShardSyncTask implements ITask {
|
||||||
|
|
||||||
private static final Log LOG = LogFactory.getLog(ShardSyncTask.class);
|
private static final Log LOG = LogFactory.getLog(ShardSyncTask.class);
|
||||||
|
|
||||||
|
|
@ -56,7 +56,7 @@ class ShardSyncTask implements ITask {
|
||||||
* @param shardSyncer shardSyncer instance used to check and create new leases
|
* @param shardSyncer shardSyncer instance used to check and create new leases
|
||||||
* @param latestShards latest snapshot of shards to reuse
|
* @param latestShards latest snapshot of shards to reuse
|
||||||
*/
|
*/
|
||||||
ShardSyncTask(IKinesisProxy kinesisProxy,
|
public ShardSyncTask(IKinesisProxy kinesisProxy,
|
||||||
ILeaseManager<KinesisClientLease> leaseManager,
|
ILeaseManager<KinesisClientLease> leaseManager,
|
||||||
InitialPositionInStreamExtended initialPositionInStream,
|
InitialPositionInStreamExtended initialPositionInStream,
|
||||||
boolean cleanupLeasesUponShardCompletion,
|
boolean cleanupLeasesUponShardCompletion,
|
||||||
|
|
|
||||||
|
|
@ -21,14 +21,14 @@ import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IShutdownNotif
|
||||||
/**
|
/**
|
||||||
* Notifies record processor of incoming shutdown request, and gives them a chance to checkpoint.
|
* Notifies record processor of incoming shutdown request, and gives them a chance to checkpoint.
|
||||||
*/
|
*/
|
||||||
class ShutdownNotificationTask implements ITask {
|
public class ShutdownNotificationTask implements ITask {
|
||||||
|
|
||||||
private final IRecordProcessor recordProcessor;
|
private final IRecordProcessor recordProcessor;
|
||||||
private final IRecordProcessorCheckpointer recordProcessorCheckpointer;
|
private final IRecordProcessorCheckpointer recordProcessorCheckpointer;
|
||||||
private final ShutdownNotification shutdownNotification;
|
private final ShutdownNotification shutdownNotification;
|
||||||
private final ShardInfo shardInfo;
|
private final ShardInfo shardInfo;
|
||||||
|
|
||||||
ShutdownNotificationTask(IRecordProcessor recordProcessor, IRecordProcessorCheckpointer recordProcessorCheckpointer, ShutdownNotification shutdownNotification, ShardInfo shardInfo) {
|
public ShutdownNotificationTask(IRecordProcessor recordProcessor, IRecordProcessorCheckpointer recordProcessorCheckpointer, ShutdownNotification shutdownNotification, ShardInfo shardInfo) {
|
||||||
this.recordProcessor = recordProcessor;
|
this.recordProcessor = recordProcessor;
|
||||||
this.recordProcessorCheckpointer = recordProcessorCheckpointer;
|
this.recordProcessorCheckpointer = recordProcessorCheckpointer;
|
||||||
this.shutdownNotification = shutdownNotification;
|
this.shutdownNotification = shutdownNotification;
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@
|
||||||
package com.amazonaws.services.kinesis.clientlibrary.lib.worker;
|
package com.amazonaws.services.kinesis.clientlibrary.lib.worker;
|
||||||
|
|
||||||
import com.amazonaws.services.kinesis.clientlibrary.types.ShutdownInput;
|
import com.amazonaws.services.kinesis.clientlibrary.types.ShutdownInput;
|
||||||
import static com.amazonaws.services.kinesis.clientlibrary.lib.worker.ConsumerStates.ConsumerState;
|
import static com.amazonaws.services.kinesis.clientlibrary.lib.worker.KinesisConsumerStates.ConsumerState;
|
||||||
import static com.amazonaws.services.kinesis.clientlibrary.lib.worker.ConsumerStates.ShardConsumerState;
|
import static com.amazonaws.services.kinesis.clientlibrary.lib.worker.KinesisConsumerStates.ShardConsumerState;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -72,7 +72,7 @@ public enum ShutdownReason {
|
||||||
return reason.rank > this.rank;
|
return reason.rank > this.rank;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsumerState getShutdownState() {
|
public ConsumerState getShutdownState() {
|
||||||
return shutdownState;
|
return shutdownState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import com.amazonaws.services.kinesis.clientlibrary.proxies.IKinesisProxy;
|
||||||
/**
|
/**
|
||||||
* Used to capture stream configuration and pass it along.
|
* Used to capture stream configuration and pass it along.
|
||||||
*/
|
*/
|
||||||
class StreamConfig {
|
public class StreamConfig {
|
||||||
|
|
||||||
private final IKinesisProxy streamProxy;
|
private final IKinesisProxy streamProxy;
|
||||||
private final int maxRecords;
|
private final int maxRecords;
|
||||||
|
|
@ -54,7 +54,7 @@ class StreamConfig {
|
||||||
/**
|
/**
|
||||||
* @return the streamProxy
|
* @return the streamProxy
|
||||||
*/
|
*/
|
||||||
IKinesisProxy getStreamProxy() {
|
public IKinesisProxy getStreamProxy() {
|
||||||
return streamProxy;
|
return streamProxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,14 +82,14 @@ class StreamConfig {
|
||||||
/**
|
/**
|
||||||
* @return the initialPositionInStream
|
* @return the initialPositionInStream
|
||||||
*/
|
*/
|
||||||
InitialPositionInStreamExtended getInitialPositionInStream() {
|
public InitialPositionInStreamExtended getInitialPositionInStream() {
|
||||||
return initialPositionInStream;
|
return initialPositionInStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return validateSequenceNumberBeforeCheckpointing
|
* @return validateSequenceNumberBeforeCheckpointing
|
||||||
*/
|
*/
|
||||||
boolean shouldValidateSequenceNumberBeforeCheckpointing() {
|
public boolean shouldValidateSequenceNumberBeforeCheckpointing() {
|
||||||
return validateSequenceNumberBeforeCheckpointing;
|
return validateSequenceNumberBeforeCheckpointing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import lombok.NonNull;
|
||||||
@Data
|
@Data
|
||||||
public class SynchronousGetRecordsRetrievalStrategy implements GetRecordsRetrievalStrategy {
|
public class SynchronousGetRecordsRetrievalStrategy implements GetRecordsRetrievalStrategy {
|
||||||
@NonNull
|
@NonNull
|
||||||
private final KinesisDataFetcher dataFetcher;
|
private final IDataFetcher dataFetcher;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GetRecordsResult getRecords(final int maxRecords) {
|
public GetRecordsResult getRecords(final int maxRecords) {
|
||||||
|
|
@ -44,7 +44,7 @@ public class SynchronousGetRecordsRetrievalStrategy implements GetRecordsRetriev
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KinesisDataFetcher getDataFetcher() {
|
public IDataFetcher getDataFetcher() {
|
||||||
return dataFetcher;
|
return dataFetcher;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import java.util.List;
|
||||||
* Used to capture information from a task that we want to communicate back to the higher layer.
|
* Used to capture information from a task that we want to communicate back to the higher layer.
|
||||||
* E.g. exception thrown when executing the task, if we reach end of a shard.
|
* E.g. exception thrown when executing the task, if we reach end of a shard.
|
||||||
*/
|
*/
|
||||||
class TaskResult {
|
public class TaskResult {
|
||||||
|
|
||||||
// Did we reach the end of the shard while processing this task.
|
// Did we reach the end of the shard while processing this task.
|
||||||
private boolean shardEndReached;
|
private boolean shardEndReached;
|
||||||
|
|
@ -33,10 +33,12 @@ class TaskResult {
|
||||||
// List of childShards of the current shard. This field is only required for the task result when we reach end of a shard.
|
// List of childShards of the current shard. This field is only required for the task result when we reach end of a shard.
|
||||||
private List<ChildShard> childShards;
|
private List<ChildShard> childShards;
|
||||||
|
|
||||||
|
private boolean leaseNotFound;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the shardEndReached
|
* @return the shardEndReached
|
||||||
*/
|
*/
|
||||||
protected boolean isShardEndReached() {
|
public boolean isShardEndReached() {
|
||||||
return shardEndReached;
|
return shardEndReached;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,6 +59,14 @@ class TaskResult {
|
||||||
*/
|
*/
|
||||||
protected void setChildShards(List<ChildShard> childShards) { this.childShards = childShards; }
|
protected void setChildShards(List<ChildShard> childShards) { this.childShards = childShards; }
|
||||||
|
|
||||||
|
public boolean isLeaseNotFound() {
|
||||||
|
return leaseNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void leaseNotFound() {
|
||||||
|
this.leaseNotFound = true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the exception
|
* @return the exception
|
||||||
*/
|
*/
|
||||||
|
|
@ -67,7 +77,7 @@ class TaskResult {
|
||||||
/**
|
/**
|
||||||
* @param e Any exception encountered when running the process task.
|
* @param e Any exception encountered when running the process task.
|
||||||
*/
|
*/
|
||||||
TaskResult(Exception e) {
|
public TaskResult(Exception e) {
|
||||||
this(e, false);
|
this(e, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,9 +97,9 @@ public class Worker implements Runnable {
|
||||||
// Default configs for periodic shard sync
|
// Default configs for periodic shard sync
|
||||||
private static final int SHARD_SYNC_SLEEP_FOR_PERIODIC_SHARD_SYNC = 0;
|
private static final int SHARD_SYNC_SLEEP_FOR_PERIODIC_SHARD_SYNC = 0;
|
||||||
private static final int PERIODIC_SHARD_SYNC_MAX_WORKERS_DEFAULT = 1; //Default for KCL.
|
private static final int PERIODIC_SHARD_SYNC_MAX_WORKERS_DEFAULT = 1; //Default for KCL.
|
||||||
static final long LEASE_TABLE_CHECK_FREQUENCY_MILLIS = 3 * 1000L;
|
static long LEASE_TABLE_CHECK_FREQUENCY_MILLIS = 3 * 1000L;
|
||||||
static final long MIN_WAIT_TIME_FOR_LEASE_TABLE_CHECK_MILLIS = 1 * 1000L;
|
static long MIN_WAIT_TIME_FOR_LEASE_TABLE_CHECK_MILLIS = 1 * 1000L;
|
||||||
static final long MAX_WAIT_TIME_FOR_LEASE_TABLE_CHECK_MILLIS = 30 * 1000L;
|
static long MAX_WAIT_TIME_FOR_LEASE_TABLE_CHECK_MILLIS = 30 * 1000L;
|
||||||
|
|
||||||
private static final WorkerStateChangeListener DEFAULT_WORKER_STATE_CHANGE_LISTENER = new NoOpWorkerStateChangeListener();
|
private static final WorkerStateChangeListener DEFAULT_WORKER_STATE_CHANGE_LISTENER = new NoOpWorkerStateChangeListener();
|
||||||
private static final LeaseCleanupValidator DEFAULT_LEASE_CLEANUP_VALIDATOR = new KinesisLeaseCleanupValidator();
|
private static final LeaseCleanupValidator DEFAULT_LEASE_CLEANUP_VALIDATOR = new KinesisLeaseCleanupValidator();
|
||||||
|
|
@ -140,7 +140,7 @@ public class Worker implements Runnable {
|
||||||
|
|
||||||
// Holds consumers for shards the worker is currently tracking. Key is shard
|
// Holds consumers for shards the worker is currently tracking. Key is shard
|
||||||
// info, value is ShardConsumer.
|
// info, value is ShardConsumer.
|
||||||
private ConcurrentMap<ShardInfo, ShardConsumer> shardInfoShardConsumerMap = new ConcurrentHashMap<ShardInfo, ShardConsumer>();
|
private ConcurrentMap<ShardInfo, IShardConsumer> shardInfoShardConsumerMap = new ConcurrentHashMap<ShardInfo, IShardConsumer>();
|
||||||
private final boolean cleanupLeasesUponShardCompletion;
|
private final boolean cleanupLeasesUponShardCompletion;
|
||||||
|
|
||||||
private final boolean skipShardSyncAtWorkerInitializationIfLeasesExist;
|
private final boolean skipShardSyncAtWorkerInitializationIfLeasesExist;
|
||||||
|
|
@ -159,10 +159,13 @@ public class Worker implements Runnable {
|
||||||
// Periodic Shard Sync related fields
|
// Periodic Shard Sync related fields
|
||||||
private LeaderDecider leaderDecider;
|
private LeaderDecider leaderDecider;
|
||||||
private ShardSyncStrategy shardSyncStrategy;
|
private ShardSyncStrategy shardSyncStrategy;
|
||||||
private PeriodicShardSyncManager leaderElectedPeriodicShardSyncManager;
|
private IPeriodicShardSyncManager leaderElectedPeriodicShardSyncManager;
|
||||||
|
|
||||||
private final LeaseCleanupManager leaseCleanupManager;
|
private final LeaseCleanupManager leaseCleanupManager;
|
||||||
|
|
||||||
|
// Shard Consumer Factory
|
||||||
|
private IShardConsumerFactory shardConsumerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
|
|
@ -536,13 +539,13 @@ public class Worker implements Runnable {
|
||||||
IMetricsFactory metricsFactory, long taskBackoffTimeMillis, long failoverTimeMillis,
|
IMetricsFactory metricsFactory, long taskBackoffTimeMillis, long failoverTimeMillis,
|
||||||
boolean skipShardSyncAtWorkerInitializationIfLeasesExist, ShardPrioritization shardPrioritization,
|
boolean skipShardSyncAtWorkerInitializationIfLeasesExist, ShardPrioritization shardPrioritization,
|
||||||
Optional<Integer> retryGetRecordsInSeconds, Optional<Integer> maxGetRecordsThreadPool, WorkerStateChangeListener workerStateChangeListener,
|
Optional<Integer> retryGetRecordsInSeconds, Optional<Integer> maxGetRecordsThreadPool, WorkerStateChangeListener workerStateChangeListener,
|
||||||
LeaseCleanupValidator leaseCleanupValidator, LeaderDecider leaderDecider, PeriodicShardSyncManager periodicShardSyncManager) {
|
LeaseCleanupValidator leaseCleanupValidator, LeaderDecider leaderDecider, IPeriodicShardSyncManager periodicShardSyncManager) {
|
||||||
this(applicationName, recordProcessorFactory, config, streamConfig, initialPositionInStream,
|
this(applicationName, recordProcessorFactory, config, streamConfig, initialPositionInStream,
|
||||||
parentShardPollIntervalMillis, shardSyncIdleTimeMillis, cleanupLeasesUponShardCompletion, checkpoint,
|
parentShardPollIntervalMillis, shardSyncIdleTimeMillis, cleanupLeasesUponShardCompletion, checkpoint,
|
||||||
leaseCoordinator, execService, metricsFactory, taskBackoffTimeMillis, failoverTimeMillis,
|
leaseCoordinator, execService, metricsFactory, taskBackoffTimeMillis, failoverTimeMillis,
|
||||||
skipShardSyncAtWorkerInitializationIfLeasesExist, shardPrioritization, retryGetRecordsInSeconds,
|
skipShardSyncAtWorkerInitializationIfLeasesExist, shardPrioritization, retryGetRecordsInSeconds,
|
||||||
maxGetRecordsThreadPool, workerStateChangeListener, new KinesisShardSyncer(leaseCleanupValidator),
|
maxGetRecordsThreadPool, workerStateChangeListener, new KinesisShardSyncer(leaseCleanupValidator),
|
||||||
leaderDecider, periodicShardSyncManager);
|
leaderDecider, periodicShardSyncManager, null /*ShardConsumerFactory*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
Worker(String applicationName, IRecordProcessorFactory recordProcessorFactory, KinesisClientLibConfiguration config,
|
Worker(String applicationName, IRecordProcessorFactory recordProcessorFactory, KinesisClientLibConfiguration config,
|
||||||
|
|
@ -553,7 +556,7 @@ public class Worker implements Runnable {
|
||||||
boolean skipShardSyncAtWorkerInitializationIfLeasesExist, ShardPrioritization shardPrioritization,
|
boolean skipShardSyncAtWorkerInitializationIfLeasesExist, ShardPrioritization shardPrioritization,
|
||||||
Optional<Integer> retryGetRecordsInSeconds, Optional<Integer> maxGetRecordsThreadPool,
|
Optional<Integer> retryGetRecordsInSeconds, Optional<Integer> maxGetRecordsThreadPool,
|
||||||
WorkerStateChangeListener workerStateChangeListener, ShardSyncer shardSyncer, LeaderDecider leaderDecider,
|
WorkerStateChangeListener workerStateChangeListener, ShardSyncer shardSyncer, LeaderDecider leaderDecider,
|
||||||
PeriodicShardSyncManager periodicShardSyncManager) {
|
IPeriodicShardSyncManager periodicShardSyncManager, IShardConsumerFactory shardConsumerFactory) {
|
||||||
this.applicationName = applicationName;
|
this.applicationName = applicationName;
|
||||||
this.recordProcessorFactory = recordProcessorFactory;
|
this.recordProcessorFactory = recordProcessorFactory;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
|
@ -579,10 +582,11 @@ public class Worker implements Runnable {
|
||||||
this.workerStateChangeListener = workerStateChangeListener;
|
this.workerStateChangeListener = workerStateChangeListener;
|
||||||
workerStateChangeListener.onWorkerStateChange(WorkerStateChangeListener.WorkerState.CREATED);
|
workerStateChangeListener.onWorkerStateChange(WorkerStateChangeListener.WorkerState.CREATED);
|
||||||
createShardSyncStrategy(config.getShardSyncStrategyType(), leaderDecider, periodicShardSyncManager);
|
createShardSyncStrategy(config.getShardSyncStrategyType(), leaderDecider, periodicShardSyncManager);
|
||||||
this.leaseCleanupManager = LeaseCleanupManager.createOrGetInstance(streamConfig.getStreamProxy(), leaseCoordinator.getLeaseManager(),
|
this.leaseCleanupManager = LeaseCleanupManager.newInstance(streamConfig.getStreamProxy(), leaseCoordinator.getLeaseManager(),
|
||||||
Executors.newSingleThreadScheduledExecutor(), metricsFactory, cleanupLeasesUponShardCompletion,
|
Executors.newSingleThreadScheduledExecutor(), metricsFactory, cleanupLeasesUponShardCompletion,
|
||||||
config.leaseCleanupIntervalMillis(), config.completedLeaseCleanupThresholdMillis(),
|
config.leaseCleanupIntervalMillis(), config.completedLeaseCleanupThresholdMillis(),
|
||||||
config.garbageLeaseCleanupThresholdMillis(), config.getMaxRecords());
|
config.garbageLeaseCleanupThresholdMillis(), config.getMaxRecords());
|
||||||
|
this.shardConsumerFactory = shardConsumerFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -593,7 +597,7 @@ public class Worker implements Runnable {
|
||||||
*/
|
*/
|
||||||
private void createShardSyncStrategy(ShardSyncStrategyType strategyType,
|
private void createShardSyncStrategy(ShardSyncStrategyType strategyType,
|
||||||
LeaderDecider leaderDecider,
|
LeaderDecider leaderDecider,
|
||||||
PeriodicShardSyncManager periodicShardSyncManager) {
|
IPeriodicShardSyncManager periodicShardSyncManager) {
|
||||||
switch (strategyType) {
|
switch (strategyType) {
|
||||||
case PERIODIC:
|
case PERIODIC:
|
||||||
this.leaderDecider = getOrCreateLeaderDecider(leaderDecider);
|
this.leaderDecider = getOrCreateLeaderDecider(leaderDecider);
|
||||||
|
|
@ -655,7 +659,7 @@ public class Worker implements Runnable {
|
||||||
/**
|
/**
|
||||||
* @return the leaderElectedPeriodicShardSyncManager
|
* @return the leaderElectedPeriodicShardSyncManager
|
||||||
*/
|
*/
|
||||||
PeriodicShardSyncManager getPeriodicShardSyncManager() {
|
IPeriodicShardSyncManager getPeriodicShardSyncManager() {
|
||||||
return leaderElectedPeriodicShardSyncManager;
|
return leaderElectedPeriodicShardSyncManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -690,7 +694,7 @@ public class Worker implements Runnable {
|
||||||
boolean foundCompletedShard = false;
|
boolean foundCompletedShard = false;
|
||||||
Set<ShardInfo> assignedShards = new HashSet<>();
|
Set<ShardInfo> assignedShards = new HashSet<>();
|
||||||
for (ShardInfo shardInfo : getShardInfoForAssignments()) {
|
for (ShardInfo shardInfo : getShardInfoForAssignments()) {
|
||||||
ShardConsumer shardConsumer = createOrGetShardConsumer(shardInfo, recordProcessorFactory);
|
IShardConsumer shardConsumer = createOrGetShardConsumer(shardInfo, recordProcessorFactory);
|
||||||
if (shardConsumer.isShutdown() && shardConsumer.getShutdownReason().equals(ShutdownReason.TERMINATE)) {
|
if (shardConsumer.isShutdown() && shardConsumer.getShutdownReason().equals(ShutdownReason.TERMINATE)) {
|
||||||
foundCompletedShard = true;
|
foundCompletedShard = true;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1000,9 +1004,9 @@ public class Worker implements Runnable {
|
||||||
ShutdownNotification shutdownNotification = new ShardConsumerShutdownNotification(leaseCoordinator,
|
ShutdownNotification shutdownNotification = new ShardConsumerShutdownNotification(leaseCoordinator,
|
||||||
lease, notificationCompleteLatch, shutdownCompleteLatch);
|
lease, notificationCompleteLatch, shutdownCompleteLatch);
|
||||||
ShardInfo shardInfo = KinesisClientLibLeaseCoordinator.convertLeaseToAssignment(lease);
|
ShardInfo shardInfo = KinesisClientLibLeaseCoordinator.convertLeaseToAssignment(lease);
|
||||||
ShardConsumer consumer = shardInfoShardConsumerMap.get(shardInfo);
|
IShardConsumer consumer = shardInfoShardConsumerMap.get(shardInfo);
|
||||||
|
|
||||||
if (consumer == null || ConsumerStates.ShardConsumerState.SHUTDOWN_COMPLETE.equals(consumer.getCurrentState())) {
|
if (consumer == null || KinesisConsumerStates.ShardConsumerState.SHUTDOWN_COMPLETE.equals(consumer.getCurrentState())) {
|
||||||
//
|
//
|
||||||
// CASE1: There is a race condition between retrieving the current assignments, and creating the
|
// CASE1: There is a race condition between retrieving the current assignments, and creating the
|
||||||
// notification. If the a lease is lost in between these two points, we explicitly decrement the
|
// notification. If the a lease is lost in between these two points, we explicitly decrement the
|
||||||
|
|
@ -1024,7 +1028,7 @@ public class Worker implements Runnable {
|
||||||
return shutdownComplete;
|
return shutdownComplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConcurrentMap<ShardInfo, ShardConsumer> getShardInfoShardConsumerMap() {
|
ConcurrentMap<ShardInfo, IShardConsumer> getShardInfoShardConsumerMap() {
|
||||||
return shardInfoShardConsumerMap;
|
return shardInfoShardConsumerMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1062,6 +1066,10 @@ public class Worker implements Runnable {
|
||||||
// Lost leases will force Worker to begin shutdown process for all shard consumers in
|
// Lost leases will force Worker to begin shutdown process for all shard consumers in
|
||||||
// Worker.run().
|
// Worker.run().
|
||||||
leaseCoordinator.stop();
|
leaseCoordinator.stop();
|
||||||
|
|
||||||
|
// Stop the lease cleanup manager
|
||||||
|
leaseCleanupManager.shutdown();
|
||||||
|
|
||||||
// Stop the periodicShardSyncManager for the worker
|
// Stop the periodicShardSyncManager for the worker
|
||||||
if (shardSyncStrategy != null) {
|
if (shardSyncStrategy != null) {
|
||||||
shardSyncStrategy.onWorkerShutDown();
|
shardSyncStrategy.onWorkerShutDown();
|
||||||
|
|
@ -1120,8 +1128,8 @@ public class Worker implements Runnable {
|
||||||
* RecordProcessor factory
|
* RecordProcessor factory
|
||||||
* @return ShardConsumer for the shard
|
* @return ShardConsumer for the shard
|
||||||
*/
|
*/
|
||||||
ShardConsumer createOrGetShardConsumer(ShardInfo shardInfo, IRecordProcessorFactory processorFactory) {
|
IShardConsumer createOrGetShardConsumer(ShardInfo shardInfo, IRecordProcessorFactory processorFactory) {
|
||||||
ShardConsumer consumer = shardInfoShardConsumerMap.get(shardInfo);
|
IShardConsumer consumer = shardInfoShardConsumerMap.get(shardInfo);
|
||||||
// Instantiate a new consumer if we don't have one, or the one we
|
// Instantiate a new consumer if we don't have one, or the one we
|
||||||
// had was from an earlier
|
// had was from an earlier
|
||||||
// lease instance (and was shutdown). Don't need to create another
|
// lease instance (and was shutdown). Don't need to create another
|
||||||
|
|
@ -1136,7 +1144,7 @@ public class Worker implements Runnable {
|
||||||
return consumer;
|
return consumer;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ShardConsumer buildConsumer(ShardInfo shardInfo, IRecordProcessorFactory processorFactory) {
|
protected IShardConsumer buildConsumer(ShardInfo shardInfo, IRecordProcessorFactory processorFactory) {
|
||||||
final IRecordProcessor recordProcessor = processorFactory.createProcessor();
|
final IRecordProcessor recordProcessor = processorFactory.createProcessor();
|
||||||
final RecordProcessorCheckpointer recordProcessorCheckpointer = new RecordProcessorCheckpointer(
|
final RecordProcessorCheckpointer recordProcessorCheckpointer = new RecordProcessorCheckpointer(
|
||||||
shardInfo,
|
shardInfo,
|
||||||
|
|
@ -1147,7 +1155,11 @@ public class Worker implements Runnable {
|
||||||
streamConfig.shouldValidateSequenceNumberBeforeCheckpointing()),
|
streamConfig.shouldValidateSequenceNumberBeforeCheckpointing()),
|
||||||
metricsFactory);
|
metricsFactory);
|
||||||
|
|
||||||
return new ShardConsumer(shardInfo,
|
if(shardConsumerFactory == null){ //Default to KinesisShardConsumerFactory if null
|
||||||
|
this.shardConsumerFactory = new KinesisShardConsumerFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
return shardConsumerFactory.createShardConsumer(shardInfo,
|
||||||
streamConfig,
|
streamConfig,
|
||||||
checkpointTracker,
|
checkpointTracker,
|
||||||
recordProcessor,
|
recordProcessor,
|
||||||
|
|
@ -1159,7 +1171,6 @@ public class Worker implements Runnable {
|
||||||
metricsFactory,
|
metricsFactory,
|
||||||
taskBackoffTimeMillis,
|
taskBackoffTimeMillis,
|
||||||
skipShardSyncAtWorkerInitializationIfLeasesExist,
|
skipShardSyncAtWorkerInitializationIfLeasesExist,
|
||||||
new KinesisDataFetcher(streamConfig.getStreamProxy(), shardInfo),
|
|
||||||
retryGetRecordsInSeconds,
|
retryGetRecordsInSeconds,
|
||||||
maxGetRecordsThreadPool,
|
maxGetRecordsThreadPool,
|
||||||
config, shardSyncer, shardSyncStrategy,
|
config, shardSyncer, shardSyncStrategy,
|
||||||
|
|
@ -1237,8 +1248,8 @@ public class Worker implements Runnable {
|
||||||
* KinesisClientLibConfiguration
|
* KinesisClientLibConfiguration
|
||||||
* @return Returns metrics factory based on the config.
|
* @return Returns metrics factory based on the config.
|
||||||
*/
|
*/
|
||||||
private static IMetricsFactory getMetricsFactory(AmazonCloudWatch cloudWatchClient,
|
public static IMetricsFactory getMetricsFactory(AmazonCloudWatch cloudWatchClient,
|
||||||
KinesisClientLibConfiguration config) {
|
KinesisClientLibConfiguration config) {
|
||||||
IMetricsFactory metricsFactory;
|
IMetricsFactory metricsFactory;
|
||||||
if (config.getMetricsLevel() == MetricsLevel.NONE) {
|
if (config.getMetricsLevel() == MetricsLevel.NONE) {
|
||||||
metricsFactory = new NullMetricsFactory();
|
metricsFactory = new NullMetricsFactory();
|
||||||
|
|
@ -1291,13 +1302,13 @@ public class Worker implements Runnable {
|
||||||
|
|
||||||
/** A non-null PeriodicShardSyncManager can only provided from unit tests. Any application code will create the
|
/** A non-null PeriodicShardSyncManager can only provided from unit tests. Any application code will create the
|
||||||
* PeriodicShardSyncManager for the first time here. */
|
* PeriodicShardSyncManager for the first time here. */
|
||||||
private PeriodicShardSyncManager getOrCreatePeriodicShardSyncManager(PeriodicShardSyncManager periodicShardSyncManager,
|
private IPeriodicShardSyncManager getOrCreatePeriodicShardSyncManager(IPeriodicShardSyncManager periodicShardSyncManager,
|
||||||
boolean isAuditorMode) {
|
boolean isAuditorMode) {
|
||||||
if (periodicShardSyncManager != null) {
|
if (periodicShardSyncManager != null) {
|
||||||
return periodicShardSyncManager;
|
return periodicShardSyncManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new PeriodicShardSyncManager(config.getWorkerIdentifier(),
|
return new KinesisPeriodicShardSyncManager(config.getWorkerIdentifier(),
|
||||||
leaderDecider,
|
leaderDecider,
|
||||||
new ShardSyncTask(streamConfig.getStreamProxy(),
|
new ShardSyncTask(streamConfig.getStreamProxy(),
|
||||||
leaseCoordinator.getLeaseManager(),
|
leaseCoordinator.getLeaseManager(),
|
||||||
|
|
@ -1366,6 +1377,10 @@ public class Worker implements Runnable {
|
||||||
@Setter @Accessors(fluent = true)
|
@Setter @Accessors(fluent = true)
|
||||||
private IKinesisProxy kinesisProxy;
|
private IKinesisProxy kinesisProxy;
|
||||||
@Setter @Accessors(fluent = true)
|
@Setter @Accessors(fluent = true)
|
||||||
|
private IPeriodicShardSyncManager periodicShardSyncManager;
|
||||||
|
@Setter @Accessors(fluent = true)
|
||||||
|
private IShardConsumerFactory shardConsumerFactory;
|
||||||
|
@Setter @Accessors(fluent = true)
|
||||||
private WorkerStateChangeListener workerStateChangeListener;
|
private WorkerStateChangeListener workerStateChangeListener;
|
||||||
@Setter @Accessors(fluent = true)
|
@Setter @Accessors(fluent = true)
|
||||||
private LeaseCleanupValidator leaseCleanupValidator;
|
private LeaseCleanupValidator leaseCleanupValidator;
|
||||||
|
|
@ -1434,6 +1449,16 @@ public class Worker implements Runnable {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Kinesis Client Library configuration needs to be provided to build Worker");
|
"Kinesis Client Library configuration needs to be provided to build Worker");
|
||||||
}
|
}
|
||||||
|
if (periodicShardSyncManager != null) {
|
||||||
|
if (leaseManager == null || shardSyncer == null || metricsFactory == null || leaderDecider == null) {
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("LeaseManager, ShardSyncer, MetricsFactory, and LeaderDecider must be provided if PeriodicShardSyncManager is provided");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(shardConsumerFactory == null){
|
||||||
|
shardConsumerFactory = new KinesisShardConsumerFactory();
|
||||||
|
}
|
||||||
|
|
||||||
if (recordProcessorFactory == null) {
|
if (recordProcessorFactory == null) {
|
||||||
throw new IllegalArgumentException("A Record Processor Factory needs to be provided to build Worker");
|
throw new IllegalArgumentException("A Record Processor Factory needs to be provided to build Worker");
|
||||||
}
|
}
|
||||||
|
|
@ -1516,7 +1541,7 @@ public class Worker implements Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
// We expect users to either inject both LeaseRenewer and the corresponding thread-pool, or neither of them (DEFAULT).
|
// We expect users to either inject both LeaseRenewer and the corresponding thread-pool, or neither of them (DEFAULT).
|
||||||
if (leaseRenewer == null) {
|
if (leaseRenewer == null) {
|
||||||
ExecutorService leaseRenewerThreadPool = LeaseCoordinator.getDefaultLeaseRenewalExecutorService(config.getMaxLeaseRenewalThreads());
|
ExecutorService leaseRenewerThreadPool = LeaseCoordinator.getDefaultLeaseRenewalExecutorService(config.getMaxLeaseRenewalThreads());
|
||||||
leaseRenewer = new LeaseRenewer<>(leaseManager, config.getWorkerIdentifier(), config.getFailoverTimeMillis(), leaseRenewerThreadPool);
|
leaseRenewer = new LeaseRenewer<>(leaseManager, config.getWorkerIdentifier(), config.getFailoverTimeMillis(), leaseRenewerThreadPool);
|
||||||
}
|
}
|
||||||
|
|
@ -1525,7 +1550,6 @@ public class Worker implements Runnable {
|
||||||
leaderDecider = new DeterministicShuffleShardSyncLeaderDecider(leaseManager,
|
leaderDecider = new DeterministicShuffleShardSyncLeaderDecider(leaseManager,
|
||||||
Executors.newSingleThreadScheduledExecutor(), PERIODIC_SHARD_SYNC_MAX_WORKERS_DEFAULT);
|
Executors.newSingleThreadScheduledExecutor(), PERIODIC_SHARD_SYNC_MAX_WORKERS_DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Worker(config.getApplicationName(),
|
return new Worker(config.getApplicationName(),
|
||||||
recordProcessorFactory,
|
recordProcessorFactory,
|
||||||
config,
|
config,
|
||||||
|
|
@ -1559,7 +1583,8 @@ public class Worker implements Runnable {
|
||||||
workerStateChangeListener,
|
workerStateChangeListener,
|
||||||
shardSyncer,
|
shardSyncer,
|
||||||
leaderDecider,
|
leaderDecider,
|
||||||
null /* PeriodicShardSyncManager */);
|
periodicShardSyncManager /*PeriodicShardSyncManager*/,
|
||||||
|
shardConsumerFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
<R, T extends AwsClientBuilder<T, R>> R createClient(final T builder,
|
<R, T extends AwsClientBuilder<T, R>> R createClient(final T builder,
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
@ -29,12 +30,14 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.amazonaws.services.kinesis.clientlibrary.utils.RequestUtil;
|
||||||
import com.amazonaws.services.kinesis.model.ShardFilter;
|
import com.amazonaws.services.kinesis.model.ShardFilter;
|
||||||
import com.amazonaws.util.CollectionUtils;
|
import com.amazonaws.util.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import com.amazonaws.arn.Arn;
|
||||||
import com.amazonaws.auth.AWSCredentialsProvider;
|
import com.amazonaws.auth.AWSCredentialsProvider;
|
||||||
import com.amazonaws.services.kinesis.AmazonKinesis;
|
import com.amazonaws.services.kinesis.AmazonKinesis;
|
||||||
import com.amazonaws.services.kinesis.AmazonKinesisClient;
|
import com.amazonaws.services.kinesis.AmazonKinesisClient;
|
||||||
|
|
@ -59,7 +62,6 @@ import com.amazonaws.services.kinesis.model.ShardIteratorType;
|
||||||
import com.amazonaws.services.kinesis.model.StreamStatus;
|
import com.amazonaws.services.kinesis.model.StreamStatus;
|
||||||
|
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.Data;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
|
|
@ -82,8 +84,6 @@ public class KinesisProxy implements IKinesisProxyExtended {
|
||||||
private AmazonKinesis client;
|
private AmazonKinesis client;
|
||||||
private AWSCredentialsProvider credentialsProvider;
|
private AWSCredentialsProvider credentialsProvider;
|
||||||
|
|
||||||
private ShardIterationState shardIterationState = null;
|
|
||||||
|
|
||||||
@Setter(AccessLevel.PACKAGE)
|
@Setter(AccessLevel.PACKAGE)
|
||||||
private volatile Map<String, Shard> cachedShardMap = null;
|
private volatile Map<String, Shard> cachedShardMap = null;
|
||||||
@Setter(AccessLevel.PACKAGE)
|
@Setter(AccessLevel.PACKAGE)
|
||||||
|
|
@ -95,6 +95,12 @@ public class KinesisProxy implements IKinesisProxyExtended {
|
||||||
|
|
||||||
private final String streamName;
|
private final String streamName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stored as a string instead of an ARN to reduce repetitive null checks when passing in the stream ARN to
|
||||||
|
* the client requests, which accepts a String stream ARN parameter.
|
||||||
|
*/
|
||||||
|
private String streamArn;
|
||||||
|
|
||||||
private static final long DEFAULT_DESCRIBE_STREAM_BACKOFF_MILLIS = 1000L;
|
private static final long DEFAULT_DESCRIBE_STREAM_BACKOFF_MILLIS = 1000L;
|
||||||
private static final int DEFAULT_DESCRIBE_STREAM_RETRY_TIMES = 50;
|
private static final int DEFAULT_DESCRIBE_STREAM_RETRY_TIMES = 50;
|
||||||
private final long describeStreamBackoffTimeInMillis;
|
private final long describeStreamBackoffTimeInMillis;
|
||||||
|
|
@ -219,6 +225,8 @@ public class KinesisProxy implements IKinesisProxyExtended {
|
||||||
config.getListShardsBackoffTimeInMillis(),
|
config.getListShardsBackoffTimeInMillis(),
|
||||||
config.getMaxListShardsRetryAttempts());
|
config.getMaxListShardsRetryAttempts());
|
||||||
this.credentialsProvider = config.getKinesisCredentialsProvider();
|
this.credentialsProvider = config.getKinesisCredentialsProvider();
|
||||||
|
Arn arn = config.getStreamArn();
|
||||||
|
this.streamArn = arn != null ? arn.toString() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KinesisProxy(final String streamName,
|
public KinesisProxy(final String streamName,
|
||||||
|
|
@ -254,6 +262,7 @@ public class KinesisProxy implements IKinesisProxyExtended {
|
||||||
|
|
||||||
final GetRecordsRequest getRecordsRequest = new GetRecordsRequest();
|
final GetRecordsRequest getRecordsRequest = new GetRecordsRequest();
|
||||||
getRecordsRequest.setRequestCredentials(credentialsProvider.getCredentials());
|
getRecordsRequest.setRequestCredentials(credentialsProvider.getCredentials());
|
||||||
|
getRecordsRequest.setStreamARN(streamArn);
|
||||||
getRecordsRequest.setShardIterator(shardIterator);
|
getRecordsRequest.setShardIterator(shardIterator);
|
||||||
getRecordsRequest.setLimit(maxRecords);
|
getRecordsRequest.setLimit(maxRecords);
|
||||||
final GetRecordsResult response = client.getRecords(getRecordsRequest);
|
final GetRecordsResult response = client.getRecords(getRecordsRequest);
|
||||||
|
|
@ -271,6 +280,7 @@ public class KinesisProxy implements IKinesisProxyExtended {
|
||||||
final DescribeStreamRequest describeStreamRequest = new DescribeStreamRequest();
|
final DescribeStreamRequest describeStreamRequest = new DescribeStreamRequest();
|
||||||
describeStreamRequest.setRequestCredentials(credentialsProvider.getCredentials());
|
describeStreamRequest.setRequestCredentials(credentialsProvider.getCredentials());
|
||||||
describeStreamRequest.setStreamName(streamName);
|
describeStreamRequest.setStreamName(streamName);
|
||||||
|
describeStreamRequest.setStreamARN(streamArn);
|
||||||
describeStreamRequest.setExclusiveStartShardId(startShardId);
|
describeStreamRequest.setExclusiveStartShardId(startShardId);
|
||||||
DescribeStreamResult response = null;
|
DescribeStreamResult response = null;
|
||||||
|
|
||||||
|
|
@ -315,13 +325,13 @@ public class KinesisProxy implements IKinesisProxyExtended {
|
||||||
request.setRequestCredentials(credentialsProvider.getCredentials());
|
request.setRequestCredentials(credentialsProvider.getCredentials());
|
||||||
if (StringUtils.isEmpty(nextToken)) {
|
if (StringUtils.isEmpty(nextToken)) {
|
||||||
request.setStreamName(streamName);
|
request.setStreamName(streamName);
|
||||||
|
request.setStreamARN(streamArn);
|
||||||
|
request.setShardFilter(shardFilter);
|
||||||
} else {
|
} else {
|
||||||
request.setNextToken(nextToken);
|
request.setNextToken(nextToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shardFilter != null) {
|
LOG.info("Listing shards with list shards request " + request);
|
||||||
request.setShardFilter(shardFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
ListShardsResult result = null;
|
ListShardsResult result = null;
|
||||||
LimitExceededException lastException = null;
|
LimitExceededException lastException = null;
|
||||||
|
|
@ -443,10 +453,8 @@ public class KinesisProxy implements IKinesisProxyExtended {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public synchronized List<Shard> getShardListWithFilter(ShardFilter shardFilter) {
|
public synchronized List<Shard> getShardListWithFilter(ShardFilter shardFilter) {
|
||||||
if (shardIterationState == null) {
|
final List<Shard> shards = new ArrayList<>();
|
||||||
shardIterationState = new ShardIterationState();
|
final List<String> requestIds = new ArrayList<>();
|
||||||
}
|
|
||||||
|
|
||||||
if (isKinesisClient) {
|
if (isKinesisClient) {
|
||||||
ListShardsResult result;
|
ListShardsResult result;
|
||||||
String nextToken = null;
|
String nextToken = null;
|
||||||
|
|
@ -461,16 +469,18 @@ public class KinesisProxy implements IKinesisProxyExtended {
|
||||||
*/
|
*/
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
shardIterationState.update(result.getShards());
|
shards.addAll(result.getShards());
|
||||||
|
requestIds.add(RequestUtil.requestId(result));
|
||||||
nextToken = result.getNextToken();
|
nextToken = result.getNextToken();
|
||||||
}
|
}
|
||||||
} while (StringUtils.isNotEmpty(result.getNextToken()));
|
} while (StringUtils.isNotEmpty(result.getNextToken()));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
DescribeStreamResult response;
|
DescribeStreamResult response;
|
||||||
|
String lastShardId = null;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
response = getStreamInfo(shardIterationState.getLastShardId());
|
response = getStreamInfo(lastShardId);
|
||||||
|
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
/*
|
/*
|
||||||
|
|
@ -479,16 +489,26 @@ public class KinesisProxy implements IKinesisProxyExtended {
|
||||||
*/
|
*/
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
shardIterationState.update(response.getStreamDescription().getShards());
|
final List<Shard> pageOfShards = response.getStreamDescription().getShards();
|
||||||
|
shards.addAll(pageOfShards);
|
||||||
|
requestIds.add(RequestUtil.requestId(response));
|
||||||
|
|
||||||
|
final Shard lastShard = pageOfShards.get(pageOfShards.size() - 1);
|
||||||
|
if (lastShardId == null || lastShardId.compareTo(lastShard.getShardId()) < 0) {
|
||||||
|
lastShardId = lastShard.getShardId();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} while (response.getStreamDescription().isHasMoreShards());
|
} while (response.getStreamDescription().isHasMoreShards());
|
||||||
}
|
}
|
||||||
List<Shard> shards = shardIterationState.getShards();
|
final List<Shard> dedupedShards = new ArrayList<>(new LinkedHashSet<>(shards));
|
||||||
this.cachedShardMap = shards.stream().collect(Collectors.toMap(Shard::getShardId, Function.identity()));
|
if (dedupedShards.size() < shards.size()) {
|
||||||
|
LOG.warn("Found duplicate shards in response when sync'ing from Kinesis. " +
|
||||||
|
"Request ids - " + requestIds + ". Response - " + shards);
|
||||||
|
}
|
||||||
|
this.cachedShardMap = dedupedShards.stream().collect(Collectors.toMap(Shard::getShardId, Function.identity()));
|
||||||
this.lastCacheUpdateTime = Instant.now();
|
this.lastCacheUpdateTime = Instant.now();
|
||||||
|
|
||||||
shardIterationState = new ShardIterationState();
|
return dedupedShards;
|
||||||
return shards;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -559,6 +579,7 @@ public class KinesisProxy implements IKinesisProxyExtended {
|
||||||
final GetShardIteratorRequest getShardIteratorRequest = new GetShardIteratorRequest();
|
final GetShardIteratorRequest getShardIteratorRequest = new GetShardIteratorRequest();
|
||||||
getShardIteratorRequest.setRequestCredentials(credentialsProvider.getCredentials());
|
getShardIteratorRequest.setRequestCredentials(credentialsProvider.getCredentials());
|
||||||
getShardIteratorRequest.setStreamName(streamName);
|
getShardIteratorRequest.setStreamName(streamName);
|
||||||
|
getShardIteratorRequest.setStreamARN(streamArn);
|
||||||
getShardIteratorRequest.setShardId(shardId);
|
getShardIteratorRequest.setShardId(shardId);
|
||||||
getShardIteratorRequest.setShardIteratorType(iteratorType);
|
getShardIteratorRequest.setShardIteratorType(iteratorType);
|
||||||
getShardIteratorRequest.setStartingSequenceNumber(sequenceNumber);
|
getShardIteratorRequest.setStartingSequenceNumber(sequenceNumber);
|
||||||
|
|
@ -575,6 +596,7 @@ public class KinesisProxy implements IKinesisProxyExtended {
|
||||||
final GetShardIteratorRequest getShardIteratorRequest = new GetShardIteratorRequest();
|
final GetShardIteratorRequest getShardIteratorRequest = new GetShardIteratorRequest();
|
||||||
getShardIteratorRequest.setRequestCredentials(credentialsProvider.getCredentials());
|
getShardIteratorRequest.setRequestCredentials(credentialsProvider.getCredentials());
|
||||||
getShardIteratorRequest.setStreamName(streamName);
|
getShardIteratorRequest.setStreamName(streamName);
|
||||||
|
getShardIteratorRequest.setStreamARN(streamArn);
|
||||||
getShardIteratorRequest.setShardId(shardId);
|
getShardIteratorRequest.setShardId(shardId);
|
||||||
getShardIteratorRequest.setShardIteratorType(iteratorType);
|
getShardIteratorRequest.setShardIteratorType(iteratorType);
|
||||||
getShardIteratorRequest.setStartingSequenceNumber(null);
|
getShardIteratorRequest.setStartingSequenceNumber(null);
|
||||||
|
|
@ -591,6 +613,7 @@ public class KinesisProxy implements IKinesisProxyExtended {
|
||||||
final GetShardIteratorRequest getShardIteratorRequest = new GetShardIteratorRequest();
|
final GetShardIteratorRequest getShardIteratorRequest = new GetShardIteratorRequest();
|
||||||
getShardIteratorRequest.setRequestCredentials(credentialsProvider.getCredentials());
|
getShardIteratorRequest.setRequestCredentials(credentialsProvider.getCredentials());
|
||||||
getShardIteratorRequest.setStreamName(streamName);
|
getShardIteratorRequest.setStreamName(streamName);
|
||||||
|
getShardIteratorRequest.setStreamARN(streamArn);
|
||||||
getShardIteratorRequest.setShardId(shardId);
|
getShardIteratorRequest.setShardId(shardId);
|
||||||
getShardIteratorRequest.setShardIteratorType(ShardIteratorType.AT_TIMESTAMP);
|
getShardIteratorRequest.setShardIteratorType(ShardIteratorType.AT_TIMESTAMP);
|
||||||
getShardIteratorRequest.setStartingSequenceNumber(null);
|
getShardIteratorRequest.setStartingSequenceNumber(null);
|
||||||
|
|
@ -610,6 +633,7 @@ public class KinesisProxy implements IKinesisProxyExtended {
|
||||||
final PutRecordRequest putRecordRequest = new PutRecordRequest();
|
final PutRecordRequest putRecordRequest = new PutRecordRequest();
|
||||||
putRecordRequest.setRequestCredentials(credentialsProvider.getCredentials());
|
putRecordRequest.setRequestCredentials(credentialsProvider.getCredentials());
|
||||||
putRecordRequest.setStreamName(streamName);
|
putRecordRequest.setStreamName(streamName);
|
||||||
|
putRecordRequest.setStreamARN(streamArn);
|
||||||
putRecordRequest.setSequenceNumberForOrdering(exclusiveMinimumSequenceNumber);
|
putRecordRequest.setSequenceNumberForOrdering(exclusiveMinimumSequenceNumber);
|
||||||
putRecordRequest.setExplicitHashKey(explicitHashKey);
|
putRecordRequest.setExplicitHashKey(explicitHashKey);
|
||||||
putRecordRequest.setPartitionKey(partitionKey);
|
putRecordRequest.setPartitionKey(partitionKey);
|
||||||
|
|
@ -618,27 +642,4 @@ public class KinesisProxy implements IKinesisProxyExtended {
|
||||||
final PutRecordResult response = client.putRecord(putRecordRequest);
|
final PutRecordResult response = client.putRecord(putRecordRequest);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
|
||||||
static class ShardIterationState {
|
|
||||||
|
|
||||||
private List<Shard> shards;
|
|
||||||
private String lastShardId;
|
|
||||||
|
|
||||||
public ShardIterationState() {
|
|
||||||
shards = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void update(List<Shard> shards) {
|
|
||||||
if (shards == null || shards.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.shards.addAll(shards);
|
|
||||||
Shard lastShard = shards.get(shards.size() - 1);
|
|
||||||
if (lastShardId == null || lastShardId.compareTo(lastShard.getShardId()) < 0) {
|
|
||||||
lastShardId = lastShard.getShardId();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.amazonaws.services.kinesis.clientlibrary.utils;
|
||||||
|
|
||||||
|
import com.amazonaws.AmazonWebServiceResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to parse metadata from AWS requests.
|
||||||
|
*/
|
||||||
|
public class RequestUtil {
|
||||||
|
private static final String DEFAULT_REQUEST_ID = "NONE";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the requestId associated with a request.
|
||||||
|
*
|
||||||
|
* @param result
|
||||||
|
* @return the requestId for a request, or "NONE" if one is not available.
|
||||||
|
*/
|
||||||
|
public static String requestId(AmazonWebServiceResult result) {
|
||||||
|
if (result == null || result.getSdkResponseMetadata() == null || result.getSdkResponseMetadata().getRequestId() == null) {
|
||||||
|
return DEFAULT_REQUEST_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.getSdkResponseMetadata().getRequestId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -22,6 +22,7 @@ import com.amazonaws.services.dynamodbv2.model.AttributeAction;
|
||||||
import com.amazonaws.services.dynamodbv2.model.AttributeDefinition;
|
import com.amazonaws.services.dynamodbv2.model.AttributeDefinition;
|
||||||
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
|
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
|
||||||
import com.amazonaws.services.dynamodbv2.model.AttributeValueUpdate;
|
import com.amazonaws.services.dynamodbv2.model.AttributeValueUpdate;
|
||||||
|
import com.amazonaws.services.dynamodbv2.model.ComparisonOperator;
|
||||||
import com.amazonaws.services.dynamodbv2.model.ExpectedAttributeValue;
|
import com.amazonaws.services.dynamodbv2.model.ExpectedAttributeValue;
|
||||||
import com.amazonaws.services.dynamodbv2.model.KeySchemaElement;
|
import com.amazonaws.services.dynamodbv2.model.KeySchemaElement;
|
||||||
import com.amazonaws.services.kinesis.clientlibrary.types.ExtendedSequenceNumber;
|
import com.amazonaws.services.kinesis.clientlibrary.types.ExtendedSequenceNumber;
|
||||||
|
|
@ -68,6 +69,11 @@ public class KinesisClientLeaseSerializer implements ILeaseSerializer<KinesisCli
|
||||||
result.put(PENDING_CHECKPOINT_SUBSEQUENCE_KEY, DynamoUtils.createAttributeValue(lease.getPendingCheckpoint().getSubSequenceNumber()));
|
result.put(PENDING_CHECKPOINT_SUBSEQUENCE_KEY, DynamoUtils.createAttributeValue(lease.getPendingCheckpoint().getSubSequenceNumber()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(lease.getHashKeyRange() != null) {
|
||||||
|
result.put(STARTING_HASH_KEY, DynamoUtils.createAttributeValue(lease.getHashKeyRange().serializedStartingHashKey()));
|
||||||
|
result.put(ENDING_HASH_KEY, DynamoUtils.createAttributeValue(lease.getHashKeyRange().serializedEndingHashKey()));
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,6 +98,12 @@ public class KinesisClientLeaseSerializer implements ILeaseSerializer<KinesisCli
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final String startingHashKey, endingHashKey;
|
||||||
|
if (!Strings.isNullOrEmpty(startingHashKey = DynamoUtils.safeGetString(dynamoRecord, STARTING_HASH_KEY))
|
||||||
|
&& !Strings.isNullOrEmpty(endingHashKey = DynamoUtils.safeGetString(dynamoRecord, ENDING_HASH_KEY))) {
|
||||||
|
result.setHashKeyRange(HashKeyRangeForLease.deserialize(startingHashKey, endingHashKey));
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,6 +127,19 @@ public class KinesisClientLeaseSerializer implements ILeaseSerializer<KinesisCli
|
||||||
return baseSerializer.getDynamoLeaseOwnerExpectation(lease);
|
return baseSerializer.getDynamoLeaseOwnerExpectation(lease);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, ExpectedAttributeValue> getDynamoLeaseCheckpointExpectation(KinesisClientLease lease) {
|
||||||
|
Map<String, ExpectedAttributeValue> result = baseSerializer.getDynamoLeaseCheckpointExpectation(lease);
|
||||||
|
ExpectedAttributeValue eav;
|
||||||
|
|
||||||
|
if (!lease.getCheckpoint().equals(ExtendedSequenceNumber.SHARD_END)) {
|
||||||
|
eav = new ExpectedAttributeValue(DynamoUtils.createAttributeValue(ExtendedSequenceNumber.SHARD_END.getSequenceNumber()));
|
||||||
|
eav.setComparisonOperator(ComparisonOperator.NE);
|
||||||
|
result.put(CHECKPOINT_SEQUENCE_NUMBER_KEY, eav);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, ExpectedAttributeValue> getDynamoNonexistantExpectation() {
|
public Map<String, ExpectedAttributeValue> getDynamoNonexistantExpectation() {
|
||||||
return baseSerializer.getDynamoNonexistantExpectation();
|
return baseSerializer.getDynamoNonexistantExpectation();
|
||||||
|
|
@ -163,6 +188,11 @@ public class KinesisClientLeaseSerializer implements ILeaseSerializer<KinesisCli
|
||||||
result.put(CHILD_SHARD_IDS_KEY, new AttributeValueUpdate(DynamoUtils.createAttributeValue(lease.getChildShardIds()), AttributeAction.PUT));
|
result.put(CHILD_SHARD_IDS_KEY, new AttributeValueUpdate(DynamoUtils.createAttributeValue(lease.getChildShardIds()), AttributeAction.PUT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(lease.getHashKeyRange() != null) {
|
||||||
|
result.put(STARTING_HASH_KEY, new AttributeValueUpdate(DynamoUtils.createAttributeValue(lease.getHashKeyRange().serializedStartingHashKey()), AttributeAction.PUT));
|
||||||
|
result.put(ENDING_HASH_KEY, new AttributeValueUpdate(DynamoUtils.createAttributeValue(lease.getHashKeyRange().serializedEndingHashKey()), AttributeAction.PUT));
|
||||||
|
}
|
||||||
|
|
||||||
if (lease.getPendingCheckpoint() != null && !lease.getPendingCheckpoint().getSequenceNumber().isEmpty()) {
|
if (lease.getPendingCheckpoint() != null && !lease.getPendingCheckpoint().getSequenceNumber().isEmpty()) {
|
||||||
result.put(PENDING_CHECKPOINT_SEQUENCE_KEY, new AttributeValueUpdate(DynamoUtils.createAttributeValue(lease.getPendingCheckpoint().getSequenceNumber()), AttributeAction.PUT));
|
result.put(PENDING_CHECKPOINT_SEQUENCE_KEY, new AttributeValueUpdate(DynamoUtils.createAttributeValue(lease.getPendingCheckpoint().getSequenceNumber()), AttributeAction.PUT));
|
||||||
result.put(PENDING_CHECKPOINT_SUBSEQUENCE_KEY, new AttributeValueUpdate(DynamoUtils.createAttributeValue(lease.getPendingCheckpoint().getSubSequenceNumber()), AttributeAction.PUT));
|
result.put(PENDING_CHECKPOINT_SUBSEQUENCE_KEY, new AttributeValueUpdate(DynamoUtils.createAttributeValue(lease.getPendingCheckpoint().getSubSequenceNumber()), AttributeAction.PUT));
|
||||||
|
|
@ -181,7 +211,10 @@ public class KinesisClientLeaseSerializer implements ILeaseSerializer<KinesisCli
|
||||||
|
|
||||||
switch (updateField) {
|
switch (updateField) {
|
||||||
case CHILD_SHARDS:
|
case CHILD_SHARDS:
|
||||||
// TODO: Implement update fields for child shards
|
if (!CollectionUtils.isNullOrEmpty(lease.getChildShardIds())) {
|
||||||
|
result.put(CHILD_SHARD_IDS_KEY, new AttributeValueUpdate(DynamoUtils.createAttributeValue(
|
||||||
|
lease.getChildShardIds()), AttributeAction.PUT));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case HASH_KEY_RANGE:
|
case HASH_KEY_RANGE:
|
||||||
if (lease.getHashKeyRange() != null) {
|
if (lease.getHashKeyRange() != null) {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ package com.amazonaws.services.kinesis.leases.impl;
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import com.amazonaws.services.kinesis.clientlibrary.lib.worker.KinesisClientLibConfiguration;
|
||||||
import com.amazonaws.services.kinesis.clientlibrary.lib.worker.ShardInfo;
|
import com.amazonaws.services.kinesis.clientlibrary.lib.worker.ShardInfo;
|
||||||
import com.amazonaws.services.kinesis.clientlibrary.proxies.IKinesisProxy;
|
import com.amazonaws.services.kinesis.clientlibrary.proxies.IKinesisProxy;
|
||||||
import com.amazonaws.services.kinesis.clientlibrary.types.ExtendedSequenceNumber;
|
import com.amazonaws.services.kinesis.clientlibrary.types.ExtendedSequenceNumber;
|
||||||
|
|
@ -81,10 +82,8 @@ public class LeaseCleanupManager {
|
||||||
@Getter
|
@Getter
|
||||||
private volatile boolean isRunning = false;
|
private volatile boolean isRunning = false;
|
||||||
|
|
||||||
private static LeaseCleanupManager instance;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory method to return a singleton instance of {@link LeaseCleanupManager}.
|
* Method to return a new instance of {@link LeaseCleanupManager}.
|
||||||
* @param kinesisProxy
|
* @param kinesisProxy
|
||||||
* @param leaseManager
|
* @param leaseManager
|
||||||
* @param deletionThreadPool
|
* @param deletionThreadPool
|
||||||
|
|
@ -96,17 +95,13 @@ public class LeaseCleanupManager {
|
||||||
* @param maxRecords
|
* @param maxRecords
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static LeaseCleanupManager createOrGetInstance(IKinesisProxy kinesisProxy, ILeaseManager leaseManager,
|
public static LeaseCleanupManager newInstance(IKinesisProxy kinesisProxy, ILeaseManager leaseManager,
|
||||||
ScheduledExecutorService deletionThreadPool, IMetricsFactory metricsFactory,
|
ScheduledExecutorService deletionThreadPool, IMetricsFactory metricsFactory,
|
||||||
boolean cleanupLeasesUponShardCompletion, long leaseCleanupIntervalMillis,
|
boolean cleanupLeasesUponShardCompletion, long leaseCleanupIntervalMillis,
|
||||||
long completedLeaseCleanupIntervalMillis, long garbageLeaseCleanupIntervalMillis,
|
long completedLeaseCleanupIntervalMillis, long garbageLeaseCleanupIntervalMillis,
|
||||||
int maxRecords) {
|
int maxRecords) {
|
||||||
if (instance == null) {
|
return new LeaseCleanupManager(kinesisProxy, leaseManager, deletionThreadPool, metricsFactory, cleanupLeasesUponShardCompletion,
|
||||||
instance = new LeaseCleanupManager(kinesisProxy, leaseManager, deletionThreadPool, metricsFactory, cleanupLeasesUponShardCompletion,
|
leaseCleanupIntervalMillis, completedLeaseCleanupIntervalMillis, garbageLeaseCleanupIntervalMillis, maxRecords);
|
||||||
leaseCleanupIntervalMillis, completedLeaseCleanupIntervalMillis, garbageLeaseCleanupIntervalMillis, maxRecords);
|
|
||||||
}
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -126,6 +121,23 @@ public class LeaseCleanupManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the lease cleanup thread, which is scheduled periodically as specified by
|
||||||
|
* {@link LeaseCleanupManager#leaseCleanupIntervalMillis}
|
||||||
|
*/
|
||||||
|
public void shutdown() {
|
||||||
|
if (isRunning) {
|
||||||
|
LOG.info("Stopping the lease cleanup thread.");
|
||||||
|
completedLeaseStopwatch.stop();
|
||||||
|
garbageLeaseStopwatch.stop();
|
||||||
|
deletionThreadPool.shutdown();
|
||||||
|
|
||||||
|
isRunning = false;
|
||||||
|
} else {
|
||||||
|
LOG.info("Lease cleanup thread already stopped.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enqueues a lease for deletion without check for duplicate entry. Use {@link #isEnqueuedForDeletion}
|
* Enqueues a lease for deletion without check for duplicate entry. Use {@link #isEnqueuedForDeletion}
|
||||||
* for checking the duplicate entries.
|
* for checking the duplicate entries.
|
||||||
|
|
@ -181,6 +193,7 @@ public class LeaseCleanupManager {
|
||||||
boolean alreadyCheckedForGarbageCollection = false;
|
boolean alreadyCheckedForGarbageCollection = false;
|
||||||
boolean wereChildShardsPresent = false;
|
boolean wereChildShardsPresent = false;
|
||||||
boolean wasResourceNotFound = false;
|
boolean wasResourceNotFound = false;
|
||||||
|
String cleanupFailureReason = "";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (cleanupLeasesUponShardCompletion && timeToCheckForCompletedShard) {
|
if (cleanupLeasesUponShardCompletion && timeToCheckForCompletedShard) {
|
||||||
|
|
@ -189,49 +202,57 @@ public class LeaseCleanupManager {
|
||||||
Set<String> childShardKeys = leaseFromDDB.getChildShardIds();
|
Set<String> childShardKeys = leaseFromDDB.getChildShardIds();
|
||||||
if (CollectionUtils.isNullOrEmpty(childShardKeys)) {
|
if (CollectionUtils.isNullOrEmpty(childShardKeys)) {
|
||||||
try {
|
try {
|
||||||
|
// throws ResourceNotFoundException
|
||||||
childShardKeys = getChildShardsFromService(shardInfo);
|
childShardKeys = getChildShardsFromService(shardInfo);
|
||||||
|
|
||||||
if (CollectionUtils.isNullOrEmpty(childShardKeys)) {
|
if (CollectionUtils.isNullOrEmpty(childShardKeys)) {
|
||||||
LOG.error("No child shards returned from service for shard " + shardInfo.getShardId());
|
LOG.error("No child shards returned from service for shard " + shardInfo.getShardId());
|
||||||
|
// If no children shard is found in DDB and from service, then do not delete the lease
|
||||||
|
throw new InvalidStateException("No child shards found for this supposedly " +
|
||||||
|
"closed shard in both local DDB and in service " + shardInfo.getShardId());
|
||||||
} else {
|
} else {
|
||||||
wereChildShardsPresent = true;
|
wereChildShardsPresent = true;
|
||||||
updateLeaseWithChildShards(leasePendingDeletion, childShardKeys);
|
updateLeaseWithChildShards(leasePendingDeletion, childShardKeys);
|
||||||
}
|
}
|
||||||
} catch (ResourceNotFoundException e) {
|
|
||||||
throw e;
|
|
||||||
} finally {
|
} finally {
|
||||||
|
// We rely on resource presence in service for garbage collection. Since we already
|
||||||
|
// made a call to getChildShardsFromService we would be coming to know if the resource
|
||||||
|
// is present of not. In latter case, we would throw ResourceNotFoundException, which is
|
||||||
|
// handled in catch block.
|
||||||
alreadyCheckedForGarbageCollection = true;
|
alreadyCheckedForGarbageCollection = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
wereChildShardsPresent = true;
|
wereChildShardsPresent = true;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
cleanedUpCompletedLease = cleanupLeaseForCompletedShard(lease, childShardKeys);
|
final CompletedShardResult completedShardResult = cleanupLeaseForCompletedShard(lease, childShardKeys);
|
||||||
|
cleanedUpCompletedLease = completedShardResult.cleanedUp();
|
||||||
|
cleanupFailureReason = completedShardResult.failureMsg();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Suppressing the exception here, so that we can attempt for garbage cleanup.
|
// Suppressing the exception here, so that we can attempt for garbage cleanup.
|
||||||
LOG.warn("Unable to cleanup lease for shard " + shardInfo.getShardId());
|
LOG.warn("Unable to cleanup lease for shard " + shardInfo.getShardId() + " due to " + e.getMessage());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG.info("Lease not present in lease table while cleaning the shard " + shardInfo.getShardId());
|
LOG.info("Lease not present in lease table while cleaning the shard " + shardInfo.getShardId());
|
||||||
cleanedUpCompletedLease = true;
|
cleanedUpCompletedLease = true;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
cleanupFailureReason = "Configuration/Interval condition not satisfied to execute lease cleanup this cycle";
|
||||||
}
|
}
|
||||||
|
if (!cleanedUpCompletedLease && !alreadyCheckedForGarbageCollection && timeToCheckForGarbageShard) {
|
||||||
if (!alreadyCheckedForGarbageCollection && timeToCheckForGarbageShard) {
|
// throws ResourceNotFoundException
|
||||||
try {
|
wereChildShardsPresent = !CollectionUtils
|
||||||
wereChildShardsPresent = !CollectionUtils
|
|
||||||
.isNullOrEmpty(getChildShardsFromService(shardInfo));
|
.isNullOrEmpty(getChildShardsFromService(shardInfo));
|
||||||
} catch (ResourceNotFoundException e) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (ResourceNotFoundException e) {
|
} catch (ResourceNotFoundException e) {
|
||||||
wasResourceNotFound = true;
|
wasResourceNotFound = true;
|
||||||
cleanedUpGarbageLease = cleanupLeaseForGarbageShard(lease);
|
cleanedUpGarbageLease = cleanupLeaseForGarbageShard(lease);
|
||||||
|
cleanupFailureReason = cleanedUpGarbageLease ? "" : "DDB Lease Deletion Failed";
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.warn("Unable to cleanup lease for shard " + shardInfo.getShardId() + " : " + e.getMessage());
|
||||||
|
cleanupFailureReason = e.getMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new LeaseCleanupResult(cleanedUpCompletedLease, cleanedUpGarbageLease, wereChildShardsPresent,
|
return new LeaseCleanupResult(cleanedUpCompletedLease, cleanedUpGarbageLease, wereChildShardsPresent,
|
||||||
wasResourceNotFound);
|
wasResourceNotFound, cleanupFailureReason);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> getChildShardsFromService(ShardInfo shardInfo) {
|
private Set<String> getChildShardsFromService(ShardInfo shardInfo) {
|
||||||
|
|
@ -239,12 +260,16 @@ public class LeaseCleanupManager {
|
||||||
return kinesisProxy.get(iterator, maxRecords).getChildShards().stream().map(c -> c.getShardId()).collect(Collectors.toSet());
|
return kinesisProxy.get(iterator, maxRecords).getChildShards().stream().map(c -> c.getShardId()).collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// A lease that ended with SHARD_END from ResourceNotFoundException is safe to delete if it no longer exists in the
|
// A lease that ended with SHARD_END from ResourceNotFoundException is safe to delete if it no longer exists in the
|
||||||
// stream (known explicitly from ResourceNotFound being thrown when processing this shard),
|
// stream (known explicitly from ResourceNotFound being thrown when processing this shard),
|
||||||
private boolean cleanupLeaseForGarbageShard(KinesisClientLease lease) throws DependencyException, ProvisionedThroughputException, InvalidStateException {
|
private boolean cleanupLeaseForGarbageShard(KinesisClientLease lease) throws DependencyException, ProvisionedThroughputException, InvalidStateException {
|
||||||
LOG.info("Deleting lease " + lease.getLeaseKey() + " as it is not present in the stream.");
|
LOG.info("Deleting lease " + lease.getLeaseKey() + " as it is not present in the stream.");
|
||||||
leaseManager.deleteLease(lease);
|
try {
|
||||||
|
leaseManager.deleteLease(lease);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.warn("Lease deletion failed for " + lease.getLeaseKey() + " due to " + e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -264,8 +289,9 @@ public class LeaseCleanupManager {
|
||||||
// We should only be deleting the current shard's lease if
|
// We should only be deleting the current shard's lease if
|
||||||
// 1. All of its children are currently being processed, i.e their checkpoint is not TRIM_HORIZON or AT_TIMESTAMP.
|
// 1. All of its children are currently being processed, i.e their checkpoint is not TRIM_HORIZON or AT_TIMESTAMP.
|
||||||
// 2. Its parent shard lease(s) have already been deleted.
|
// 2. Its parent shard lease(s) have already been deleted.
|
||||||
private boolean cleanupLeaseForCompletedShard(KinesisClientLease lease, Set<String> childShardLeaseKeys)
|
private CompletedShardResult cleanupLeaseForCompletedShard(KinesisClientLease lease, Set<String> childShardLeaseKeys)
|
||||||
throws DependencyException, ProvisionedThroughputException, InvalidStateException, IllegalStateException {
|
throws DependencyException, ProvisionedThroughputException, InvalidStateException, IllegalStateException {
|
||||||
|
|
||||||
final Set<String> processedChildShardLeaseKeys = new HashSet<>();
|
final Set<String> processedChildShardLeaseKeys = new HashSet<>();
|
||||||
|
|
||||||
for (String childShardLeaseKey : childShardLeaseKeys) {
|
for (String childShardLeaseKey : childShardLeaseKeys) {
|
||||||
|
|
@ -281,14 +307,17 @@ public class LeaseCleanupManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!allParentShardLeasesDeleted(lease) || !Objects.equals(childShardLeaseKeys, processedChildShardLeaseKeys)) {
|
boolean parentShardsDeleted = allParentShardLeasesDeleted(lease);
|
||||||
return false;
|
boolean childrenStartedProcessing = Objects.equals(childShardLeaseKeys, processedChildShardLeaseKeys);
|
||||||
|
|
||||||
|
if (!parentShardsDeleted || !childrenStartedProcessing) {
|
||||||
|
return new CompletedShardResult(false, !parentShardsDeleted ? "Parent shard(s) not deleted yet" : "Child shard(s) yet to begin processing");
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG.info("Deleting lease " + lease.getLeaseKey() + " as it has been completely processed and processing of child shard(s) has begun.");
|
LOG.info("Deleting lease " + lease.getLeaseKey() + " as it has been completely processed and processing of child shard(s) has begun.");
|
||||||
leaseManager.deleteLease(lease);
|
leaseManager.deleteLease(lease);
|
||||||
|
|
||||||
return true;
|
return new CompletedShardResult(true, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateLeaseWithChildShards(LeasePendingDeletion leasePendingDeletion, Set<String> childShardKeys)
|
private void updateLeaseWithChildShards(LeasePendingDeletion leasePendingDeletion, Set<String> childShardKeys)
|
||||||
|
|
@ -296,7 +325,7 @@ public class LeaseCleanupManager {
|
||||||
final KinesisClientLease updatedLease = leasePendingDeletion.lease();
|
final KinesisClientLease updatedLease = leasePendingDeletion.lease();
|
||||||
updatedLease.setChildShardIds(childShardKeys);
|
updatedLease.setChildShardIds(childShardKeys);
|
||||||
|
|
||||||
leaseManager.updateLease(updatedLease);
|
leaseManager.updateLeaseWithMetaInfo(updatedLease, UpdateField.CHILD_SHARDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
|
@ -364,9 +393,17 @@ public class LeaseCleanupManager {
|
||||||
boolean cleanedUpGarbageLease;
|
boolean cleanedUpGarbageLease;
|
||||||
boolean wereChildShardsPresent;
|
boolean wereChildShardsPresent;
|
||||||
boolean wasResourceNotFound;
|
boolean wasResourceNotFound;
|
||||||
|
String cleanupFailureReason;
|
||||||
|
|
||||||
public boolean leaseCleanedUp() {
|
public boolean leaseCleanedUp() {
|
||||||
return cleanedUpCompletedLease | cleanedUpGarbageLease;
|
return cleanedUpCompletedLease | cleanedUpGarbageLease;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@Value
|
||||||
|
@Accessors(fluent = true)
|
||||||
|
private static class CompletedShardResult {
|
||||||
|
boolean cleanedUp;
|
||||||
|
String failureMsg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,6 +20,7 @@ import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import com.amazonaws.services.dynamodbv2.model.BillingMode;
|
import com.amazonaws.services.dynamodbv2.model.BillingMode;
|
||||||
|
import com.amazonaws.services.dynamodbv2.model.ExpectedAttributeValue;
|
||||||
import com.amazonaws.services.kinesis.clientlibrary.lib.worker.KinesisClientLibConfiguration;
|
import com.amazonaws.services.kinesis.clientlibrary.lib.worker.KinesisClientLibConfiguration;
|
||||||
import com.amazonaws.services.kinesis.leases.util.DynamoUtils;
|
import com.amazonaws.services.kinesis.leases.util.DynamoUtils;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
|
|
@ -582,7 +583,9 @@ public class LeaseManager<T extends Lease> implements ILeaseManager<T> {
|
||||||
UpdateItemRequest request = new UpdateItemRequest();
|
UpdateItemRequest request = new UpdateItemRequest();
|
||||||
request.setTableName(table);
|
request.setTableName(table);
|
||||||
request.setKey(serializer.getDynamoHashKey(lease));
|
request.setKey(serializer.getDynamoHashKey(lease));
|
||||||
request.setExpected(serializer.getDynamoLeaseCounterExpectation(lease));
|
Map<String, ExpectedAttributeValue> expectations = serializer.getDynamoLeaseCounterExpectation(lease);
|
||||||
|
expectations.putAll(serializer.getDynamoLeaseCheckpointExpectation(lease));
|
||||||
|
request.setExpected(expectations);
|
||||||
|
|
||||||
Map<String, AttributeValueUpdate> updates = serializer.getDynamoLeaseCounterUpdate(lease);
|
Map<String, AttributeValueUpdate> updates = serializer.getDynamoLeaseCounterUpdate(lease);
|
||||||
updates.putAll(serializer.getDynamoUpdateLeaseUpdate(lease));
|
updates.putAll(serializer.getDynamoUpdateLeaseUpdate(lease));
|
||||||
|
|
@ -624,7 +627,6 @@ public class LeaseManager<T extends Lease> implements ILeaseManager<T> {
|
||||||
request.setExpected(serializer.getDynamoExistentExpectation(lease.getLeaseKey()));
|
request.setExpected(serializer.getDynamoExistentExpectation(lease.getLeaseKey()));
|
||||||
|
|
||||||
Map<String, AttributeValueUpdate> updates = serializer.getDynamoUpdateLeaseUpdate(lease, updateField);
|
Map<String, AttributeValueUpdate> updates = serializer.getDynamoUpdateLeaseUpdate(lease, updateField);
|
||||||
updates.putAll(serializer.getDynamoUpdateLeaseUpdate(lease));
|
|
||||||
request.setAttributeUpdates(updates);
|
request.setAttributeUpdates(updates);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,11 @@ public class LeaseSerializer implements ILeaseSerializer<Lease> {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, ExpectedAttributeValue> getDynamoLeaseCheckpointExpectation(final Lease lease) {
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, ExpectedAttributeValue> getDynamoNonexistantExpectation() {
|
public Map<String, ExpectedAttributeValue> getDynamoNonexistantExpectation() {
|
||||||
Map<String, ExpectedAttributeValue> result = new HashMap<String, ExpectedAttributeValue>();
|
Map<String, ExpectedAttributeValue> result = new HashMap<String, ExpectedAttributeValue>();
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,14 @@ public interface ILeaseSerializer<T extends Lease> {
|
||||||
*/
|
*/
|
||||||
public Map<String, ExpectedAttributeValue> getDynamoLeaseOwnerExpectation(T lease);
|
public Map<String, ExpectedAttributeValue> getDynamoLeaseOwnerExpectation(T lease);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param lease
|
||||||
|
* @return the attribute value map asserting that the checkpoint state is as expected.
|
||||||
|
*/
|
||||||
|
default Map<String, ExpectedAttributeValue> getDynamoLeaseCheckpointExpectation(T lease) {
|
||||||
|
throw new UnsupportedOperationException("DynamoLeaseCheckpointExpectation is not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the attribute value map asserting that a lease does not exist.
|
* @return the attribute value map asserting that a lease does not exist.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@
|
||||||
*/
|
*/
|
||||||
package com.amazonaws.services.kinesis.clientlibrary.lib.worker;
|
package com.amazonaws.services.kinesis.clientlibrary.lib.worker;
|
||||||
|
|
||||||
import static com.amazonaws.services.kinesis.clientlibrary.lib.worker.ConsumerStates.ConsumerState;
|
import static com.amazonaws.services.kinesis.clientlibrary.lib.worker.KinesisConsumerStates.ConsumerState;
|
||||||
import static com.amazonaws.services.kinesis.clientlibrary.lib.worker.ConsumerStates.ShardConsumerState;
|
import static com.amazonaws.services.kinesis.clientlibrary.lib.worker.KinesisConsumerStates.ShardConsumerState;
|
||||||
import static org.hamcrest.CoreMatchers.equalTo;
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
import static org.hamcrest.CoreMatchers.nullValue;
|
import static org.hamcrest.CoreMatchers.nullValue;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
|
@ -50,7 +50,7 @@ import com.amazonaws.services.kinesis.leases.interfaces.ILeaseManager;
|
||||||
public class ConsumerStatesTest {
|
public class ConsumerStatesTest {
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private ShardConsumer consumer;
|
private KinesisShardConsumer consumer;
|
||||||
@Mock
|
@Mock
|
||||||
private StreamConfig streamConfig;
|
private StreamConfig streamConfig;
|
||||||
@Mock
|
@Mock
|
||||||
|
|
@ -251,9 +251,9 @@ public class ConsumerStatesTest {
|
||||||
equalTo((IRecordProcessorCheckpointer) recordProcessorCheckpointer)));
|
equalTo((IRecordProcessorCheckpointer) recordProcessorCheckpointer)));
|
||||||
assertThat(task, shutdownReqTask(ShutdownNotification.class, "shutdownNotification", equalTo(shutdownNotification)));
|
assertThat(task, shutdownReqTask(ShutdownNotification.class, "shutdownNotification", equalTo(shutdownNotification)));
|
||||||
|
|
||||||
assertThat(state.successTransition(), equalTo(ConsumerStates.SHUTDOWN_REQUEST_COMPLETION_STATE));
|
assertThat(state.successTransition(), equalTo(KinesisConsumerStates.SHUTDOWN_REQUEST_COMPLETION_STATE));
|
||||||
assertThat(state.shutdownTransition(ShutdownReason.REQUESTED),
|
assertThat(state.shutdownTransition(ShutdownReason.REQUESTED),
|
||||||
equalTo(ConsumerStates.SHUTDOWN_REQUEST_COMPLETION_STATE));
|
equalTo(KinesisConsumerStates.SHUTDOWN_REQUEST_COMPLETION_STATE));
|
||||||
assertThat(state.shutdownTransition(ShutdownReason.ZOMBIE),
|
assertThat(state.shutdownTransition(ShutdownReason.ZOMBIE),
|
||||||
equalTo(ShardConsumerState.SHUTTING_DOWN.getConsumerState()));
|
equalTo(ShardConsumerState.SHUTTING_DOWN.getConsumerState()));
|
||||||
assertThat(state.shutdownTransition(ShutdownReason.TERMINATE),
|
assertThat(state.shutdownTransition(ShutdownReason.TERMINATE),
|
||||||
|
|
@ -266,7 +266,7 @@ public class ConsumerStatesTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shutdownRequestCompleteStateTest() {
|
public void shutdownRequestCompleteStateTest() {
|
||||||
ConsumerState state = ConsumerStates.SHUTDOWN_REQUEST_COMPLETION_STATE;
|
ConsumerState state = KinesisConsumerStates.SHUTDOWN_REQUEST_COMPLETION_STATE;
|
||||||
|
|
||||||
assertThat(state.createTask(consumer), nullValue());
|
assertThat(state.createTask(consumer), nullValue());
|
||||||
|
|
||||||
|
|
@ -345,9 +345,9 @@ public class ConsumerStatesTest {
|
||||||
verify(shutdownNotification, never()).shutdownComplete();
|
verify(shutdownNotification, never()).shutdownComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
static <ValueType> ReflectionPropertyMatcher<ShutdownTask, ValueType> shutdownTask(Class<ValueType> valueTypeClass,
|
static <ValueType> ReflectionPropertyMatcher<KinesisShutdownTask, ValueType> shutdownTask(Class<ValueType> valueTypeClass,
|
||||||
String propertyName, Matcher<ValueType> matcher) {
|
String propertyName, Matcher<ValueType> matcher) {
|
||||||
return taskWith(ShutdownTask.class, valueTypeClass, propertyName, matcher);
|
return taskWith(KinesisShutdownTask.class, valueTypeClass, propertyName, matcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
static <ValueType> ReflectionPropertyMatcher<ShutdownNotificationTask, ValueType> shutdownReqTask(
|
static <ValueType> ReflectionPropertyMatcher<ShutdownNotificationTask, ValueType> shutdownReqTask(
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ public class GracefulShutdownCoordinatorTest {
|
||||||
@Mock
|
@Mock
|
||||||
private Callable<GracefulShutdownContext> contextCallable;
|
private Callable<GracefulShutdownContext> contextCallable;
|
||||||
@Mock
|
@Mock
|
||||||
private ConcurrentMap<ShardInfo, ShardConsumer> shardInfoConsumerMap;
|
private ConcurrentMap<ShardInfo, IShardConsumer> shardInfoConsumerMap;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAllShutdownCompletedAlready() throws Exception {
|
public void testAllShutdownCompletedAlready() throws Exception {
|
||||||
|
|
|
||||||
|
|
@ -20,13 +20,16 @@ import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import com.amazonaws.services.dynamodbv2.model.BillingMode;
|
import com.amazonaws.services.dynamodbv2.model.BillingMode;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
import com.amazonaws.ClientConfiguration;
|
import com.amazonaws.ClientConfiguration;
|
||||||
|
import com.amazonaws.arn.Arn;
|
||||||
import com.amazonaws.auth.AWSCredentialsProvider;
|
import com.amazonaws.auth.AWSCredentialsProvider;
|
||||||
import com.amazonaws.regions.Region;
|
import com.amazonaws.regions.Region;
|
||||||
import com.amazonaws.regions.RegionUtils;
|
import com.amazonaws.regions.RegionUtils;
|
||||||
|
|
@ -34,6 +37,7 @@ import com.amazonaws.services.cloudwatch.AmazonCloudWatchClient;
|
||||||
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
|
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
|
||||||
import com.amazonaws.services.kinesis.AmazonKinesisClient;
|
import com.amazonaws.services.kinesis.AmazonKinesisClient;
|
||||||
import com.amazonaws.services.kinesis.clientlibrary.interfaces.IRecordProcessorFactory;
|
import com.amazonaws.services.kinesis.clientlibrary.interfaces.IRecordProcessorFactory;
|
||||||
|
import com.amazonaws.services.kinesis.clientlibrary.lib.worker.SimpleRecordsFetcherFactory;
|
||||||
import com.amazonaws.services.kinesis.metrics.interfaces.MetricsLevel;
|
import com.amazonaws.services.kinesis.metrics.interfaces.MetricsLevel;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
|
||||||
|
|
@ -46,8 +50,28 @@ public class KinesisClientLibConfigurationTest {
|
||||||
private static final long TEST_VALUE_LONG = 1000L;
|
private static final long TEST_VALUE_LONG = 1000L;
|
||||||
private static final int TEST_VALUE_INT = 1000;
|
private static final int TEST_VALUE_INT = 1000;
|
||||||
private static final int PARAMETER_COUNT = 6;
|
private static final int PARAMETER_COUNT = 6;
|
||||||
|
private static final String ACCOUNT_ID = "123456789012";
|
||||||
|
|
||||||
private static final String TEST_STRING = "TestString";
|
private static final String TEST_STRING = "TestString";
|
||||||
|
private static final Arn TEST_ARN = Arn.builder()
|
||||||
|
.withPartition("aws")
|
||||||
|
.withService("kinesis")
|
||||||
|
.withRegion("us-east-1")
|
||||||
|
.withAccountId(ACCOUNT_ID)
|
||||||
|
.withResource("stream/" + TEST_STRING)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalid steamARN due to invalid service. This is a sample used for testing.
|
||||||
|
* @see KinesisClientLibConfigurationTest#testWithInvalidStreamArnsThrowsException() for more examples
|
||||||
|
*/
|
||||||
|
private static final Arn INVALID_TEST_ARN = Arn.builder()
|
||||||
|
.withPartition("aws")
|
||||||
|
.withService("dynamodb")
|
||||||
|
.withRegion("us-east-1")
|
||||||
|
.withAccountId(ACCOUNT_ID)
|
||||||
|
.withResource("stream/" + TEST_STRING)
|
||||||
|
.build();
|
||||||
private static final String ALTER_STRING = "AlterString";
|
private static final String ALTER_STRING = "AlterString";
|
||||||
|
|
||||||
// We don't want any of these tests to run checkpoint validation
|
// We don't want any of these tests to run checkpoint validation
|
||||||
|
|
@ -62,32 +86,11 @@ public class KinesisClientLibConfigurationTest {
|
||||||
new KinesisClientLibConfiguration(TEST_STRING, TEST_STRING, null, TEST_STRING);
|
new KinesisClientLibConfiguration(TEST_STRING, TEST_STRING, null, TEST_STRING);
|
||||||
|
|
||||||
// Test constructor with all valid arguments.
|
// Test constructor with all valid arguments.
|
||||||
config =
|
config = buildKinesisClientLibConfiguration(TEST_STRING);
|
||||||
new KinesisClientLibConfiguration(TEST_STRING,
|
|
||||||
TEST_STRING,
|
// Test constructor with streamArn with all valid arguments.
|
||||||
TEST_STRING,
|
config = buildKinesisClientLibConfiguration(TEST_ARN);
|
||||||
TEST_STRING,
|
Assert.assertEquals(config.getStreamName(), TEST_STRING);
|
||||||
InitialPositionInStream.LATEST,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
TEST_VALUE_LONG,
|
|
||||||
TEST_STRING,
|
|
||||||
TEST_VALUE_INT,
|
|
||||||
TEST_VALUE_LONG,
|
|
||||||
false,
|
|
||||||
TEST_VALUE_LONG,
|
|
||||||
TEST_VALUE_LONG,
|
|
||||||
true,
|
|
||||||
new ClientConfiguration(),
|
|
||||||
new ClientConfiguration(),
|
|
||||||
new ClientConfiguration(),
|
|
||||||
TEST_VALUE_LONG,
|
|
||||||
TEST_VALUE_LONG,
|
|
||||||
TEST_VALUE_INT,
|
|
||||||
skipCheckpointValidationValue,
|
|
||||||
null,
|
|
||||||
TEST_VALUE_LONG, BillingMode.PROVISIONED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -168,6 +171,12 @@ public class KinesisClientLibConfigurationTest {
|
||||||
}
|
}
|
||||||
intValues[i] = TEST_VALUE_INT;
|
intValues[i] = TEST_VALUE_INT;
|
||||||
}
|
}
|
||||||
|
// Test constructor with invalid streamArn
|
||||||
|
try {
|
||||||
|
config = buildKinesisClientLibConfiguration(INVALID_TEST_ARN);
|
||||||
|
} catch(IllegalArgumentException e) {
|
||||||
|
System.out.println(e.getMessage());
|
||||||
|
}
|
||||||
Assert.assertTrue("KCLConfiguration should return null when using negative arguments", config == null);
|
Assert.assertTrue("KCLConfiguration should return null when using negative arguments", config == null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -370,4 +379,111 @@ public class KinesisClientLibConfigurationTest {
|
||||||
config = config.withIgnoreUnexpectedChildShards(true);
|
config = config.withIgnoreUnexpectedChildShards(true);
|
||||||
assertTrue(config.shouldIgnoreUnexpectedChildShards());
|
assertTrue(config.shouldIgnoreUnexpectedChildShards());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWithValidStreamArnsSucceed() {
|
||||||
|
List<String> validArnList = Arrays.asList(
|
||||||
|
"arn:aws:kinesis:us-east-1:123456789012:stream/123stream-name123",
|
||||||
|
"arn:aws-china:kinesis:us-east-2:123456789012:stream/stream-name",
|
||||||
|
"arn:aws-us-gov:kinesis:us-east-2:123456789012:stream/stream-name"
|
||||||
|
);
|
||||||
|
|
||||||
|
KinesisClientLibConfiguration config =
|
||||||
|
new KinesisClientLibConfiguration("TestApplication", "TestStream", null, "TestWorker");
|
||||||
|
|
||||||
|
for (final String arn : validArnList) {
|
||||||
|
config.withStreamArn(Arn.fromString(arn));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWithInvalidStreamArnsThrowsException() {
|
||||||
|
List<String> invalidArnList = Arrays.asList(
|
||||||
|
"arn:abc:kinesis:us-east-1:123456789012:stream/stream-name", //invalid partition
|
||||||
|
"arn:aws:dynamnodb:us-east-1:123456789012:stream/stream-name", // Kinesis ARN, but with a non-Kinesis service
|
||||||
|
"arn:aws:kinesis::123456789012:stream/stream-name", // missing region
|
||||||
|
"arn:aws:kinesis:us-east-1::stream/stream-name", // missing account id
|
||||||
|
"arn:aws:kinesis:us-east-1:123456789:stream/stream-name", // account id not 12 digits
|
||||||
|
"arn:aws:kinesis:us-east-1:123456789abc:stream/stream-name", // 12char alphanumeric account id
|
||||||
|
"arn:aws:kinesis:us-east-1:123456789012:table/table-name", // incorrect resource type
|
||||||
|
"arn:aws:dynamodb:us-east-1:123456789012:table/myDynamoDBTable" // valid arn but not a stream
|
||||||
|
);
|
||||||
|
|
||||||
|
KinesisClientLibConfiguration config =
|
||||||
|
new KinesisClientLibConfiguration("TestApplication", "TestStream", null, "TestWorker");
|
||||||
|
|
||||||
|
for (final String arnString : invalidArnList) {
|
||||||
|
Arn arn = Arn.fromString(arnString);
|
||||||
|
try {
|
||||||
|
config.withStreamArn(arn);
|
||||||
|
fail("Arn " + arn + " should have thrown an IllegalArgumentException");
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private KinesisClientLibConfiguration buildKinesisClientLibConfiguration(Arn streamArn) {
|
||||||
|
return new KinesisClientLibConfiguration(TEST_STRING,
|
||||||
|
streamArn,
|
||||||
|
TEST_STRING,
|
||||||
|
TEST_STRING,
|
||||||
|
InitialPositionInStream.LATEST,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
TEST_VALUE_LONG,
|
||||||
|
TEST_STRING,
|
||||||
|
TEST_VALUE_INT,
|
||||||
|
TEST_VALUE_LONG,
|
||||||
|
false,
|
||||||
|
TEST_VALUE_LONG,
|
||||||
|
TEST_VALUE_LONG,
|
||||||
|
true,
|
||||||
|
new ClientConfiguration(),
|
||||||
|
new ClientConfiguration(),
|
||||||
|
new ClientConfiguration(),
|
||||||
|
TEST_VALUE_LONG,
|
||||||
|
TEST_VALUE_LONG,
|
||||||
|
TEST_VALUE_INT,
|
||||||
|
skipCheckpointValidationValue,
|
||||||
|
null,
|
||||||
|
TEST_VALUE_LONG, BillingMode.PROVISIONED,
|
||||||
|
new SimpleRecordsFetcherFactory(),
|
||||||
|
TEST_VALUE_LONG,
|
||||||
|
TEST_VALUE_LONG,
|
||||||
|
TEST_VALUE_LONG);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KinesisClientLibConfiguration buildKinesisClientLibConfiguration(String streamName) {
|
||||||
|
return new KinesisClientLibConfiguration(TEST_STRING,
|
||||||
|
streamName,
|
||||||
|
TEST_STRING,
|
||||||
|
TEST_STRING,
|
||||||
|
InitialPositionInStream.LATEST,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
TEST_VALUE_LONG,
|
||||||
|
TEST_STRING,
|
||||||
|
TEST_VALUE_INT,
|
||||||
|
TEST_VALUE_LONG,
|
||||||
|
false,
|
||||||
|
TEST_VALUE_LONG,
|
||||||
|
TEST_VALUE_LONG,
|
||||||
|
true,
|
||||||
|
new ClientConfiguration(),
|
||||||
|
new ClientConfiguration(),
|
||||||
|
new ClientConfiguration(),
|
||||||
|
TEST_VALUE_LONG,
|
||||||
|
TEST_VALUE_LONG,
|
||||||
|
TEST_VALUE_INT,
|
||||||
|
skipCheckpointValidationValue,
|
||||||
|
null,
|
||||||
|
TEST_VALUE_LONG, BillingMode.PROVISIONED,
|
||||||
|
new SimpleRecordsFetcherFactory(),
|
||||||
|
TEST_VALUE_LONG,
|
||||||
|
TEST_VALUE_LONG,
|
||||||
|
TEST_VALUE_LONG);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ public class KinesisClientLibLeaseCoordinatorTest {
|
||||||
leaseCoordinator.initialize();
|
leaseCoordinator.initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = KinesisClientLibIOException.class)
|
@Test(expected = com.amazonaws.services.kinesis.clientlibrary.exceptions.InvalidStateException.class)
|
||||||
public void testGetCheckpointObjectWithNoLease()
|
public void testGetCheckpointObjectWithNoLease()
|
||||||
throws DependencyException, ProvisionedThroughputException, IllegalStateException, InvalidStateException,
|
throws DependencyException, ProvisionedThroughputException, IllegalStateException, InvalidStateException,
|
||||||
KinesisClientLibException {
|
KinesisClientLibException {
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ public class PeriodicShardSyncManagerTest {
|
||||||
private static final String WORKER_ID = "workerId";
|
private static final String WORKER_ID = "workerId";
|
||||||
public static final long LEASES_RECOVERY_AUDITOR_EXECUTION_FREQUENCY_MILLIS = 2 * 60 * 1000L;
|
public static final long LEASES_RECOVERY_AUDITOR_EXECUTION_FREQUENCY_MILLIS = 2 * 60 * 1000L;
|
||||||
public static final int LEASES_RECOVERY_AUDITOR_INCONSISTENCY_CONFIDENCE_THRESHOLD = 3;
|
public static final int LEASES_RECOVERY_AUDITOR_INCONSISTENCY_CONFIDENCE_THRESHOLD = 3;
|
||||||
|
private static final int MAX_DEPTH_WITH_IN_PROGRESS_PARENTS = 1;
|
||||||
|
|
||||||
/** Manager for PERIODIC shard sync strategy */
|
/** Manager for PERIODIC shard sync strategy */
|
||||||
private PeriodicShardSyncManager periodicShardSyncManager;
|
private PeriodicShardSyncManager periodicShardSyncManager;
|
||||||
|
|
@ -475,7 +476,7 @@ public class PeriodicShardSyncManagerTest {
|
||||||
for (int i = 0; i < 1000; i++) {
|
for (int i = 0; i < 1000; i++) {
|
||||||
int maxInitialLeaseCount = 100;
|
int maxInitialLeaseCount = 100;
|
||||||
List<KinesisClientLease> leases = generateInitialLeases(maxInitialLeaseCount);
|
List<KinesisClientLease> leases = generateInitialLeases(maxInitialLeaseCount);
|
||||||
reshard(leases, 5, ReshardType.MERGE, maxInitialLeaseCount, true);
|
reshard(leases, MAX_DEPTH_WITH_IN_PROGRESS_PARENTS, ReshardType.MERGE, maxInitialLeaseCount, true);
|
||||||
Collections.shuffle(leases);
|
Collections.shuffle(leases);
|
||||||
Assert.assertFalse(periodicShardSyncManager.hasHoleInLeases(leases).isPresent());
|
Assert.assertFalse(periodicShardSyncManager.hasHoleInLeases(leases).isPresent());
|
||||||
Assert.assertFalse(auditorPeriodicShardSyncManager.hasHoleInLeases(leases).isPresent());
|
Assert.assertFalse(auditorPeriodicShardSyncManager.hasHoleInLeases(leases).isPresent());
|
||||||
|
|
@ -487,7 +488,7 @@ public class PeriodicShardSyncManagerTest {
|
||||||
for (int i = 0; i < 1000; i++) {
|
for (int i = 0; i < 1000; i++) {
|
||||||
int maxInitialLeaseCount = 100;
|
int maxInitialLeaseCount = 100;
|
||||||
List<KinesisClientLease> leases = generateInitialLeases(maxInitialLeaseCount);
|
List<KinesisClientLease> leases = generateInitialLeases(maxInitialLeaseCount);
|
||||||
reshard(leases, 5, ReshardType.ANY, maxInitialLeaseCount, true);
|
reshard(leases, MAX_DEPTH_WITH_IN_PROGRESS_PARENTS, ReshardType.ANY, maxInitialLeaseCount, true);
|
||||||
Collections.shuffle(leases);
|
Collections.shuffle(leases);
|
||||||
Assert.assertFalse(periodicShardSyncManager.hasHoleInLeases(leases).isPresent());
|
Assert.assertFalse(periodicShardSyncManager.hasHoleInLeases(leases).isPresent());
|
||||||
Assert.assertFalse(auditorPeriodicShardSyncManager.hasHoleInLeases(leases).isPresent());
|
Assert.assertFalse(auditorPeriodicShardSyncManager.hasHoleInLeases(leases).isPresent());
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,6 @@ import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.RejectedExecutionException;
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import com.amazonaws.services.kinesis.leases.impl.LeaseCleanupManager;
|
|
||||||
import com.amazonaws.services.kinesis.leases.impl.LeaseManager;
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.hamcrest.Description;
|
import org.hamcrest.Description;
|
||||||
|
|
@ -64,7 +62,6 @@ import org.hamcrest.TypeSafeMatcher;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.InjectMocks;
|
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
|
@ -88,7 +85,7 @@ import com.amazonaws.services.kinesis.model.Shard;
|
||||||
import com.amazonaws.services.kinesis.model.ShardIteratorType;
|
import com.amazonaws.services.kinesis.model.ShardIteratorType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests of {@link ShardConsumer}.
|
* Unit tests of {@link KinesisShardConsumer}.
|
||||||
*/
|
*/
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
public class ShardConsumerTest {
|
public class ShardConsumerTest {
|
||||||
|
|
@ -163,8 +160,8 @@ public class ShardConsumerTest {
|
||||||
callProcessRecordsForEmptyRecordList,
|
callProcessRecordsForEmptyRecordList,
|
||||||
skipCheckpointValidationValue, INITIAL_POSITION_LATEST);
|
skipCheckpointValidationValue, INITIAL_POSITION_LATEST);
|
||||||
|
|
||||||
ShardConsumer consumer =
|
KinesisShardConsumer consumer =
|
||||||
new ShardConsumer(shardInfo,
|
new KinesisShardConsumer(shardInfo,
|
||||||
streamConfig,
|
streamConfig,
|
||||||
checkpoint,
|
checkpoint,
|
||||||
processor,
|
processor,
|
||||||
|
|
@ -178,19 +175,19 @@ public class ShardConsumerTest {
|
||||||
config,
|
config,
|
||||||
shardSyncer,
|
shardSyncer,
|
||||||
shardSyncStrategy);
|
shardSyncStrategy);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
||||||
consumer.consumeShard(); // initialize
|
consumer.consumeShard(); // initialize
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
||||||
consumer.consumeShard(); // initialize
|
consumer.consumeShard(); // initialize
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.INITIALIZING)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.INITIALIZING)));
|
||||||
consumer.consumeShard(); // initialize
|
consumer.consumeShard(); // initialize
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.INITIALIZING)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.INITIALIZING)));
|
||||||
consumer.consumeShard(); // initialize
|
consumer.consumeShard(); // initialize
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.INITIALIZING)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.INITIALIZING)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -213,8 +210,8 @@ public class ShardConsumerTest {
|
||||||
callProcessRecordsForEmptyRecordList,
|
callProcessRecordsForEmptyRecordList,
|
||||||
skipCheckpointValidationValue, INITIAL_POSITION_LATEST);
|
skipCheckpointValidationValue, INITIAL_POSITION_LATEST);
|
||||||
|
|
||||||
ShardConsumer consumer =
|
KinesisShardConsumer consumer =
|
||||||
new ShardConsumer(shardInfo,
|
new KinesisShardConsumer(shardInfo,
|
||||||
streamConfig,
|
streamConfig,
|
||||||
checkpoint,
|
checkpoint,
|
||||||
processor,
|
processor,
|
||||||
|
|
@ -229,23 +226,69 @@ public class ShardConsumerTest {
|
||||||
shardSyncer,
|
shardSyncer,
|
||||||
shardSyncStrategy);
|
shardSyncStrategy);
|
||||||
|
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
||||||
consumer.consumeShard(); // initialize
|
consumer.consumeShard(); // initialize
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
||||||
|
|
||||||
doThrow(new RejectedExecutionException()).when(spyExecutorService).submit(any(InitializeTask.class));
|
doThrow(new RejectedExecutionException()).when(spyExecutorService).submit(any(InitializeTask.class));
|
||||||
consumer.consumeShard(); // initialize
|
consumer.consumeShard(); // initialize
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.INITIALIZING)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.INITIALIZING)));
|
||||||
consumer.consumeShard(); // initialize
|
consumer.consumeShard(); // initialize
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.INITIALIZING)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.INITIALIZING)));
|
||||||
consumer.consumeShard(); // initialize
|
consumer.consumeShard(); // initialize
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.INITIALIZING)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.INITIALIZING)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInitializationStateTransitionsToShutdownOnLeaseNotFound() throws Exception {
|
||||||
|
ShardInfo shardInfo = new ShardInfo("s-0-0", "testToken", null, ExtendedSequenceNumber.TRIM_HORIZON);
|
||||||
|
|
||||||
|
ICheckpoint checkpoint = new KinesisClientLibLeaseCoordinator(leaseManager, "", 0, 0);
|
||||||
|
|
||||||
|
when(leaseManager.getLease(anyString())).thenReturn(null);
|
||||||
|
when(leaseCoordinator.getLeaseManager()).thenReturn(leaseManager);
|
||||||
|
StreamConfig streamConfig =
|
||||||
|
new StreamConfig(streamProxy,
|
||||||
|
1,
|
||||||
|
10,
|
||||||
|
callProcessRecordsForEmptyRecordList,
|
||||||
|
skipCheckpointValidationValue, INITIAL_POSITION_LATEST);
|
||||||
|
|
||||||
|
KinesisShardConsumer consumer =
|
||||||
|
new KinesisShardConsumer(shardInfo,
|
||||||
|
streamConfig,
|
||||||
|
checkpoint,
|
||||||
|
processor,
|
||||||
|
leaseCoordinator,
|
||||||
|
parentShardPollIntervalMillis,
|
||||||
|
cleanupLeasesOfCompletedShards,
|
||||||
|
executorService,
|
||||||
|
metricsFactory,
|
||||||
|
taskBackoffTimeMillis,
|
||||||
|
KinesisClientLibConfiguration.DEFAULT_SKIP_SHARD_SYNC_AT_STARTUP_IF_LEASES_EXIST,
|
||||||
|
config,
|
||||||
|
shardSyncer,
|
||||||
|
shardSyncStrategy);
|
||||||
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
||||||
|
consumer.consumeShard();
|
||||||
|
Thread.sleep(50L);
|
||||||
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
||||||
|
consumer.consumeShard();
|
||||||
|
Thread.sleep(50L);
|
||||||
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.INITIALIZING)));
|
||||||
|
consumer.consumeShard();
|
||||||
|
Thread.sleep(50L);
|
||||||
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.SHUTTING_DOWN)));
|
||||||
|
consumer.consumeShard();
|
||||||
|
Thread.sleep(50L);
|
||||||
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.SHUTDOWN_COMPLETE)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Test
|
@Test
|
||||||
public final void testRecordProcessorThrowable() throws Exception {
|
public final void testRecordProcessorThrowable() throws Exception {
|
||||||
|
|
@ -257,8 +300,8 @@ public class ShardConsumerTest {
|
||||||
callProcessRecordsForEmptyRecordList,
|
callProcessRecordsForEmptyRecordList,
|
||||||
skipCheckpointValidationValue, INITIAL_POSITION_LATEST);
|
skipCheckpointValidationValue, INITIAL_POSITION_LATEST);
|
||||||
|
|
||||||
ShardConsumer consumer =
|
KinesisShardConsumer consumer =
|
||||||
new ShardConsumer(shardInfo,
|
new KinesisShardConsumer(shardInfo,
|
||||||
streamConfig,
|
streamConfig,
|
||||||
checkpoint,
|
checkpoint,
|
||||||
processor,
|
processor,
|
||||||
|
|
@ -281,10 +324,10 @@ public class ShardConsumerTest {
|
||||||
when(checkpoint.getCheckpointObject(anyString())).thenReturn(
|
when(checkpoint.getCheckpointObject(anyString())).thenReturn(
|
||||||
new Checkpoint(checkpointSequenceNumber, pendingCheckpointSequenceNumber));
|
new Checkpoint(checkpointSequenceNumber, pendingCheckpointSequenceNumber));
|
||||||
|
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
||||||
consumer.consumeShard(); // submit BlockOnParentShardTask
|
consumer.consumeShard(); // submit BlockOnParentShardTask
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
||||||
verify(processor, times(0)).initialize(any(InitializationInput.class));
|
verify(processor, times(0)).initialize(any(InitializationInput.class));
|
||||||
|
|
||||||
// Throw Error when IRecordProcessor.initialize() is invoked.
|
// Throw Error when IRecordProcessor.initialize() is invoked.
|
||||||
|
|
@ -292,7 +335,7 @@ public class ShardConsumerTest {
|
||||||
|
|
||||||
consumer.consumeShard(); // submit InitializeTask
|
consumer.consumeShard(); // submit InitializeTask
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.INITIALIZING)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.INITIALIZING)));
|
||||||
verify(processor, times(1)).initialize(argThat(
|
verify(processor, times(1)).initialize(argThat(
|
||||||
initializationInputMatcher(checkpointSequenceNumber, pendingCheckpointSequenceNumber)));
|
initializationInputMatcher(checkpointSequenceNumber, pendingCheckpointSequenceNumber)));
|
||||||
|
|
||||||
|
|
@ -304,7 +347,7 @@ public class ShardConsumerTest {
|
||||||
assertThat(e.getCause(), instanceOf(ExecutionException.class));
|
assertThat(e.getCause(), instanceOf(ExecutionException.class));
|
||||||
}
|
}
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.INITIALIZING)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.INITIALIZING)));
|
||||||
verify(processor, times(1)).initialize(argThat(
|
verify(processor, times(1)).initialize(argThat(
|
||||||
initializationInputMatcher(checkpointSequenceNumber, pendingCheckpointSequenceNumber)));
|
initializationInputMatcher(checkpointSequenceNumber, pendingCheckpointSequenceNumber)));
|
||||||
|
|
||||||
|
|
@ -312,7 +355,7 @@ public class ShardConsumerTest {
|
||||||
|
|
||||||
consumer.consumeShard(); // submit InitializeTask again.
|
consumer.consumeShard(); // submit InitializeTask again.
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.INITIALIZING)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.INITIALIZING)));
|
||||||
verify(processor, times(2)).initialize(argThat(
|
verify(processor, times(2)).initialize(argThat(
|
||||||
initializationInputMatcher(checkpointSequenceNumber, pendingCheckpointSequenceNumber)));
|
initializationInputMatcher(checkpointSequenceNumber, pendingCheckpointSequenceNumber)));
|
||||||
verify(processor, times(2)).initialize(any(InitializationInput.class)); // no other calls with different args
|
verify(processor, times(2)).initialize(any(InitializationInput.class)); // no other calls with different args
|
||||||
|
|
@ -320,11 +363,11 @@ public class ShardConsumerTest {
|
||||||
// Checking the status of submitted InitializeTask from above should pass.
|
// Checking the status of submitted InitializeTask from above should pass.
|
||||||
consumer.consumeShard();
|
consumer.consumeShard();
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.PROCESSING)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.PROCESSING)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link ShardConsumer#consumeShard()}
|
* Test method for {@link KinesisShardConsumer#consumeShard()}
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public final void testConsumeShard() throws Exception {
|
public final void testConsumeShard() throws Exception {
|
||||||
|
|
@ -377,8 +420,8 @@ public class ShardConsumerTest {
|
||||||
any(IMetricsFactory.class), anyInt()))
|
any(IMetricsFactory.class), anyInt()))
|
||||||
.thenReturn(getRecordsCache);
|
.thenReturn(getRecordsCache);
|
||||||
|
|
||||||
ShardConsumer consumer =
|
KinesisShardConsumer consumer =
|
||||||
new ShardConsumer(shardInfo,
|
new KinesisShardConsumer(shardInfo,
|
||||||
streamConfig,
|
streamConfig,
|
||||||
checkpoint,
|
checkpoint,
|
||||||
processor,
|
processor,
|
||||||
|
|
@ -397,11 +440,11 @@ public class ShardConsumerTest {
|
||||||
shardSyncer,
|
shardSyncer,
|
||||||
shardSyncStrategy);
|
shardSyncStrategy);
|
||||||
|
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
||||||
consumer.consumeShard(); // check on parent shards
|
consumer.consumeShard(); // check on parent shards
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
consumer.consumeShard(); // start initialization
|
consumer.consumeShard(); // start initialization
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.INITIALIZING)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.INITIALIZING)));
|
||||||
consumer.consumeShard(); // initialize
|
consumer.consumeShard(); // initialize
|
||||||
processor.getInitializeLatch().await(5, TimeUnit.SECONDS);
|
processor.getInitializeLatch().await(5, TimeUnit.SECONDS);
|
||||||
verify(getRecordsCache).start();
|
verify(getRecordsCache).start();
|
||||||
|
|
@ -411,7 +454,7 @@ public class ShardConsumerTest {
|
||||||
boolean newTaskSubmitted = consumer.consumeShard();
|
boolean newTaskSubmitted = consumer.consumeShard();
|
||||||
if (newTaskSubmitted) {
|
if (newTaskSubmitted) {
|
||||||
LOG.debug("New processing task was submitted, call # " + i);
|
LOG.debug("New processing task was submitted, call # " + i);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.PROCESSING)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.PROCESSING)));
|
||||||
// CHECKSTYLE:IGNORE ModifiedControlVariable FOR NEXT 1 LINES
|
// CHECKSTYLE:IGNORE ModifiedControlVariable FOR NEXT 1 LINES
|
||||||
i += maxRecords;
|
i += maxRecords;
|
||||||
}
|
}
|
||||||
|
|
@ -426,21 +469,21 @@ public class ShardConsumerTest {
|
||||||
assertThat(processor.getNotifyShutdownLatch().await(1, TimeUnit.SECONDS), is(true));
|
assertThat(processor.getNotifyShutdownLatch().await(1, TimeUnit.SECONDS), is(true));
|
||||||
Thread.sleep(50);
|
Thread.sleep(50);
|
||||||
assertThat(consumer.getShutdownReason(), equalTo(ShutdownReason.REQUESTED));
|
assertThat(consumer.getShutdownReason(), equalTo(ShutdownReason.REQUESTED));
|
||||||
assertThat(consumer.getCurrentState(), equalTo(ConsumerStates.ShardConsumerState.SHUTDOWN_REQUESTED));
|
assertThat(consumer.getCurrentState(), equalTo(KinesisConsumerStates.ShardConsumerState.SHUTDOWN_REQUESTED));
|
||||||
verify(shutdownNotification).shutdownNotificationComplete();
|
verify(shutdownNotification).shutdownNotificationComplete();
|
||||||
assertThat(processor.isShutdownNotificationCalled(), equalTo(true));
|
assertThat(processor.isShutdownNotificationCalled(), equalTo(true));
|
||||||
consumer.consumeShard();
|
consumer.consumeShard();
|
||||||
Thread.sleep(50);
|
Thread.sleep(50);
|
||||||
assertThat(consumer.getCurrentState(), equalTo(ConsumerStates.ShardConsumerState.SHUTDOWN_REQUESTED));
|
assertThat(consumer.getCurrentState(), equalTo(KinesisConsumerStates.ShardConsumerState.SHUTDOWN_REQUESTED));
|
||||||
|
|
||||||
consumer.beginShutdown();
|
consumer.beginShutdown();
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
assertThat(consumer.getShutdownReason(), equalTo(ShutdownReason.ZOMBIE));
|
assertThat(consumer.getShutdownReason(), equalTo(ShutdownReason.ZOMBIE));
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.SHUTTING_DOWN)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.SHUTTING_DOWN)));
|
||||||
consumer.beginShutdown();
|
consumer.beginShutdown();
|
||||||
consumer.consumeShard();
|
consumer.consumeShard();
|
||||||
verify(shutdownNotification, atLeastOnce()).shutdownComplete();
|
verify(shutdownNotification, atLeastOnce()).shutdownComplete();
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.SHUTDOWN_COMPLETE)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.SHUTDOWN_COMPLETE)));
|
||||||
assertThat(processor.getShutdownReason(), is(equalTo(ShutdownReason.ZOMBIE)));
|
assertThat(processor.getShutdownReason(), is(equalTo(ShutdownReason.ZOMBIE)));
|
||||||
|
|
||||||
verify(getRecordsCache).shutdown();
|
verify(getRecordsCache).shutdown();
|
||||||
|
|
@ -481,8 +524,8 @@ public class ShardConsumerTest {
|
||||||
when(recordProcessorCheckpointer.getLastCheckpointValue()).thenReturn(ExtendedSequenceNumber.SHARD_END);
|
when(recordProcessorCheckpointer.getLastCheckpointValue()).thenReturn(ExtendedSequenceNumber.SHARD_END);
|
||||||
when(streamConfig.getStreamProxy()).thenReturn(streamProxy);
|
when(streamConfig.getStreamProxy()).thenReturn(streamProxy);
|
||||||
|
|
||||||
final ShardConsumer consumer =
|
final KinesisShardConsumer consumer =
|
||||||
new ShardConsumer(shardInfo,
|
new KinesisShardConsumer(shardInfo,
|
||||||
streamConfig,
|
streamConfig,
|
||||||
checkpoint,
|
checkpoint,
|
||||||
processor,
|
processor,
|
||||||
|
|
@ -501,21 +544,21 @@ public class ShardConsumerTest {
|
||||||
shardSyncer,
|
shardSyncer,
|
||||||
shardSyncStrategy);
|
shardSyncStrategy);
|
||||||
|
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
||||||
verify(parentLease, times(0)).getCheckpoint();
|
verify(parentLease, times(0)).getCheckpoint();
|
||||||
consumer.consumeShard(); // check on parent shards
|
consumer.consumeShard(); // check on parent shards
|
||||||
Thread.sleep(parentShardPollIntervalMillis * 2);
|
Thread.sleep(parentShardPollIntervalMillis * 2);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
||||||
verify(parentLease, times(1)).getCheckpoint();
|
verify(parentLease, times(1)).getCheckpoint();
|
||||||
consumer.notifyShutdownRequested(shutdownNotification);
|
consumer.notifyShutdownRequested(shutdownNotification);
|
||||||
verify(shutdownNotification, times(0)).shutdownComplete();
|
verify(shutdownNotification, times(0)).shutdownComplete();
|
||||||
assertThat(consumer.getShutdownReason(), equalTo(ShutdownReason.REQUESTED));
|
assertThat(consumer.getShutdownReason(), equalTo(ShutdownReason.REQUESTED));
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
||||||
consumer.consumeShard();
|
consumer.consumeShard();
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.SHUTTING_DOWN)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.SHUTTING_DOWN)));
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
consumer.beginShutdown();
|
consumer.beginShutdown();
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.SHUTDOWN_COMPLETE)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.SHUTDOWN_COMPLETE)));
|
||||||
assertThat(consumer.isShutdown(), is(true));
|
assertThat(consumer.isShutdown(), is(true));
|
||||||
verify(shutdownNotification, times(1)).shutdownComplete();
|
verify(shutdownNotification, times(1)).shutdownComplete();
|
||||||
consumer.beginShutdown();
|
consumer.beginShutdown();
|
||||||
|
|
@ -540,7 +583,7 @@ public class ShardConsumerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link ShardConsumer#consumeShard()} that ensures a transient error thrown from the record
|
* Test method for {@link KinesisShardConsumer#consumeShard()} that ensures a transient error thrown from the record
|
||||||
* processor's shutdown method with reason zombie will be retried.
|
* processor's shutdown method with reason zombie will be retried.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -587,7 +630,9 @@ public class ShardConsumerTest {
|
||||||
parentShardIds.add("parentShardId");
|
parentShardIds.add("parentShardId");
|
||||||
KinesisClientLease currentLease = createLease(streamShardId, "leaseOwner", parentShardIds);
|
KinesisClientLease currentLease = createLease(streamShardId, "leaseOwner", parentShardIds);
|
||||||
currentLease.setCheckpoint(new ExtendedSequenceNumber("testSequenceNumbeer"));
|
currentLease.setCheckpoint(new ExtendedSequenceNumber("testSequenceNumbeer"));
|
||||||
when(leaseManager.getLease(streamShardId)).thenReturn(currentLease);
|
KinesisClientLease currentLease1 = createLease(streamShardId, "leaseOwner", parentShardIds);
|
||||||
|
currentLease1.setCheckpoint(ExtendedSequenceNumber.SHARD_END);
|
||||||
|
when(leaseManager.getLease(streamShardId)).thenReturn(currentLease, currentLease, currentLease1);
|
||||||
when(leaseCoordinator.getCurrentlyHeldLease(shardInfo.getShardId())).thenReturn(currentLease);
|
when(leaseCoordinator.getCurrentlyHeldLease(shardInfo.getShardId())).thenReturn(currentLease);
|
||||||
|
|
||||||
RecordProcessorCheckpointer recordProcessorCheckpointer = new RecordProcessorCheckpointer(
|
RecordProcessorCheckpointer recordProcessorCheckpointer = new RecordProcessorCheckpointer(
|
||||||
|
|
@ -601,8 +646,8 @@ public class ShardConsumerTest {
|
||||||
metricsFactory
|
metricsFactory
|
||||||
);
|
);
|
||||||
|
|
||||||
ShardConsumer consumer =
|
KinesisShardConsumer consumer =
|
||||||
new ShardConsumer(shardInfo,
|
new KinesisShardConsumer(shardInfo,
|
||||||
streamConfig,
|
streamConfig,
|
||||||
checkpoint,
|
checkpoint,
|
||||||
processor,
|
processor,
|
||||||
|
|
@ -622,11 +667,11 @@ public class ShardConsumerTest {
|
||||||
shardSyncStrategy);
|
shardSyncStrategy);
|
||||||
|
|
||||||
when(leaseCoordinator.updateLease(any(KinesisClientLease.class), any(UUID.class))).thenReturn(true);
|
when(leaseCoordinator.updateLease(any(KinesisClientLease.class), any(UUID.class))).thenReturn(true);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
||||||
consumer.consumeShard(); // check on parent shards
|
consumer.consumeShard(); // check on parent shards
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
consumer.consumeShard(); // start initialization
|
consumer.consumeShard(); // start initialization
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.INITIALIZING)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.INITIALIZING)));
|
||||||
consumer.consumeShard(); // initialize
|
consumer.consumeShard(); // initialize
|
||||||
processor.getInitializeLatch().await(5, TimeUnit.SECONDS);
|
processor.getInitializeLatch().await(5, TimeUnit.SECONDS);
|
||||||
verify(getRecordsCache).start();
|
verify(getRecordsCache).start();
|
||||||
|
|
@ -636,7 +681,7 @@ public class ShardConsumerTest {
|
||||||
boolean newTaskSubmitted = consumer.consumeShard();
|
boolean newTaskSubmitted = consumer.consumeShard();
|
||||||
if (newTaskSubmitted) {
|
if (newTaskSubmitted) {
|
||||||
LOG.debug("New processing task was submitted, call # " + i);
|
LOG.debug("New processing task was submitted, call # " + i);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.PROCESSING)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.PROCESSING)));
|
||||||
// CHECKSTYLE:IGNORE ModifiedControlVariable FOR NEXT 1 LINES
|
// CHECKSTYLE:IGNORE ModifiedControlVariable FOR NEXT 1 LINES
|
||||||
i += maxRecords;
|
i += maxRecords;
|
||||||
}
|
}
|
||||||
|
|
@ -664,12 +709,12 @@ public class ShardConsumerTest {
|
||||||
// Wait for shutdown complete now that terminate shutdown is successful
|
// Wait for shutdown complete now that terminate shutdown is successful
|
||||||
for (int i = 0; i < 100; i++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
consumer.consumeShard();
|
consumer.consumeShard();
|
||||||
if (consumer.getCurrentState() == ConsumerStates.ShardConsumerState.SHUTDOWN_COMPLETE) {
|
if (consumer.getCurrentState() == KinesisConsumerStates.ShardConsumerState.SHUTDOWN_COMPLETE) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
}
|
}
|
||||||
assertThat(consumer.getCurrentState(), equalTo(ConsumerStates.ShardConsumerState.SHUTDOWN_COMPLETE));
|
assertThat(consumer.getCurrentState(), equalTo(KinesisConsumerStates.ShardConsumerState.SHUTDOWN_COMPLETE));
|
||||||
|
|
||||||
assertThat(processor.getShutdownReason(), is(equalTo(ShutdownReason.TERMINATE)));
|
assertThat(processor.getShutdownReason(), is(equalTo(ShutdownReason.TERMINATE)));
|
||||||
|
|
||||||
|
|
@ -687,7 +732,7 @@ public class ShardConsumerTest {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link ShardConsumer#consumeShard()} that ensures the shardConsumer gets shutdown with shutdown
|
* Test method for {@link KinesisShardConsumer#consumeShard()} that ensures the shardConsumer gets shutdown with shutdown
|
||||||
* reason TERMINATE when the shard end is reached.
|
* reason TERMINATE when the shard end is reached.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -714,7 +759,10 @@ public class ShardConsumerTest {
|
||||||
parentShardIds.add("parentShardId");
|
parentShardIds.add("parentShardId");
|
||||||
KinesisClientLease currentLease = createLease(streamShardId, "leaseOwner", parentShardIds);
|
KinesisClientLease currentLease = createLease(streamShardId, "leaseOwner", parentShardIds);
|
||||||
currentLease.setCheckpoint(new ExtendedSequenceNumber("testSequenceNumbeer"));
|
currentLease.setCheckpoint(new ExtendedSequenceNumber("testSequenceNumbeer"));
|
||||||
when(leaseManager.getLease(streamShardId)).thenReturn(currentLease);
|
KinesisClientLease currentLease1 = createLease(streamShardId, "leaseOwner", parentShardIds);
|
||||||
|
currentLease1.setCheckpoint(ExtendedSequenceNumber.SHARD_END);
|
||||||
|
when(leaseManager.getLease(streamShardId)).thenReturn(currentLease, currentLease, currentLease1);
|
||||||
|
|
||||||
when(leaseCoordinator.getLeaseManager()).thenReturn(leaseManager);
|
when(leaseCoordinator.getLeaseManager()).thenReturn(leaseManager);
|
||||||
|
|
||||||
TransientShutdownErrorTestStreamlet processor = new TransientShutdownErrorTestStreamlet();
|
TransientShutdownErrorTestStreamlet processor = new TransientShutdownErrorTestStreamlet();
|
||||||
|
|
@ -747,8 +795,8 @@ public class ShardConsumerTest {
|
||||||
metricsFactory
|
metricsFactory
|
||||||
);
|
);
|
||||||
|
|
||||||
ShardConsumer consumer =
|
KinesisShardConsumer consumer =
|
||||||
new ShardConsumer(shardInfo,
|
new KinesisShardConsumer(shardInfo,
|
||||||
streamConfig,
|
streamConfig,
|
||||||
checkpoint,
|
checkpoint,
|
||||||
processor,
|
processor,
|
||||||
|
|
@ -770,11 +818,11 @@ public class ShardConsumerTest {
|
||||||
when(leaseCoordinator.getCurrentlyHeldLease(shardInfo.getShardId())).thenReturn(currentLease);
|
when(leaseCoordinator.getCurrentlyHeldLease(shardInfo.getShardId())).thenReturn(currentLease);
|
||||||
when(leaseCoordinator.updateLease(any(KinesisClientLease.class), any(UUID.class))).thenReturn(true);
|
when(leaseCoordinator.updateLease(any(KinesisClientLease.class), any(UUID.class))).thenReturn(true);
|
||||||
|
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
||||||
consumer.consumeShard(); // check on parent shards
|
consumer.consumeShard(); // check on parent shards
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
consumer.consumeShard(); // start initialization
|
consumer.consumeShard(); // start initialization
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.INITIALIZING)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.INITIALIZING)));
|
||||||
consumer.consumeShard(); // initialize
|
consumer.consumeShard(); // initialize
|
||||||
processor.getInitializeLatch().await(5, TimeUnit.SECONDS);
|
processor.getInitializeLatch().await(5, TimeUnit.SECONDS);
|
||||||
verify(getRecordsCache).start();
|
verify(getRecordsCache).start();
|
||||||
|
|
@ -784,7 +832,7 @@ public class ShardConsumerTest {
|
||||||
boolean newTaskSubmitted = consumer.consumeShard();
|
boolean newTaskSubmitted = consumer.consumeShard();
|
||||||
if (newTaskSubmitted) {
|
if (newTaskSubmitted) {
|
||||||
LOG.debug("New processing task was submitted, call # " + i);
|
LOG.debug("New processing task was submitted, call # " + i);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.PROCESSING)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.PROCESSING)));
|
||||||
// CHECKSTYLE:IGNORE ModifiedControlVariable FOR NEXT 1 LINES
|
// CHECKSTYLE:IGNORE ModifiedControlVariable FOR NEXT 1 LINES
|
||||||
i += maxRecords;
|
i += maxRecords;
|
||||||
}
|
}
|
||||||
|
|
@ -812,12 +860,12 @@ public class ShardConsumerTest {
|
||||||
// Wait for shutdown complete now that terminate shutdown is successful
|
// Wait for shutdown complete now that terminate shutdown is successful
|
||||||
for (int i = 0; i < 100; i++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
consumer.consumeShard();
|
consumer.consumeShard();
|
||||||
if (consumer.getCurrentState() == ConsumerStates.ShardConsumerState.SHUTDOWN_COMPLETE) {
|
if (consumer.getCurrentState() == KinesisConsumerStates.ShardConsumerState.SHUTDOWN_COMPLETE) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
}
|
}
|
||||||
assertThat(consumer.getCurrentState(), equalTo(ConsumerStates.ShardConsumerState.SHUTDOWN_COMPLETE));
|
assertThat(consumer.getCurrentState(), equalTo(KinesisConsumerStates.ShardConsumerState.SHUTDOWN_COMPLETE));
|
||||||
|
|
||||||
assertThat(processor.getShutdownReason(), is(equalTo(ShutdownReason.TERMINATE)));
|
assertThat(processor.getShutdownReason(), is(equalTo(ShutdownReason.TERMINATE)));
|
||||||
|
|
||||||
|
|
@ -833,7 +881,7 @@ public class ShardConsumerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link ShardConsumer#consumeShard()} that starts from initial position of type AT_TIMESTAMP.
|
* Test method for {@link KinesisShardConsumer#consumeShard()} that starts from initial position of type AT_TIMESTAMP.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public final void testConsumeShardWithInitialPositionAtTimestamp() throws Exception {
|
public final void testConsumeShardWithInitialPositionAtTimestamp() throws Exception {
|
||||||
|
|
@ -890,8 +938,8 @@ public class ShardConsumerTest {
|
||||||
any(IMetricsFactory.class), anyInt()))
|
any(IMetricsFactory.class), anyInt()))
|
||||||
.thenReturn(getRecordsCache);
|
.thenReturn(getRecordsCache);
|
||||||
|
|
||||||
ShardConsumer consumer =
|
KinesisShardConsumer consumer =
|
||||||
new ShardConsumer(shardInfo,
|
new KinesisShardConsumer(shardInfo,
|
||||||
streamConfig,
|
streamConfig,
|
||||||
checkpoint,
|
checkpoint,
|
||||||
processor,
|
processor,
|
||||||
|
|
@ -910,11 +958,11 @@ public class ShardConsumerTest {
|
||||||
shardSyncer,
|
shardSyncer,
|
||||||
shardSyncStrategy);
|
shardSyncStrategy);
|
||||||
|
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
||||||
consumer.consumeShard(); // check on parent shards
|
consumer.consumeShard(); // check on parent shards
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
consumer.consumeShard(); // start initialization
|
consumer.consumeShard(); // start initialization
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.INITIALIZING)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.INITIALIZING)));
|
||||||
consumer.consumeShard(); // initialize
|
consumer.consumeShard(); // initialize
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
|
|
||||||
|
|
@ -925,7 +973,7 @@ public class ShardConsumerTest {
|
||||||
boolean newTaskSubmitted = consumer.consumeShard();
|
boolean newTaskSubmitted = consumer.consumeShard();
|
||||||
if (newTaskSubmitted) {
|
if (newTaskSubmitted) {
|
||||||
LOG.debug("New processing task was submitted, call # " + i);
|
LOG.debug("New processing task was submitted, call # " + i);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.PROCESSING)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.PROCESSING)));
|
||||||
// CHECKSTYLE:IGNORE ModifiedControlVariable FOR NEXT 1 LINES
|
// CHECKSTYLE:IGNORE ModifiedControlVariable FOR NEXT 1 LINES
|
||||||
i += maxRecords;
|
i += maxRecords;
|
||||||
}
|
}
|
||||||
|
|
@ -937,9 +985,9 @@ public class ShardConsumerTest {
|
||||||
assertThat(processor.getShutdownReason(), nullValue());
|
assertThat(processor.getShutdownReason(), nullValue());
|
||||||
consumer.beginShutdown();
|
consumer.beginShutdown();
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.SHUTTING_DOWN)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.SHUTTING_DOWN)));
|
||||||
consumer.beginShutdown();
|
consumer.beginShutdown();
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.SHUTDOWN_COMPLETE)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.SHUTDOWN_COMPLETE)));
|
||||||
assertThat(processor.getShutdownReason(), is(equalTo(ShutdownReason.ZOMBIE)));
|
assertThat(processor.getShutdownReason(), is(equalTo(ShutdownReason.ZOMBIE)));
|
||||||
|
|
||||||
executorService.shutdown();
|
executorService.shutdown();
|
||||||
|
|
@ -966,8 +1014,8 @@ public class ShardConsumerTest {
|
||||||
callProcessRecordsForEmptyRecordList,
|
callProcessRecordsForEmptyRecordList,
|
||||||
skipCheckpointValidationValue, INITIAL_POSITION_LATEST);
|
skipCheckpointValidationValue, INITIAL_POSITION_LATEST);
|
||||||
|
|
||||||
ShardConsumer consumer =
|
KinesisShardConsumer consumer =
|
||||||
new ShardConsumer(shardInfo,
|
new KinesisShardConsumer(shardInfo,
|
||||||
streamConfig,
|
streamConfig,
|
||||||
checkpoint,
|
checkpoint,
|
||||||
processor,
|
processor,
|
||||||
|
|
@ -993,22 +1041,22 @@ public class ShardConsumerTest {
|
||||||
when(checkpoint.getCheckpointObject(anyString())).thenReturn(
|
when(checkpoint.getCheckpointObject(anyString())).thenReturn(
|
||||||
new Checkpoint(checkpointSequenceNumber, pendingCheckpointSequenceNumber));
|
new Checkpoint(checkpointSequenceNumber, pendingCheckpointSequenceNumber));
|
||||||
|
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
||||||
consumer.consumeShard(); // submit BlockOnParentShardTask
|
consumer.consumeShard(); // submit BlockOnParentShardTask
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.WAITING_ON_PARENT_SHARDS)));
|
||||||
verify(processor, times(0)).initialize(any(InitializationInput.class));
|
verify(processor, times(0)).initialize(any(InitializationInput.class));
|
||||||
|
|
||||||
consumer.consumeShard(); // submit InitializeTask
|
consumer.consumeShard(); // submit InitializeTask
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.INITIALIZING)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.INITIALIZING)));
|
||||||
verify(processor, times(1)).initialize(argThat(
|
verify(processor, times(1)).initialize(argThat(
|
||||||
initializationInputMatcher(checkpointSequenceNumber, pendingCheckpointSequenceNumber)));
|
initializationInputMatcher(checkpointSequenceNumber, pendingCheckpointSequenceNumber)));
|
||||||
verify(processor, times(1)).initialize(any(InitializationInput.class)); // no other calls with different args
|
verify(processor, times(1)).initialize(any(InitializationInput.class)); // no other calls with different args
|
||||||
|
|
||||||
consumer.consumeShard();
|
consumer.consumeShard();
|
||||||
Thread.sleep(50L);
|
Thread.sleep(50L);
|
||||||
assertThat(consumer.getCurrentState(), is(equalTo(ConsumerStates.ShardConsumerState.PROCESSING)));
|
assertThat(consumer.getCurrentState(), is(equalTo(KinesisConsumerStates.ShardConsumerState.PROCESSING)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -1021,8 +1069,8 @@ public class ShardConsumerTest {
|
||||||
callProcessRecordsForEmptyRecordList,
|
callProcessRecordsForEmptyRecordList,
|
||||||
skipCheckpointValidationValue, INITIAL_POSITION_LATEST);
|
skipCheckpointValidationValue, INITIAL_POSITION_LATEST);
|
||||||
|
|
||||||
ShardConsumer shardConsumer =
|
KinesisShardConsumer shardConsumer =
|
||||||
new ShardConsumer(shardInfo,
|
new KinesisShardConsumer(shardInfo,
|
||||||
streamConfig,
|
streamConfig,
|
||||||
checkpoint,
|
checkpoint,
|
||||||
processor,
|
processor,
|
||||||
|
|
@ -1053,8 +1101,8 @@ public class ShardConsumerTest {
|
||||||
callProcessRecordsForEmptyRecordList,
|
callProcessRecordsForEmptyRecordList,
|
||||||
skipCheckpointValidationValue, INITIAL_POSITION_LATEST);
|
skipCheckpointValidationValue, INITIAL_POSITION_LATEST);
|
||||||
|
|
||||||
ShardConsumer shardConsumer =
|
KinesisShardConsumer shardConsumer =
|
||||||
new ShardConsumer(shardInfo,
|
new KinesisShardConsumer(shardInfo,
|
||||||
streamConfig,
|
streamConfig,
|
||||||
checkpoint,
|
checkpoint,
|
||||||
processor,
|
processor,
|
||||||
|
|
@ -1096,7 +1144,7 @@ public class ShardConsumerTest {
|
||||||
skipCheckpointValidationValue,
|
skipCheckpointValidationValue,
|
||||||
INITIAL_POSITION_LATEST);
|
INITIAL_POSITION_LATEST);
|
||||||
|
|
||||||
ShardConsumer shardConsumer = new ShardConsumer(
|
KinesisShardConsumer shardConsumer = new KinesisShardConsumer(
|
||||||
shardInfo,
|
shardInfo,
|
||||||
streamConfig,
|
streamConfig,
|
||||||
checkpoint,
|
checkpoint,
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ import com.amazonaws.services.kinesis.leases.impl.KinesisClientLease;
|
||||||
import com.amazonaws.services.kinesis.leases.impl.KinesisClientLeaseManager;
|
import com.amazonaws.services.kinesis.leases.impl.KinesisClientLeaseManager;
|
||||||
import com.amazonaws.services.kinesis.leases.interfaces.IKinesisClientLeaseManager;
|
import com.amazonaws.services.kinesis.leases.interfaces.IKinesisClientLeaseManager;
|
||||||
import com.amazonaws.services.kinesis.model.StreamStatus;
|
import com.amazonaws.services.kinesis.model.StreamStatus;
|
||||||
|
import com.amazonaws.services.kinesis.model.LimitExceededException;
|
||||||
|
|
||||||
import static junit.framework.TestCase.fail;
|
import static junit.framework.TestCase.fail;
|
||||||
|
|
||||||
|
|
@ -58,6 +59,8 @@ public class ShardSyncTaskIntegrationTest {
|
||||||
private IKinesisProxy kinesisProxy;
|
private IKinesisProxy kinesisProxy;
|
||||||
private final KinesisShardSyncer shardSyncer = new KinesisShardSyncer(new KinesisLeaseCleanupValidator());
|
private final KinesisShardSyncer shardSyncer = new KinesisShardSyncer(new KinesisLeaseCleanupValidator());
|
||||||
|
|
||||||
|
private static final int retryBackoffMillis = 1000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws java.lang.Exception
|
* @throws java.lang.Exception
|
||||||
*/
|
*/
|
||||||
|
|
@ -71,9 +74,13 @@ public class ShardSyncTaskIntegrationTest {
|
||||||
} catch (AmazonServiceException ase) {
|
} catch (AmazonServiceException ase) {
|
||||||
|
|
||||||
}
|
}
|
||||||
StreamStatus status;
|
StreamStatus status = null;
|
||||||
do {
|
do {
|
||||||
status = StreamStatus.fromValue(kinesis.describeStream(STREAM_NAME).getStreamDescription().getStreamStatus());
|
try {
|
||||||
|
status = StreamStatus.fromValue(kinesis.describeStream(STREAM_NAME).getStreamDescription().getStreamStatus());
|
||||||
|
} catch (LimitExceededException e) {
|
||||||
|
Thread.sleep(retryBackoffMillis + (long) (Math.random() * 100));
|
||||||
|
}
|
||||||
} while (status != StreamStatus.ACTIVE);
|
} while (status != StreamStatus.ACTIVE);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,8 @@ public class ShardSyncerTest {
|
||||||
private LeaseManager<KinesisClientLease> leaseManager = new KinesisClientLeaseManager("tempTestTable", ddbClient, KinesisClientLibConfiguration.DEFAULT_DDB_BILLING_MODE);
|
private LeaseManager<KinesisClientLease> leaseManager = new KinesisClientLeaseManager("tempTestTable", ddbClient, KinesisClientLibConfiguration.DEFAULT_DDB_BILLING_MODE);
|
||||||
protected static final KinesisLeaseCleanupValidator leaseCleanupValidator = new KinesisLeaseCleanupValidator();
|
protected static final KinesisLeaseCleanupValidator leaseCleanupValidator = new KinesisLeaseCleanupValidator();
|
||||||
private static final KinesisShardSyncer shardSyncer = new KinesisShardSyncer(leaseCleanupValidator);
|
private static final KinesisShardSyncer shardSyncer = new KinesisShardSyncer(leaseCleanupValidator);
|
||||||
|
private static final HashKeyRange hashKeyRange = new HashKeyRange().withStartingHashKey("0").withEndingHashKey("10");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Old/Obsolete max value of a sequence number (2^128 -1).
|
* Old/Obsolete max value of a sequence number (2^128 -1).
|
||||||
*/
|
*/
|
||||||
|
|
@ -154,10 +156,10 @@ public class ShardSyncerTest {
|
||||||
SequenceNumberRange sequenceRange = ShardObjectHelper.newSequenceNumberRange("342980", null);
|
SequenceNumberRange sequenceRange = ShardObjectHelper.newSequenceNumberRange("342980", null);
|
||||||
|
|
||||||
String shardId0 = "shardId-0";
|
String shardId0 = "shardId-0";
|
||||||
shards.add(ShardObjectHelper.newShard(shardId0, null, null, sequenceRange));
|
shards.add(ShardObjectHelper.newShard(shardId0, null, null, sequenceRange, hashKeyRange));
|
||||||
|
|
||||||
String shardId1 = "shardId-1";
|
String shardId1 = "shardId-1";
|
||||||
shards.add(ShardObjectHelper.newShard(shardId1, null, null, sequenceRange));
|
shards.add(ShardObjectHelper.newShard(shardId1, null, null, sequenceRange, hashKeyRange));
|
||||||
|
|
||||||
final LeaseSynchronizer leaseSynchronizer = getLeaseSynchronizer(shards, currentLeases);
|
final LeaseSynchronizer leaseSynchronizer = getLeaseSynchronizer(shards, currentLeases);
|
||||||
|
|
||||||
|
|
@ -183,16 +185,16 @@ public class ShardSyncerTest {
|
||||||
SequenceNumberRange sequenceRange = ShardObjectHelper.newSequenceNumberRange("342980", null);
|
SequenceNumberRange sequenceRange = ShardObjectHelper.newSequenceNumberRange("342980", null);
|
||||||
|
|
||||||
String shardId0 = "shardId-0";
|
String shardId0 = "shardId-0";
|
||||||
shards.add(ShardObjectHelper.newShard(shardId0, null, null, sequenceRange));
|
shards.add(ShardObjectHelper.newShard(shardId0, null, null, sequenceRange, hashKeyRange));
|
||||||
|
|
||||||
String shardId1 = "shardId-1";
|
String shardId1 = "shardId-1";
|
||||||
shards.add(ShardObjectHelper.newShard(shardId1, null, null, sequenceRange));
|
shards.add(ShardObjectHelper.newShard(shardId1, null, null, sequenceRange, hashKeyRange));
|
||||||
|
|
||||||
String shardId2 = "shardId-2";
|
String shardId2 = "shardId-2";
|
||||||
shards.add(ShardObjectHelper.newShard(shardId2, shardId1, null, sequenceRange));
|
shards.add(ShardObjectHelper.newShard(shardId2, shardId1, null, sequenceRange, hashKeyRange));
|
||||||
|
|
||||||
String shardIdWithLease = "shardId-3";
|
String shardIdWithLease = "shardId-3";
|
||||||
shards.add(ShardObjectHelper.newShard(shardIdWithLease, shardIdWithLease, null, sequenceRange));
|
shards.add(ShardObjectHelper.newShard(shardIdWithLease, shardIdWithLease, null, sequenceRange, hashKeyRange));
|
||||||
|
|
||||||
currentLeases.add(newLease(shardIdWithLease));
|
currentLeases.add(newLease(shardIdWithLease));
|
||||||
|
|
||||||
|
|
@ -699,9 +701,9 @@ public class ShardSyncerTest {
|
||||||
SequenceNumberRange sequenceRange = ShardObjectHelper.newSequenceNumberRange("342980", null);
|
SequenceNumberRange sequenceRange = ShardObjectHelper.newSequenceNumberRange("342980", null);
|
||||||
|
|
||||||
String shardId0 = "shardId-0";
|
String shardId0 = "shardId-0";
|
||||||
shards.add(ShardObjectHelper.newShard(shardId0, null, null, sequenceRange));
|
shards.add(ShardObjectHelper.newShard(shardId0, null, null, sequenceRange, hashKeyRange));
|
||||||
String shardId1 = "shardId-1";
|
String shardId1 = "shardId-1";
|
||||||
shards.add(ShardObjectHelper.newShard(shardId1, null, null, sequenceRange));
|
shards.add(ShardObjectHelper.newShard(shardId1, null, null, sequenceRange, hashKeyRange));
|
||||||
File dataFile = KinesisLocalFileDataCreator.generateTempDataFile(shards, 2, "testBootstrap1");
|
File dataFile = KinesisLocalFileDataCreator.generateTempDataFile(shards, 2, "testBootstrap1");
|
||||||
dataFile.deleteOnExit();
|
dataFile.deleteOnExit();
|
||||||
IKinesisProxy kinesisProxy = new KinesisLocalFileProxy(dataFile.getAbsolutePath());
|
IKinesisProxy kinesisProxy = new KinesisLocalFileProxy(dataFile.getAbsolutePath());
|
||||||
|
|
@ -731,10 +733,10 @@ public class ShardSyncerTest {
|
||||||
SequenceNumberRange sequenceRange = ShardObjectHelper.newSequenceNumberRange("342980", null);
|
SequenceNumberRange sequenceRange = ShardObjectHelper.newSequenceNumberRange("342980", null);
|
||||||
|
|
||||||
String shardId0 = "shardId-0";
|
String shardId0 = "shardId-0";
|
||||||
shards.add(ShardObjectHelper.newShard(shardId0, null, null, sequenceRange));
|
shards.add(ShardObjectHelper.newShard(shardId0, null, null, sequenceRange, hashKeyRange));
|
||||||
|
|
||||||
String shardId1 = "shardId-1";
|
String shardId1 = "shardId-1";
|
||||||
shards.add(ShardObjectHelper.newShard(shardId1, null, null, sequenceRange));
|
shards.add(ShardObjectHelper.newShard(shardId1, null, null, sequenceRange, hashKeyRange));
|
||||||
|
|
||||||
Set<InitialPositionInStreamExtended> initialPositions = new HashSet<InitialPositionInStreamExtended>();
|
Set<InitialPositionInStreamExtended> initialPositions = new HashSet<InitialPositionInStreamExtended>();
|
||||||
initialPositions.add(INITIAL_POSITION_LATEST);
|
initialPositions.add(INITIAL_POSITION_LATEST);
|
||||||
|
|
@ -769,17 +771,20 @@ public class ShardSyncerTest {
|
||||||
shardsWithoutLeases.add(ShardObjectHelper.newShard("shardId-0",
|
shardsWithoutLeases.add(ShardObjectHelper.newShard("shardId-0",
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
ShardObjectHelper.newSequenceNumberRange("303", "404")));
|
ShardObjectHelper.newSequenceNumberRange("303", "404"),
|
||||||
|
hashKeyRange));
|
||||||
final String lastShardId = "shardId-1";
|
final String lastShardId = "shardId-1";
|
||||||
shardsWithoutLeases.add(ShardObjectHelper.newShard(lastShardId,
|
shardsWithoutLeases.add(ShardObjectHelper.newShard(lastShardId,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
ShardObjectHelper.newSequenceNumberRange("405", null)));
|
ShardObjectHelper.newSequenceNumberRange("405", null),
|
||||||
|
hashKeyRange));
|
||||||
|
|
||||||
shardsWithLeases.add(ShardObjectHelper.newShard("shardId-2",
|
shardsWithLeases.add(ShardObjectHelper.newShard("shardId-2",
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
ShardObjectHelper.newSequenceNumberRange("202", "302")));
|
ShardObjectHelper.newSequenceNumberRange("202", "302"),
|
||||||
|
hashKeyRange));
|
||||||
currentLeases.add(newLease("shardId-2"));
|
currentLeases.add(newLease("shardId-2"));
|
||||||
|
|
||||||
final List<Shard> allShards =
|
final List<Shard> allShards =
|
||||||
|
|
@ -805,12 +810,14 @@ public class ShardSyncerTest {
|
||||||
shards.add(ShardObjectHelper.newShard(firstShardId,
|
shards.add(ShardObjectHelper.newShard(firstShardId,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
ShardObjectHelper.newSequenceNumberRange("303", "404")));
|
ShardObjectHelper.newSequenceNumberRange("303", "404"),
|
||||||
|
hashKeyRange));
|
||||||
final String lastShardId = "shardId-1";
|
final String lastShardId = "shardId-1";
|
||||||
shards.add(ShardObjectHelper.newShard(lastShardId,
|
shards.add(ShardObjectHelper.newShard(lastShardId,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
ShardObjectHelper.newSequenceNumberRange("405", null)));
|
ShardObjectHelper.newSequenceNumberRange("405", null),
|
||||||
|
hashKeyRange));
|
||||||
|
|
||||||
final LeaseSynchronizer leaseSynchronizer = getLeaseSynchronizer(shards, currentLeases);
|
final LeaseSynchronizer leaseSynchronizer = getLeaseSynchronizer(shards, currentLeases);
|
||||||
|
|
||||||
|
|
@ -1969,14 +1976,14 @@ public class ShardSyncerTest {
|
||||||
Map<String, Shard> kinesisShards = new HashMap<String, Shard>();
|
Map<String, Shard> kinesisShards = new HashMap<String, Shard>();
|
||||||
|
|
||||||
String parentShardId = "shardId-parent";
|
String parentShardId = "shardId-parent";
|
||||||
kinesisShards.put(parentShardId, ShardObjectHelper.newShard(parentShardId, null, null, null));
|
kinesisShards.put(parentShardId, ShardObjectHelper.newShard(parentShardId, null, null, null, hashKeyRange));
|
||||||
shardIdsOfCurrentLeases.add(parentShardId);
|
shardIdsOfCurrentLeases.add(parentShardId);
|
||||||
|
|
||||||
String adjacentParentShardId = "shardId-adjacentParent";
|
String adjacentParentShardId = "shardId-adjacentParent";
|
||||||
kinesisShards.put(adjacentParentShardId, ShardObjectHelper.newShard(adjacentParentShardId, null, null, null));
|
kinesisShards.put(adjacentParentShardId, ShardObjectHelper.newShard(adjacentParentShardId, null, null, null, hashKeyRange));
|
||||||
|
|
||||||
String shardId = "shardId-9-1";
|
String shardId = "shardId-9-1";
|
||||||
Shard shard = ShardObjectHelper.newShard(shardId, parentShardId, adjacentParentShardId, null);
|
Shard shard = ShardObjectHelper.newShard(shardId, parentShardId, adjacentParentShardId, null, hashKeyRange);
|
||||||
kinesisShards.put(shardId, shard);
|
kinesisShards.put(shardId, shard);
|
||||||
|
|
||||||
final MemoizationContext memoizationContext = new MemoizationContext();
|
final MemoizationContext memoizationContext = new MemoizationContext();
|
||||||
|
|
@ -2097,6 +2104,7 @@ public class ShardSyncerTest {
|
||||||
String adjacentParentShardId = "shardId-adjacentParent";
|
String adjacentParentShardId = "shardId-adjacentParent";
|
||||||
shard.setParentShardId(parentShardId);
|
shard.setParentShardId(parentShardId);
|
||||||
shard.setAdjacentParentShardId(adjacentParentShardId);
|
shard.setAdjacentParentShardId(adjacentParentShardId);
|
||||||
|
shard.setHashKeyRange(hashKeyRange);
|
||||||
|
|
||||||
KinesisClientLease lease = shardSyncer.newKCLLease(shard);
|
KinesisClientLease lease = shardSyncer.newKCLLease(shard);
|
||||||
Assert.assertEquals(shardId, lease.getLeaseKey());
|
Assert.assertEquals(shardId, lease.getLeaseKey());
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
import static org.mockito.Matchers.anyString;
|
import static org.mockito.Matchers.anyString;
|
||||||
|
import static org.mockito.Mockito.doAnswer;
|
||||||
import static org.mockito.Mockito.doNothing;
|
import static org.mockito.Mockito.doNothing;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
|
|
@ -65,7 +66,7 @@ import com.amazonaws.services.kinesis.leases.interfaces.ILeaseManager;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
import static com.amazonaws.services.kinesis.clientlibrary.lib.worker.ShutdownTask.RETRY_RANDOM_MAX_RANGE;
|
import static com.amazonaws.services.kinesis.clientlibrary.lib.worker.KinesisShutdownTask.RETRY_RANDOM_MAX_RANGE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -138,7 +139,7 @@ public class ShutdownTaskTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link ShutdownTask#call()}.
|
* Test method for {@link KinesisShutdownTask#call()}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public final void testCallWhenApplicationDoesNotCheckpoint() {
|
public final void testCallWhenApplicationDoesNotCheckpoint() {
|
||||||
|
|
@ -147,7 +148,7 @@ public class ShutdownTaskTest {
|
||||||
when(leaseCoordinator.getLeaseManager()).thenReturn(leaseManager);
|
when(leaseCoordinator.getLeaseManager()).thenReturn(leaseManager);
|
||||||
boolean cleanupLeasesOfCompletedShards = false;
|
boolean cleanupLeasesOfCompletedShards = false;
|
||||||
boolean ignoreUnexpectedChildShards = false;
|
boolean ignoreUnexpectedChildShards = false;
|
||||||
ShutdownTask task = new ShutdownTask(defaultShardInfo,
|
KinesisShutdownTask task = new KinesisShutdownTask(defaultShardInfo,
|
||||||
defaultRecordProcessor,
|
defaultRecordProcessor,
|
||||||
checkpointer,
|
checkpointer,
|
||||||
ShutdownReason.TERMINATE,
|
ShutdownReason.TERMINATE,
|
||||||
|
|
@ -170,7 +171,7 @@ public class ShutdownTaskTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link ShutdownTask#call()}.
|
* Test method for {@link KinesisShutdownTask#call()}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public final void testCallWhenCreatingLeaseThrows() throws Exception {
|
public final void testCallWhenCreatingLeaseThrows() throws Exception {
|
||||||
|
|
@ -182,7 +183,7 @@ public class ShutdownTaskTest {
|
||||||
|
|
||||||
final String exceptionMessage = "InvalidStateException is thrown.";
|
final String exceptionMessage = "InvalidStateException is thrown.";
|
||||||
when(leaseManager.createLeaseIfNotExists(any(KinesisClientLease.class))).thenThrow(new InvalidStateException(exceptionMessage));
|
when(leaseManager.createLeaseIfNotExists(any(KinesisClientLease.class))).thenThrow(new InvalidStateException(exceptionMessage));
|
||||||
ShutdownTask task = new ShutdownTask(defaultShardInfo,
|
KinesisShutdownTask task = new KinesisShutdownTask(defaultShardInfo,
|
||||||
defaultRecordProcessor,
|
defaultRecordProcessor,
|
||||||
checkpointer,
|
checkpointer,
|
||||||
ShutdownReason.TERMINATE,
|
ShutdownReason.TERMINATE,
|
||||||
|
|
@ -211,16 +212,21 @@ public class ShutdownTaskTest {
|
||||||
boolean cleanupLeasesOfCompletedShards = false;
|
boolean cleanupLeasesOfCompletedShards = false;
|
||||||
boolean ignoreUnexpectedChildShards = false;
|
boolean ignoreUnexpectedChildShards = false;
|
||||||
|
|
||||||
KinesisClientLease currentLease = createLease(defaultShardId, "leaseOwner", Collections.emptyList());
|
KinesisClientLease currentLease1 = createLease(defaultShardId, "leaseOwner", Collections.emptyList());
|
||||||
currentLease.setCheckpoint(new ExtendedSequenceNumber("3298"));
|
currentLease1.setCheckpoint(new ExtendedSequenceNumber("3298"));
|
||||||
|
KinesisClientLease currentLease2 = createLease(defaultShardId, "leaseOwner", Collections.emptyList());
|
||||||
|
currentLease2.setCheckpoint(ExtendedSequenceNumber.SHARD_END);
|
||||||
|
|
||||||
KinesisClientLease adjacentParentLease = createLease("ShardId-1", "leaseOwner", Collections.emptyList());
|
KinesisClientLease adjacentParentLease = createLease("ShardId-1", "leaseOwner", Collections.emptyList());
|
||||||
when(leaseCoordinator.getCurrentlyHeldLease(defaultShardId)).thenReturn( currentLease);
|
when(leaseCoordinator.getCurrentlyHeldLease(defaultShardId)).thenReturn( currentLease1);
|
||||||
when(leaseManager.getLease(defaultShardId)).thenReturn(currentLease);
|
// 6 times as part of parent lease get in failure mode and then two times in actual execution
|
||||||
|
when(leaseManager.getLease(defaultShardId)).thenReturn(currentLease1, currentLease1, currentLease1, currentLease1,
|
||||||
|
currentLease1, currentLease1, currentLease1, currentLease2);
|
||||||
when(leaseManager.getLease("ShardId-1")).thenReturn(null, null, null, null, null, adjacentParentLease);
|
when(leaseManager.getLease("ShardId-1")).thenReturn(null, null, null, null, null, adjacentParentLease);
|
||||||
|
|
||||||
// Make first 5 attempts with partial parent info in lease table
|
// Make first 5 attempts with partial parent info in lease table
|
||||||
for (int i = 0; i < 5; i++) {
|
for (int i = 0; i < 5; i++) {
|
||||||
ShutdownTask task = spy(new ShutdownTask(defaultShardInfo,
|
KinesisShutdownTask task = spy(new KinesisShutdownTask(defaultShardInfo,
|
||||||
defaultRecordProcessor,
|
defaultRecordProcessor,
|
||||||
checkpointer,
|
checkpointer,
|
||||||
ShutdownReason.TERMINATE,
|
ShutdownReason.TERMINATE,
|
||||||
|
|
@ -246,7 +252,7 @@ public class ShutdownTaskTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make next attempt with complete parent info in lease table
|
// Make next attempt with complete parent info in lease table
|
||||||
ShutdownTask task = spy(new ShutdownTask(defaultShardInfo,
|
KinesisShutdownTask task = spy(new KinesisShutdownTask(defaultShardInfo,
|
||||||
defaultRecordProcessor,
|
defaultRecordProcessor,
|
||||||
checkpointer,
|
checkpointer,
|
||||||
ShutdownReason.TERMINATE,
|
ShutdownReason.TERMINATE,
|
||||||
|
|
@ -267,7 +273,7 @@ public class ShutdownTaskTest {
|
||||||
verify(task, never()).isOneInNProbability(RETRY_RANDOM_MAX_RANGE);
|
verify(task, never()).isOneInNProbability(RETRY_RANDOM_MAX_RANGE);
|
||||||
verify(getRecordsCache).shutdown();
|
verify(getRecordsCache).shutdown();
|
||||||
verify(defaultRecordProcessor).shutdown(any(ShutdownInput.class));
|
verify(defaultRecordProcessor).shutdown(any(ShutdownInput.class));
|
||||||
verify(leaseCoordinator, never()).dropLease(currentLease);
|
verify(leaseCoordinator, never()).dropLease(currentLease1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -284,7 +290,7 @@ public class ShutdownTaskTest {
|
||||||
when(leaseManager.getLease("ShardId-1")).thenReturn(null, null, null, null, null, null, null, null, null, null, null);
|
when(leaseManager.getLease("ShardId-1")).thenReturn(null, null, null, null, null, null, null, null, null, null, null);
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
ShutdownTask task = spy(new ShutdownTask(defaultShardInfo,
|
KinesisShutdownTask task = spy(new KinesisShutdownTask(defaultShardInfo,
|
||||||
defaultRecordProcessor,
|
defaultRecordProcessor,
|
||||||
checkpointer,
|
checkpointer,
|
||||||
ShutdownReason.TERMINATE,
|
ShutdownReason.TERMINATE,
|
||||||
|
|
@ -309,7 +315,7 @@ public class ShutdownTaskTest {
|
||||||
verify(defaultRecordProcessor, never()).shutdown(any(ShutdownInput.class));
|
verify(defaultRecordProcessor, never()).shutdown(any(ShutdownInput.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
ShutdownTask task = spy(new ShutdownTask(defaultShardInfo,
|
KinesisShutdownTask task = spy(new KinesisShutdownTask(defaultShardInfo,
|
||||||
defaultRecordProcessor,
|
defaultRecordProcessor,
|
||||||
checkpointer,
|
checkpointer,
|
||||||
ShutdownReason.TERMINATE,
|
ShutdownReason.TERMINATE,
|
||||||
|
|
@ -337,10 +343,15 @@ public class ShutdownTaskTest {
|
||||||
public final void testCallWhenShardEnd() throws Exception {
|
public final void testCallWhenShardEnd() throws Exception {
|
||||||
RecordProcessorCheckpointer checkpointer = mock(RecordProcessorCheckpointer.class);
|
RecordProcessorCheckpointer checkpointer = mock(RecordProcessorCheckpointer.class);
|
||||||
when(checkpointer.getLastCheckpointValue()).thenReturn(ExtendedSequenceNumber.SHARD_END);
|
when(checkpointer.getLastCheckpointValue()).thenReturn(ExtendedSequenceNumber.SHARD_END);
|
||||||
|
final KinesisClientLease parentLease1 = createLease(defaultShardId, "leaseOwner", Collections.emptyList());
|
||||||
|
parentLease1.setCheckpoint(new ExtendedSequenceNumber("3298"));
|
||||||
|
final KinesisClientLease parentLease2 = createLease(defaultShardId, "leaseOwner", Collections.emptyList());
|
||||||
|
parentLease2.setCheckpoint(ExtendedSequenceNumber.SHARD_END);
|
||||||
|
when(leaseManager.getLease(defaultShardId)).thenReturn(parentLease1).thenReturn(parentLease2);
|
||||||
boolean cleanupLeasesOfCompletedShards = false;
|
boolean cleanupLeasesOfCompletedShards = false;
|
||||||
boolean ignoreUnexpectedChildShards = false;
|
boolean ignoreUnexpectedChildShards = false;
|
||||||
|
|
||||||
ShutdownTask task = new ShutdownTask(defaultShardInfo,
|
KinesisShutdownTask task = new KinesisShutdownTask(defaultShardInfo,
|
||||||
defaultRecordProcessor,
|
defaultRecordProcessor,
|
||||||
checkpointer,
|
checkpointer,
|
||||||
ShutdownReason.TERMINATE,
|
ShutdownReason.TERMINATE,
|
||||||
|
|
@ -374,7 +385,7 @@ public class ShutdownTaskTest {
|
||||||
boolean cleanupLeasesOfCompletedShards = false;
|
boolean cleanupLeasesOfCompletedShards = false;
|
||||||
boolean ignoreUnexpectedChildShards = false;
|
boolean ignoreUnexpectedChildShards = false;
|
||||||
|
|
||||||
ShutdownTask task = new ShutdownTask(shardInfo,
|
KinesisShutdownTask task = new KinesisShutdownTask(shardInfo,
|
||||||
defaultRecordProcessor,
|
defaultRecordProcessor,
|
||||||
checkpointer,
|
checkpointer,
|
||||||
ShutdownReason.TERMINATE,
|
ShutdownReason.TERMINATE,
|
||||||
|
|
@ -404,7 +415,7 @@ public class ShutdownTaskTest {
|
||||||
boolean cleanupLeasesOfCompletedShards = false;
|
boolean cleanupLeasesOfCompletedShards = false;
|
||||||
boolean ignoreUnexpectedChildShards = false;
|
boolean ignoreUnexpectedChildShards = false;
|
||||||
|
|
||||||
ShutdownTask task = new ShutdownTask(defaultShardInfo,
|
KinesisShutdownTask task = new KinesisShutdownTask(defaultShardInfo,
|
||||||
defaultRecordProcessor,
|
defaultRecordProcessor,
|
||||||
checkpointer,
|
checkpointer,
|
||||||
ShutdownReason.ZOMBIE,
|
ShutdownReason.ZOMBIE,
|
||||||
|
|
@ -427,12 +438,12 @@ public class ShutdownTaskTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link ShutdownTask#getTaskType()}.
|
* Test method for {@link KinesisShutdownTask#getTaskType()}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public final void testGetTaskType() {
|
public final void testGetTaskType() {
|
||||||
KinesisClientLibLeaseCoordinator leaseCoordinator = mock(KinesisClientLibLeaseCoordinator.class);
|
KinesisClientLibLeaseCoordinator leaseCoordinator = mock(KinesisClientLibLeaseCoordinator.class);
|
||||||
ShutdownTask task = new ShutdownTask(null, null, null, null,
|
KinesisShutdownTask task = new KinesisShutdownTask(null, null, null, null,
|
||||||
null, null, false,
|
null, null, false,
|
||||||
false, leaseCoordinator, 0,
|
false, leaseCoordinator, 0,
|
||||||
getRecordsCache, shardSyncer, shardSyncStrategy, Collections.emptyList(), leaseCleanupManager);
|
getRecordsCache, shardSyncer, shardSyncStrategy, Collections.emptyList(), leaseCleanupManager);
|
||||||
|
|
|
||||||
|
|
@ -186,7 +186,7 @@ public class WorkerTest {
|
||||||
@Mock
|
@Mock
|
||||||
private IRecordProcessor v2RecordProcessor;
|
private IRecordProcessor v2RecordProcessor;
|
||||||
@Mock
|
@Mock
|
||||||
private ShardConsumer shardConsumer;
|
private IShardConsumer shardConsumer;
|
||||||
@Mock
|
@Mock
|
||||||
private Future<TaskResult> taskFuture;
|
private Future<TaskResult> taskFuture;
|
||||||
@Mock
|
@Mock
|
||||||
|
|
@ -204,6 +204,10 @@ public class WorkerTest {
|
||||||
when(config.getRecordsFetcherFactory()).thenReturn(recordsFetcherFactory);
|
when(config.getRecordsFetcherFactory()).thenReturn(recordsFetcherFactory);
|
||||||
when(leaseCoordinator.getLeaseManager()).thenReturn(mock(ILeaseManager.class));
|
when(leaseCoordinator.getLeaseManager()).thenReturn(mock(ILeaseManager.class));
|
||||||
when(streamConfig.getStreamProxy()).thenReturn(kinesisProxy);
|
when(streamConfig.getStreamProxy()).thenReturn(kinesisProxy);
|
||||||
|
Worker.MIN_WAIT_TIME_FOR_LEASE_TABLE_CHECK_MILLIS = 10;
|
||||||
|
Worker.MAX_WAIT_TIME_FOR_LEASE_TABLE_CHECK_MILLIS = 50;
|
||||||
|
Worker.LEASE_TABLE_CHECK_FREQUENCY_MILLIS = 10;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CHECKSTYLE:IGNORE AnonInnerLengthCheck FOR NEXT 50 LINES
|
// CHECKSTYLE:IGNORE AnonInnerLengthCheck FOR NEXT 50 LINES
|
||||||
|
|
@ -293,13 +297,13 @@ public class WorkerTest {
|
||||||
KinesisClientLibConfiguration.DEFAULT_SKIP_SHARD_SYNC_AT_STARTUP_IF_LEASES_EXIST,
|
KinesisClientLibConfiguration.DEFAULT_SKIP_SHARD_SYNC_AT_STARTUP_IF_LEASES_EXIST,
|
||||||
shardPrioritization);
|
shardPrioritization);
|
||||||
ShardInfo shardInfo = new ShardInfo(dummyKinesisShardId, testConcurrencyToken, null, ExtendedSequenceNumber.TRIM_HORIZON);
|
ShardInfo shardInfo = new ShardInfo(dummyKinesisShardId, testConcurrencyToken, null, ExtendedSequenceNumber.TRIM_HORIZON);
|
||||||
ShardConsumer consumer = worker.createOrGetShardConsumer(shardInfo, streamletFactory);
|
IShardConsumer consumer = worker.createOrGetShardConsumer(shardInfo, streamletFactory);
|
||||||
Assert.assertNotNull(consumer);
|
Assert.assertNotNull(consumer);
|
||||||
ShardConsumer consumer2 = worker.createOrGetShardConsumer(shardInfo, streamletFactory);
|
IShardConsumer consumer2 = worker.createOrGetShardConsumer(shardInfo, streamletFactory);
|
||||||
Assert.assertSame(consumer, consumer2);
|
Assert.assertSame(consumer, consumer2);
|
||||||
ShardInfo shardInfoWithSameShardIdButDifferentConcurrencyToken =
|
ShardInfo shardInfoWithSameShardIdButDifferentConcurrencyToken =
|
||||||
new ShardInfo(dummyKinesisShardId, anotherConcurrencyToken, null, ExtendedSequenceNumber.TRIM_HORIZON);
|
new ShardInfo(dummyKinesisShardId, anotherConcurrencyToken, null, ExtendedSequenceNumber.TRIM_HORIZON);
|
||||||
ShardConsumer consumer3 =
|
IShardConsumer consumer3 =
|
||||||
worker.createOrGetShardConsumer(shardInfoWithSameShardIdButDifferentConcurrencyToken, streamletFactory);
|
worker.createOrGetShardConsumer(shardInfoWithSameShardIdButDifferentConcurrencyToken, streamletFactory);
|
||||||
Assert.assertNotNull(consumer3);
|
Assert.assertNotNull(consumer3);
|
||||||
Assert.assertNotSame(consumer3, consumer);
|
Assert.assertNotSame(consumer3, consumer);
|
||||||
|
|
@ -415,10 +419,10 @@ public class WorkerTest {
|
||||||
new ShardInfo(dummyKinesisShardId, anotherConcurrencyToken, null, ExtendedSequenceNumber.TRIM_HORIZON);
|
new ShardInfo(dummyKinesisShardId, anotherConcurrencyToken, null, ExtendedSequenceNumber.TRIM_HORIZON);
|
||||||
ShardInfo shardInfo2 = new ShardInfo(anotherDummyKinesisShardId, concurrencyToken, null, ExtendedSequenceNumber.TRIM_HORIZON);
|
ShardInfo shardInfo2 = new ShardInfo(anotherDummyKinesisShardId, concurrencyToken, null, ExtendedSequenceNumber.TRIM_HORIZON);
|
||||||
|
|
||||||
ShardConsumer consumerOfShardInfo1 = worker.createOrGetShardConsumer(shardInfo1, streamletFactory);
|
IShardConsumer consumerOfShardInfo1 = worker.createOrGetShardConsumer(shardInfo1, streamletFactory);
|
||||||
ShardConsumer consumerOfDuplicateOfShardInfo1ButWithAnotherConcurrencyToken =
|
IShardConsumer consumerOfDuplicateOfShardInfo1ButWithAnotherConcurrencyToken =
|
||||||
worker.createOrGetShardConsumer(duplicateOfShardInfo1ButWithAnotherConcurrencyToken, streamletFactory);
|
worker.createOrGetShardConsumer(duplicateOfShardInfo1ButWithAnotherConcurrencyToken, streamletFactory);
|
||||||
ShardConsumer consumerOfShardInfo2 = worker.createOrGetShardConsumer(shardInfo2, streamletFactory);
|
IShardConsumer consumerOfShardInfo2 = worker.createOrGetShardConsumer(shardInfo2, streamletFactory);
|
||||||
|
|
||||||
Set<ShardInfo> assignedShards = new HashSet<ShardInfo>();
|
Set<ShardInfo> assignedShards = new HashSet<ShardInfo>();
|
||||||
assignedShards.add(shardInfo1);
|
assignedShards.add(shardInfo1);
|
||||||
|
|
@ -1215,11 +1219,11 @@ public class WorkerTest {
|
||||||
false,
|
false,
|
||||||
shardPrioritization);
|
shardPrioritization);
|
||||||
|
|
||||||
final Map<ShardInfo, ShardConsumer> shardInfoShardConsumerMap = worker.getShardInfoShardConsumerMap();
|
final Map<ShardInfo, IShardConsumer> shardInfoShardConsumerMap = worker.getShardInfoShardConsumerMap();
|
||||||
final ShardInfo completedShardInfo = KinesisClientLibLeaseCoordinator.convertLeaseToAssignment(completedLease);
|
final ShardInfo completedShardInfo = KinesisClientLibLeaseCoordinator.convertLeaseToAssignment(completedLease);
|
||||||
final ShardConsumer completedShardConsumer = mock(ShardConsumer.class);
|
final KinesisShardConsumer completedShardConsumer = mock(KinesisShardConsumer.class);
|
||||||
shardInfoShardConsumerMap.put(completedShardInfo, completedShardConsumer);
|
shardInfoShardConsumerMap.put(completedShardInfo, completedShardConsumer);
|
||||||
when(completedShardConsumer.getCurrentState()).thenReturn(ConsumerStates.ShardConsumerState.SHUTDOWN_COMPLETE);
|
when(completedShardConsumer.getCurrentState()).thenReturn(KinesisConsumerStates.ShardConsumerState.SHUTDOWN_COMPLETE);
|
||||||
|
|
||||||
Callable<GracefulShutdownContext> callable = worker.createWorkerShutdownCallable();
|
Callable<GracefulShutdownContext> callable = worker.createWorkerShutdownCallable();
|
||||||
assertThat(worker.hasGracefulShutdownStarted(), equalTo(false));
|
assertThat(worker.hasGracefulShutdownStarted(), equalTo(false));
|
||||||
|
|
@ -1334,11 +1338,11 @@ public class WorkerTest {
|
||||||
|
|
||||||
verify(executorService).submit(argThat(both(isA(MetricsCollectingTaskDecorator.class))
|
verify(executorService).submit(argThat(both(isA(MetricsCollectingTaskDecorator.class))
|
||||||
.and(TaskTypeMatcher.isOfType(TaskType.SHUTDOWN)).and(ReflectionFieldMatcher
|
.and(TaskTypeMatcher.isOfType(TaskType.SHUTDOWN)).and(ReflectionFieldMatcher
|
||||||
.withField(ShutdownTask.class, "shardInfo", equalTo(shardInfo1)))));
|
.withField(KinesisShutdownTask.class, "shardInfo", equalTo(shardInfo1)))));
|
||||||
|
|
||||||
verify(executorService, never()).submit(argThat(both(isA(MetricsCollectingTaskDecorator.class))
|
verify(executorService, never()).submit(argThat(both(isA(MetricsCollectingTaskDecorator.class))
|
||||||
.and(TaskTypeMatcher.isOfType(TaskType.SHUTDOWN)).and(ReflectionFieldMatcher
|
.and(TaskTypeMatcher.isOfType(TaskType.SHUTDOWN)).and(ReflectionFieldMatcher
|
||||||
.withField(ShutdownTask.class, "shardInfo", equalTo(shardInfo2)))));
|
.withField(KinesisShutdownTask.class, "shardInfo", equalTo(shardInfo2)))));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1447,11 +1451,11 @@ public class WorkerTest {
|
||||||
|
|
||||||
verify(executorService, never()).submit(argThat(both(isA(MetricsCollectingTaskDecorator.class))
|
verify(executorService, never()).submit(argThat(both(isA(MetricsCollectingTaskDecorator.class))
|
||||||
.and(TaskTypeMatcher.isOfType(TaskType.SHUTDOWN)).and(ReflectionFieldMatcher
|
.and(TaskTypeMatcher.isOfType(TaskType.SHUTDOWN)).and(ReflectionFieldMatcher
|
||||||
.withField(ShutdownTask.class, "shardInfo", equalTo(shardInfo1)))));
|
.withField(KinesisShutdownTask.class, "shardInfo", equalTo(shardInfo1)))));
|
||||||
|
|
||||||
verify(executorService, never()).submit(argThat(both(isA(MetricsCollectingTaskDecorator.class))
|
verify(executorService, never()).submit(argThat(both(isA(MetricsCollectingTaskDecorator.class))
|
||||||
.and(TaskTypeMatcher.isOfType(TaskType.SHUTDOWN)).and(ReflectionFieldMatcher
|
.and(TaskTypeMatcher.isOfType(TaskType.SHUTDOWN)).and(ReflectionFieldMatcher
|
||||||
.withField(ShutdownTask.class, "shardInfo", equalTo(shardInfo2)))));
|
.withField(KinesisShutdownTask.class, "shardInfo", equalTo(shardInfo2)))));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -2009,19 +2013,19 @@ public class WorkerTest {
|
||||||
@Override
|
@Override
|
||||||
protected boolean matchesSafely(MetricsCollectingTaskDecorator item, Description mismatchDescription) {
|
protected boolean matchesSafely(MetricsCollectingTaskDecorator item, Description mismatchDescription) {
|
||||||
return Condition.matched(item, mismatchDescription)
|
return Condition.matched(item, mismatchDescription)
|
||||||
.and(new Condition.Step<MetricsCollectingTaskDecorator, ShutdownTask>() {
|
.and(new Condition.Step<MetricsCollectingTaskDecorator, KinesisShutdownTask>() {
|
||||||
@Override
|
@Override
|
||||||
public Condition<ShutdownTask> apply(MetricsCollectingTaskDecorator value,
|
public Condition<KinesisShutdownTask> apply(MetricsCollectingTaskDecorator value,
|
||||||
Description mismatch) {
|
Description mismatch) {
|
||||||
if (!(value.getOther() instanceof ShutdownTask)) {
|
if (!(value.getOther() instanceof KinesisShutdownTask)) {
|
||||||
mismatch.appendText("Wrapped task isn't a shutdown task");
|
mismatch.appendText("Wrapped task isn't a shutdown task");
|
||||||
return Condition.notMatched();
|
return Condition.notMatched();
|
||||||
}
|
}
|
||||||
return Condition.matched((ShutdownTask) value.getOther(), mismatch);
|
return Condition.matched((KinesisShutdownTask) value.getOther(), mismatch);
|
||||||
}
|
}
|
||||||
}).and(new Condition.Step<ShutdownTask, ShutdownReason>() {
|
}).and(new Condition.Step<KinesisShutdownTask, ShutdownReason>() {
|
||||||
@Override
|
@Override
|
||||||
public Condition<ShutdownReason> apply(ShutdownTask value, Description mismatch) {
|
public Condition<ShutdownReason> apply(KinesisShutdownTask value, Description mismatch) {
|
||||||
return Condition.matched(value.getReason(), mismatch);
|
return Condition.matched(value.getReason(), mismatch);
|
||||||
}
|
}
|
||||||
}).matching(matcher);
|
}).matching(matcher);
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.amazonaws.services.kinesis.model.ChildShard;
|
import com.amazonaws.services.kinesis.model.ChildShard;
|
||||||
|
import com.amazonaws.services.kinesis.model.HashKeyRange;
|
||||||
import com.amazonaws.services.kinesis.model.ShardFilter;
|
import com.amazonaws.services.kinesis.model.ShardFilter;
|
||||||
import com.amazonaws.util.CollectionUtils;
|
import com.amazonaws.util.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
@ -408,11 +409,13 @@ public class KinesisLocalFileProxy implements IKinesisProxy {
|
||||||
ChildShard leftChild = new ChildShard();
|
ChildShard leftChild = new ChildShard();
|
||||||
leftChild.setShardId("shardId-1");
|
leftChild.setShardId("shardId-1");
|
||||||
leftChild.setParentShards(parentShards);
|
leftChild.setParentShards(parentShards);
|
||||||
|
leftChild.setHashKeyRange(new HashKeyRange().withStartingHashKey("0").withEndingHashKey("10"));
|
||||||
childShards.add(leftChild);
|
childShards.add(leftChild);
|
||||||
|
|
||||||
ChildShard rightChild = new ChildShard();
|
ChildShard rightChild = new ChildShard();
|
||||||
rightChild.setShardId("shardId-2");
|
rightChild.setShardId("shardId-2");
|
||||||
rightChild.setParentShards(parentShards);
|
rightChild.setParentShards(parentShards);
|
||||||
|
rightChild.setHashKeyRange(new HashKeyRange().withStartingHashKey("11").withEndingHashKey(MAX_HASHKEY_VALUE.toString()));
|
||||||
childShards.add(rightChild);
|
childShards.add(rightChild);
|
||||||
return childShards;
|
return childShards;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import static org.hamcrest.Matchers.nullValue;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
import static org.mockito.Matchers.argThat;
|
import static org.mockito.Matchers.argThat;
|
||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
|
@ -47,6 +48,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.hamcrest.Description;
|
import org.hamcrest.Description;
|
||||||
import org.hamcrest.TypeSafeDiagnosingMatcher;
|
import org.hamcrest.TypeSafeDiagnosingMatcher;
|
||||||
|
|
@ -58,6 +60,7 @@ import org.mockito.Mock;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
import com.amazonaws.AmazonServiceException;
|
import com.amazonaws.AmazonServiceException;
|
||||||
|
import com.amazonaws.arn.Arn;
|
||||||
import com.amazonaws.auth.AWSCredentialsProvider;
|
import com.amazonaws.auth.AWSCredentialsProvider;
|
||||||
import com.amazonaws.services.dynamodbv2.streamsadapter.AmazonDynamoDBStreamsAdapterClient;
|
import com.amazonaws.services.dynamodbv2.streamsadapter.AmazonDynamoDBStreamsAdapterClient;
|
||||||
import com.amazonaws.services.dynamodbv2.streamsadapter.AmazonDynamoDBStreamsAdapterClientChild;
|
import com.amazonaws.services.dynamodbv2.streamsadapter.AmazonDynamoDBStreamsAdapterClientChild;
|
||||||
|
|
@ -76,11 +79,17 @@ import com.amazonaws.services.kinesis.model.ShardIteratorType;
|
||||||
import com.amazonaws.services.kinesis.model.StreamDescription;
|
import com.amazonaws.services.kinesis.model.StreamDescription;
|
||||||
import com.amazonaws.services.kinesis.model.StreamStatus;
|
import com.amazonaws.services.kinesis.model.StreamStatus;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
public class KinesisProxyTest {
|
public class KinesisProxyTest {
|
||||||
private static final String TEST_STRING = "TestString";
|
private static final String TEST_STRING = "TestString";
|
||||||
|
private static final String ACCOUNT_ID = "123456789012";
|
||||||
|
private static final Arn TEST_ARN = Arn.builder()
|
||||||
|
.withPartition("aws")
|
||||||
|
.withService("kinesis")
|
||||||
|
.withRegion("us-east-1")
|
||||||
|
.withAccountId(ACCOUNT_ID)
|
||||||
|
.withResource("stream/" + TEST_STRING)
|
||||||
|
.build();
|
||||||
private static final long DESCRIBE_STREAM_BACKOFF_TIME = 10L;
|
private static final long DESCRIBE_STREAM_BACKOFF_TIME = 10L;
|
||||||
private static final long LIST_SHARDS_BACKOFF_TIME = 10L;
|
private static final long LIST_SHARDS_BACKOFF_TIME = 10L;
|
||||||
private static final int DESCRIBE_STREAM_RETRY_TIMES = 3;
|
private static final int DESCRIBE_STREAM_RETRY_TIMES = 3;
|
||||||
|
|
@ -92,6 +101,7 @@ public class KinesisProxyTest {
|
||||||
private static final String SHARD_4 = "shard-4";
|
private static final String SHARD_4 = "shard-4";
|
||||||
private static final String NOT_CACHED_SHARD = "ShardId-0005";
|
private static final String NOT_CACHED_SHARD = "ShardId-0005";
|
||||||
private static final String NEVER_PRESENT_SHARD = "ShardId-0010";
|
private static final String NEVER_PRESENT_SHARD = "ShardId-0010";
|
||||||
|
private static final String REQUEST_ID = "requestId";
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private AmazonKinesis mockClient;
|
private AmazonKinesis mockClient;
|
||||||
|
|
@ -130,6 +140,7 @@ public class KinesisProxyTest {
|
||||||
public void setUpTest() {
|
public void setUpTest() {
|
||||||
// Set up kinesis ddbProxy
|
// Set up kinesis ddbProxy
|
||||||
when(config.getStreamName()).thenReturn(TEST_STRING);
|
when(config.getStreamName()).thenReturn(TEST_STRING);
|
||||||
|
when(config.getStreamArn()).thenReturn(TEST_ARN);
|
||||||
when(config.getListShardsBackoffTimeInMillis()).thenReturn(LIST_SHARDS_BACKOFF_TIME);
|
when(config.getListShardsBackoffTimeInMillis()).thenReturn(LIST_SHARDS_BACKOFF_TIME);
|
||||||
when(config.getMaxListShardsRetryAttempts()).thenReturn(LIST_SHARDS_RETRY_TIMES);
|
when(config.getMaxListShardsRetryAttempts()).thenReturn(LIST_SHARDS_RETRY_TIMES);
|
||||||
when(config.getKinesisCredentialsProvider()).thenReturn(mockCredentialsProvider);
|
when(config.getKinesisCredentialsProvider()).thenReturn(mockCredentialsProvider);
|
||||||
|
|
@ -161,7 +172,8 @@ public class KinesisProxyTest {
|
||||||
// Second call describeStream returning response with rest shards.
|
// Second call describeStream returning response with rest shards.
|
||||||
DescribeStreamResult responseWithMoreData = createGetStreamInfoResponse(shards.subList(0, 2), true);
|
DescribeStreamResult responseWithMoreData = createGetStreamInfoResponse(shards.subList(0, 2), true);
|
||||||
DescribeStreamResult responseFinal = createGetStreamInfoResponse(shards.subList(2, shards.size()), false);
|
DescribeStreamResult responseFinal = createGetStreamInfoResponse(shards.subList(2, shards.size()), false);
|
||||||
doReturn(responseWithMoreData).when(mockDDBStreamClient).describeStream(argThat(new IsRequestWithStartShardId(null)));
|
IsRequestWithStartShardId requestMatcher = IsRequestWithStartShardId.builder().streamName(TEST_STRING).build();
|
||||||
|
doReturn(responseWithMoreData).when(mockDDBStreamClient).describeStream(argThat(requestMatcher));
|
||||||
doReturn(responseFinal).when(mockDDBStreamClient)
|
doReturn(responseFinal).when(mockDDBStreamClient)
|
||||||
.describeStream(argThat(new OldIsRequestWithStartShardId(shards.get(1).getShardId())));
|
.describeStream(argThat(new OldIsRequestWithStartShardId(shards.get(1).getShardId())));
|
||||||
|
|
||||||
|
|
@ -249,54 +261,6 @@ public class KinesisProxyTest {
|
||||||
ddbProxy.getShardList();
|
ddbProxy.getShardList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetStreamInfoStoresOffset() throws Exception {
|
|
||||||
when(describeStreamResult.getStreamDescription()).thenReturn(streamDescription);
|
|
||||||
when(streamDescription.getStreamStatus()).thenReturn(StreamStatus.ACTIVE.name());
|
|
||||||
Shard shard1 = mock(Shard.class);
|
|
||||||
Shard shard2 = mock(Shard.class);
|
|
||||||
Shard shard3 = mock(Shard.class);
|
|
||||||
List<Shard> shardList1 = Collections.singletonList(shard1);
|
|
||||||
List<Shard> shardList2 = Collections.singletonList(shard2);
|
|
||||||
List<Shard> shardList3 = Collections.singletonList(shard3);
|
|
||||||
|
|
||||||
String shardId1 = "ShardId-0001";
|
|
||||||
String shardId2 = "ShardId-0002";
|
|
||||||
String shardId3 = "ShardId-0003";
|
|
||||||
|
|
||||||
when(shard1.getShardId()).thenReturn(shardId1);
|
|
||||||
when(shard2.getShardId()).thenReturn(shardId2);
|
|
||||||
when(shard3.getShardId()).thenReturn(shardId3);
|
|
||||||
|
|
||||||
when(streamDescription.getShards()).thenReturn(shardList1).thenReturn(shardList2).thenReturn(shardList3);
|
|
||||||
when(streamDescription.isHasMoreShards()).thenReturn(true, true, false);
|
|
||||||
when(mockDDBStreamClient.describeStream(argThat(describeWithoutShardId()))).thenReturn(describeStreamResult);
|
|
||||||
|
|
||||||
when(mockDDBStreamClient.describeStream(argThat(describeWithShardId(shardId1))))
|
|
||||||
.thenThrow(new LimitExceededException("1"), new LimitExceededException("2"),
|
|
||||||
new LimitExceededException("3"))
|
|
||||||
.thenReturn(describeStreamResult);
|
|
||||||
|
|
||||||
when(mockDDBStreamClient.describeStream(argThat(describeWithShardId(shardId2)))).thenReturn(describeStreamResult);
|
|
||||||
|
|
||||||
boolean limitExceeded = false;
|
|
||||||
try {
|
|
||||||
ddbProxy.getShardList();
|
|
||||||
} catch (LimitExceededException le) {
|
|
||||||
limitExceeded = true;
|
|
||||||
}
|
|
||||||
assertThat(limitExceeded, equalTo(true));
|
|
||||||
List<Shard> actualShards = ddbProxy.getShardList();
|
|
||||||
List<Shard> expectedShards = Arrays.asList(shard1, shard2, shard3);
|
|
||||||
|
|
||||||
assertThat(actualShards, equalTo(expectedShards));
|
|
||||||
|
|
||||||
verify(mockDDBStreamClient).describeStream(argThat(describeWithoutShardId()));
|
|
||||||
verify(mockDDBStreamClient, times(4)).describeStream(argThat(describeWithShardId(shardId1)));
|
|
||||||
verify(mockDDBStreamClient).describeStream(argThat(describeWithShardId(shardId2)));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testListShardsWithMoreDataAvailable() {
|
public void testListShardsWithMoreDataAvailable() {
|
||||||
ListShardsResult responseWithMoreData = new ListShardsResult().withShards(shards.subList(0, 2)).withNextToken(NEXT_TOKEN);
|
ListShardsResult responseWithMoreData = new ListShardsResult().withShards(shards.subList(0, 2)).withNextToken(NEXT_TOKEN);
|
||||||
|
|
@ -356,7 +320,8 @@ public class KinesisProxyTest {
|
||||||
public void testGetShardListWithDDBChildClient() {
|
public void testGetShardListWithDDBChildClient() {
|
||||||
DescribeStreamResult responseWithMoreData = createGetStreamInfoResponse(shards.subList(0, 2), true);
|
DescribeStreamResult responseWithMoreData = createGetStreamInfoResponse(shards.subList(0, 2), true);
|
||||||
DescribeStreamResult responseFinal = createGetStreamInfoResponse(shards.subList(2, shards.size()), false);
|
DescribeStreamResult responseFinal = createGetStreamInfoResponse(shards.subList(2, shards.size()), false);
|
||||||
doReturn(responseWithMoreData).when(mockDDBChildClient).describeStream(argThat(new IsRequestWithStartShardId(null)));
|
IsRequestWithStartShardId requestMatcher = IsRequestWithStartShardId.builder().streamName(TEST_STRING).build();
|
||||||
|
doReturn(responseWithMoreData).when(mockDDBChildClient).describeStream(argThat(requestMatcher));
|
||||||
doReturn(responseFinal).when(mockDDBChildClient)
|
doReturn(responseFinal).when(mockDDBChildClient)
|
||||||
.describeStream(argThat(new OldIsRequestWithStartShardId(shards.get(1).getShardId())));
|
.describeStream(argThat(new OldIsRequestWithStartShardId(shards.get(1).getShardId())));
|
||||||
|
|
||||||
|
|
@ -483,6 +448,47 @@ public class KinesisProxyTest {
|
||||||
verify(mockClient).listShards(any());
|
verify(mockClient).listShards(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that if we fail halfway through a listShards call, we fail gracefully and subsequent calls are not
|
||||||
|
* affected by the failure of the first request.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testNoDuplicateShardsInPartialFailure() {
|
||||||
|
proxy.setCachedShardMap(null);
|
||||||
|
|
||||||
|
ListShardsResult firstPage = new ListShardsResult().withShards(shards.subList(0, 2)).withNextToken(NEXT_TOKEN);
|
||||||
|
ListShardsResult lastPage = new ListShardsResult().withShards(shards.subList(2, shards.size())).withNextToken(null);
|
||||||
|
|
||||||
|
when(mockClient.listShards(any()))
|
||||||
|
.thenReturn(firstPage).thenThrow(new RuntimeException("Failed!"))
|
||||||
|
.thenReturn(firstPage).thenReturn(lastPage);
|
||||||
|
|
||||||
|
try {
|
||||||
|
proxy.getShardList();
|
||||||
|
fail("First ListShards call should have failed!");
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
assertEquals(shards, proxy.getShardList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that if we receive any duplicate shard responses from the service during a shard sync, we dedup the response
|
||||||
|
* and continue gracefully.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testDuplicateShardResponseDedupedGracefully() {
|
||||||
|
proxy.setCachedShardMap(null);
|
||||||
|
List<Shard> duplicateShards = new ArrayList<>(shards);
|
||||||
|
duplicateShards.addAll(shards);
|
||||||
|
ListShardsResult pageOfShards = new ListShardsResult().withShards(duplicateShards).withNextToken(null);
|
||||||
|
|
||||||
|
when(mockClient.listShards(any())).thenReturn(pageOfShards);
|
||||||
|
|
||||||
|
proxy.getShardList();
|
||||||
|
assertEquals(shards, proxy.getShardList());
|
||||||
|
}
|
||||||
|
|
||||||
private void mockListShardsForSingleResponse(List<Shard> shards) {
|
private void mockListShardsForSingleResponse(List<Shard> shards) {
|
||||||
when(mockClient.listShards(any())).thenReturn(listShardsResult);
|
when(mockClient.listShards(any())).thenReturn(listShardsResult);
|
||||||
when(listShardsResult.getShards()).thenReturn(shards);
|
when(listShardsResult.getShards()).thenReturn(shards);
|
||||||
|
|
@ -503,37 +509,61 @@ public class KinesisProxyTest {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IsRequestWithStartShardId describeWithoutShardId() {
|
|
||||||
return describeWithShardId(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IsRequestWithStartShardId describeWithShardId(String shardId) {
|
private IsRequestWithStartShardId describeWithShardId(String shardId) {
|
||||||
return new IsRequestWithStartShardId(shardId);
|
return IsRequestWithStartShardId.builder()
|
||||||
|
.streamName(TEST_STRING)
|
||||||
|
.streamArn(TEST_ARN)
|
||||||
|
.shardId(shardId)
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Builder
|
||||||
private static class IsRequestWithStartShardId extends TypeSafeDiagnosingMatcher<DescribeStreamRequest> {
|
private static class IsRequestWithStartShardId extends TypeSafeDiagnosingMatcher<DescribeStreamRequest> {
|
||||||
|
|
||||||
|
private final String streamName;
|
||||||
|
private final Arn streamArn;
|
||||||
private final String shardId;
|
private final String shardId;
|
||||||
|
|
||||||
public IsRequestWithStartShardId(String shardId) {
|
|
||||||
this.shardId = shardId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean matchesSafely(DescribeStreamRequest item, Description mismatchDescription) {
|
protected boolean matchesSafely(DescribeStreamRequest item, Description mismatchDescription) {
|
||||||
|
boolean matches = true;
|
||||||
|
if (streamName == null) {
|
||||||
|
if (item.getStreamName() != null) {
|
||||||
|
mismatchDescription.appendText("Expected streamName of null, but was ")
|
||||||
|
.appendValue(item.getStreamName());
|
||||||
|
matches = false;
|
||||||
|
}
|
||||||
|
} else if (!streamName.equals(item.getStreamName())) {
|
||||||
|
mismatchDescription.appendValue(streamName).appendText(" doesn't match expected ")
|
||||||
|
.appendValue(item.getStreamName());
|
||||||
|
matches = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (streamArn == null) {
|
||||||
|
if (item.getStreamARN() != null) {
|
||||||
|
mismatchDescription.appendText("Expected streamArn of null, but was ")
|
||||||
|
.appendValue(item.getStreamARN());
|
||||||
|
matches = false;
|
||||||
|
}
|
||||||
|
} else if (!streamArn.equals(Arn.fromString(item.getStreamARN()))) {
|
||||||
|
mismatchDescription.appendValue(streamArn).appendText(" doesn't match expected ")
|
||||||
|
.appendValue(item.getStreamARN());
|
||||||
|
matches = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (shardId == null) {
|
if (shardId == null) {
|
||||||
if (item.getExclusiveStartShardId() != null) {
|
if (item.getExclusiveStartShardId() != null) {
|
||||||
mismatchDescription.appendText("Expected starting shard id of null, but was ")
|
mismatchDescription.appendText("Expected starting shard id of null, but was ")
|
||||||
.appendValue(item.getExclusiveStartShardId());
|
.appendValue(item.getExclusiveStartShardId());
|
||||||
return false;
|
matches = false;
|
||||||
}
|
}
|
||||||
} else if (!shardId.equals(item.getExclusiveStartShardId())) {
|
} else if (!shardId.equals(item.getExclusiveStartShardId())) {
|
||||||
mismatchDescription.appendValue(shardId).appendText(" doesn't match expected ")
|
mismatchDescription.appendValue(shardId).appendText(" doesn't match expected ")
|
||||||
.appendValue(item.getExclusiveStartShardId());
|
.appendValue(item.getExclusiveStartShardId());
|
||||||
return false;
|
matches = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -562,49 +592,87 @@ public class KinesisProxyTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ListShardsRequestMatcher initialListShardsRequestMatcher() {
|
private static ListShardsRequestMatcher initialListShardsRequestMatcher() {
|
||||||
return new ListShardsRequestMatcher(null, null);
|
return ListShardsRequestMatcher.builder()
|
||||||
|
.streamName(TEST_STRING)
|
||||||
|
.streamArn(TEST_ARN)
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ListShardsRequestMatcher listShardsNextToken(final String nextToken) {
|
private static ListShardsRequestMatcher listShardsNextToken(final String nextToken) {
|
||||||
return new ListShardsRequestMatcher(null, nextToken);
|
return ListShardsRequestMatcher.builder()
|
||||||
|
.nextToken(nextToken)
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@AllArgsConstructor
|
@Builder
|
||||||
private static class ListShardsRequestMatcher extends TypeSafeDiagnosingMatcher<ListShardsRequest> {
|
private static class ListShardsRequestMatcher extends TypeSafeDiagnosingMatcher<ListShardsRequest> {
|
||||||
|
private final String streamName;
|
||||||
|
private final Arn streamArn;
|
||||||
private final String shardId;
|
private final String shardId;
|
||||||
private final String nextToken;
|
private final String nextToken;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean matchesSafely(final ListShardsRequest listShardsRequest, final Description description) {
|
protected boolean matchesSafely(final ListShardsRequest listShardsRequest, final Description description) {
|
||||||
|
boolean matches = true;
|
||||||
|
if (streamName == null) {
|
||||||
|
if (StringUtils.isNotEmpty(listShardsRequest.getStreamName())) {
|
||||||
|
description.appendText("Expected streamName to be null, but was ")
|
||||||
|
.appendValue(listShardsRequest.getStreamName());
|
||||||
|
matches = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!streamName.equals(listShardsRequest.getStreamName())) {
|
||||||
|
description.appendText("Expected streamName: ").appendValue(streamName)
|
||||||
|
.appendText(" doesn't match actual streamName: ")
|
||||||
|
.appendValue(listShardsRequest.getStreamName());
|
||||||
|
matches = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (streamArn == null) {
|
||||||
|
if (StringUtils.isNotEmpty(listShardsRequest.getStreamARN())) {
|
||||||
|
description.appendText("Expected streamArn to be null, but was ")
|
||||||
|
.appendValue(listShardsRequest.getStreamARN());
|
||||||
|
matches = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!streamArn.equals(Arn.fromString(listShardsRequest.getStreamARN()))) {
|
||||||
|
description.appendText("Expected streamArn: ").appendValue(streamArn)
|
||||||
|
.appendText(" doesn't match actual streamArn: ")
|
||||||
|
.appendValue(listShardsRequest.getStreamARN());
|
||||||
|
matches = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (shardId == null) {
|
if (shardId == null) {
|
||||||
if (StringUtils.isNotEmpty(listShardsRequest.getExclusiveStartShardId())) {
|
if (StringUtils.isNotEmpty(listShardsRequest.getExclusiveStartShardId())) {
|
||||||
description.appendText("Expected ExclusiveStartShardId to be null, but was ")
|
description.appendText("Expected ExclusiveStartShardId to be null, but was ")
|
||||||
.appendValue(listShardsRequest.getExclusiveStartShardId());
|
.appendValue(listShardsRequest.getExclusiveStartShardId());
|
||||||
return false;
|
matches = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!shardId.equals(listShardsRequest.getExclusiveStartShardId())) {
|
if (!shardId.equals(listShardsRequest.getExclusiveStartShardId())) {
|
||||||
description.appendText("Expected shardId: ").appendValue(shardId)
|
description.appendText("Expected shardId: ").appendValue(shardId)
|
||||||
.appendText(" doesn't match actual shardId: ")
|
.appendText(" doesn't match actual shardId: ")
|
||||||
.appendValue(listShardsRequest.getExclusiveStartShardId());
|
.appendValue(listShardsRequest.getExclusiveStartShardId());
|
||||||
return false;
|
matches = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StringUtils.isNotEmpty(listShardsRequest.getNextToken())) {
|
if (StringUtils.isNotEmpty(listShardsRequest.getNextToken())) {
|
||||||
if (StringUtils.isNotEmpty(listShardsRequest.getStreamName()) || StringUtils.isNotEmpty(listShardsRequest.getExclusiveStartShardId())) {
|
if (StringUtils.isNotEmpty(listShardsRequest.getStreamName()) || StringUtils.isNotEmpty(listShardsRequest.getExclusiveStartShardId())) {
|
||||||
return false;
|
matches = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!listShardsRequest.getNextToken().equals(nextToken)) {
|
if (!listShardsRequest.getNextToken().equals(nextToken)) {
|
||||||
description.appendText("Found nextToken: ").appendValue(listShardsRequest.getNextToken())
|
description.appendText("Found nextToken: ").appendValue(listShardsRequest.getNextToken())
|
||||||
.appendText(" when it was supposed to be null.");
|
.appendText(" when it was supposed to be null.");
|
||||||
return false;
|
matches = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return nextToken == null;
|
return nextToken == null;
|
||||||
}
|
}
|
||||||
return true;
|
return matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,7 @@ public class KinesisLocalFileDataCreator {
|
||||||
HashKeyRange hashKeyRange = new HashKeyRange();
|
HashKeyRange hashKeyRange = new HashKeyRange();
|
||||||
hashKeyRange.setStartingHashKey(hashKeyRangeStart.toString());
|
hashKeyRange.setStartingHashKey(hashKeyRangeStart.toString());
|
||||||
hashKeyRange.setEndingHashKey(hashKeyRangeEnd.toString());
|
hashKeyRange.setEndingHashKey(hashKeyRangeEnd.toString());
|
||||||
|
shard.setHashKeyRange(hashKeyRange);
|
||||||
shards.add(shard);
|
shards.add(shard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,18 @@ public class LeaseCleanupManagerTest {
|
||||||
leaseCleanupManager.start();
|
leaseCleanupManager.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests subsequent calls to shutdown {@link LeaseCleanupManager}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public final void testSubsequentShutdowns() {
|
||||||
|
leaseCleanupManager.start();
|
||||||
|
Assert.assertTrue(leaseCleanupManager.isRunning());
|
||||||
|
leaseCleanupManager.shutdown();
|
||||||
|
Assert.assertFalse(leaseCleanupManager.isRunning());
|
||||||
|
leaseCleanupManager.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that when both child shard leases are present, we are able to delete the parent shard for the completed
|
* Tests that when both child shard leases are present, we are able to delete the parent shard for the completed
|
||||||
* shard case.
|
* shard case.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue