├── .editorconfig
├── .github
├── CODEOWNERS
├── codecov.yml
├── dependabot.yml
├── labeler.yml
└── workflows
│ ├── build.yml
│ ├── dependabot.yml
│ ├── docs.yml
│ ├── labeler.yml
│ ├── release-auto.yml
│ ├── release.yml
│ └── upgrade-gradle-wrapper.yml
├── .gitignore
├── DEVELOPMENT.md
├── LICENSE
├── README.md
├── api
├── common
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ └── java
│ │ └── com
│ │ └── coditory
│ │ └── sherlock
│ │ ├── DistributedLockBuilder.java
│ │ ├── OwnerIdPolicy.java
│ │ ├── SherlockException.java
│ │ ├── connector
│ │ ├── AcquireResult.java
│ │ ├── AcquireResultWithValue.java
│ │ ├── InitializationResult.java
│ │ ├── LockResultLogger.java
│ │ └── ReleaseResult.java
│ │ └── migrator
│ │ └── MigrationResult.java
├── coroutines-connector
│ ├── build.gradle.kts
│ └── src
│ │ ├── main
│ │ └── kotlin
│ │ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── coroutines
│ │ │ ├── DelegatingDistributedLock.kt
│ │ │ ├── SherlockWithConnector.kt
│ │ │ ├── SherlockWithConnectorBuilder.kt
│ │ │ └── SuspendingDistributedLockConnector.kt
│ │ └── test
│ │ └── groovy
│ │ └── com
│ │ └── coditory
│ │ └── sherlock
│ │ └── coroutines
│ │ ├── InMemoryDistributedLockSpec.groovy
│ │ └── base
│ │ └── UsesKtSherlock.groovy
├── coroutines
│ ├── build.gradle.kts
│ └── src
│ │ ├── main
│ │ └── java
│ │ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── coroutines
│ │ │ ├── DistributedLock.kt
│ │ │ ├── Sherlock.kt
│ │ │ ├── migrator
│ │ │ ├── SherlockMigrator.kt
│ │ │ └── SherlockMigratorBuilder.kt
│ │ │ └── test
│ │ │ ├── DistributedLockMock.kt
│ │ │ └── SherlockStub.kt
│ │ └── test
│ │ ├── groovy
│ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── coroutines
│ │ │ └── MigratorSpec.groovy
│ │ └── kotlin
│ │ └── com
│ │ └── coditory
│ │ └── sherlock
│ │ └── coroutines
│ │ ├── DistributedLockMockSpec.kt
│ │ ├── DistributedLockSpec.kt
│ │ ├── SherlockStubSpec.kt
│ │ ├── SuspendedMigratorSpec.kt
│ │ └── base
│ │ ├── Assertions.kt
│ │ ├── BlockingKtMigrator.kt
│ │ ├── DynamicTest.kt
│ │ ├── TestTuple.kt
│ │ └── Tuple.kt
├── reactor
│ ├── build.gradle.kts
│ └── src
│ │ ├── integrationTest
│ │ └── groovy
│ │ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── reactor
│ │ │ ├── InMemoryDistributedLockSpec.groovy
│ │ │ └── base
│ │ │ └── UsesReactorSherlock.groovy
│ │ ├── main
│ │ └── java
│ │ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── reactor
│ │ │ ├── DelegatingDistributedLock.java
│ │ │ ├── DistributedLock.java
│ │ │ ├── DistributedLockConnector.java
│ │ │ ├── Sherlock.java
│ │ │ ├── SherlockWithConnector.java
│ │ │ ├── SherlockWithConnectorBuilder.java
│ │ │ ├── migrator
│ │ │ ├── SherlockMigrator.java
│ │ │ └── SherlockMigratorBuilder.java
│ │ │ └── test
│ │ │ ├── DistributedLockMock.java
│ │ │ └── SherlockStub.java
│ │ └── test
│ │ └── groovy
│ │ └── com
│ │ └── coditory
│ │ └── sherlock
│ │ └── reactor
│ │ ├── DistributedLockMockSpec.groovy
│ │ ├── DistributedLockSpec.groovy
│ │ ├── SherlockStubSpec.groovy
│ │ └── migrator
│ │ ├── BlockingReactorMigrator.groovy
│ │ ├── MigratorReactorSpec.groovy
│ │ └── MigratorSpec.groovy
├── rxjava
│ ├── build.gradle.kts
│ └── src
│ │ ├── integrationTest
│ │ └── groovy
│ │ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── rxjava
│ │ │ ├── InMemoryDistributedLockSpec.groovy
│ │ │ └── base
│ │ │ └── UsesRxSherlock.groovy
│ │ ├── main
│ │ └── java
│ │ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── rxjava
│ │ │ ├── DelegatingDistributedLock.java
│ │ │ ├── DistributedLock.java
│ │ │ ├── DistributedLockConnector.java
│ │ │ ├── Sherlock.java
│ │ │ ├── SherlockWithConnector.java
│ │ │ ├── SherlockWithConnectorBuilder.java
│ │ │ ├── migrator
│ │ │ ├── SherlockMigrator.java
│ │ │ └── SherlockMigratorBuilder.java
│ │ │ └── test
│ │ │ ├── DistributedLockMock.java
│ │ │ └── SherlockStub.java
│ │ └── test
│ │ └── groovy
│ │ └── com
│ │ └── coditory
│ │ └── sherlock
│ │ └── rxjava
│ │ ├── DistributedLockMockSpec.groovy
│ │ ├── DistributedLockSpec.groovy
│ │ ├── SherlockStubSpec.groovy
│ │ └── migrator
│ │ ├── BlockingRxMigrator.groovy
│ │ ├── MigratorRxJavaSpec.groovy
│ │ └── MigratorSpec.groovy
└── sync
│ ├── build.gradle.kts
│ └── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── coditory
│ │ └── sherlock
│ │ ├── DelegatingDistributedLock.java
│ │ ├── DistributedLock.java
│ │ ├── DistributedLockConnector.java
│ │ ├── Sherlock.java
│ │ ├── SherlockWithConnector.java
│ │ ├── SherlockWithConnectorBuilder.java
│ │ ├── migrator
│ │ ├── SherlockMigrator.java
│ │ └── SherlockMigratorBuilder.java
│ │ └── test
│ │ ├── DistributedLockMock.java
│ │ └── SherlockStub.java
│ └── test
│ └── groovy
│ └── com
│ └── coditory
│ └── sherlock
│ ├── DistributedLockMockSpec.groovy
│ ├── DistributedLockSpec.groovy
│ ├── SherlockStubSpec.groovy
│ └── migrator
│ ├── BlockingSyncMigrator.groovy
│ └── SherlockMigratorSpec.groovy
├── build-logic
├── build.gradle.kts
├── settings.gradle.kts
└── src
│ └── main
│ └── kotlin
│ ├── build.coverage-root.gradle.kts
│ ├── build.coverage.gradle.kts
│ ├── build.java.gradle.kts
│ ├── build.kotlin.gradle.kts
│ ├── build.publish-root.gradle.kts
│ ├── build.publish.gradle.kts
│ ├── build.test.gradle.kts
│ └── build.version.gradle.kts
├── build.gradle.kts
├── common
├── build.gradle.kts
└── src
│ └── main
│ └── java
│ └── com
│ └── coditory
│ └── sherlock
│ ├── LockRequest.java
│ ├── Ports.java
│ ├── Preconditions.java
│ ├── SherlockDefaults.java
│ ├── Timer.java
│ ├── UuidGenerator.java
│ └── migrator
│ ├── ChangeSet.java
│ ├── ChangeSetMethodExtractor.java
│ ├── MigrationChangeSet.java
│ └── MigrationChangeSetMethod.java
├── connectors
├── inmem
│ ├── common
│ │ ├── build.gradle.kts
│ │ └── src
│ │ │ └── main
│ │ │ └── java
│ │ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── inmem
│ │ │ ├── InMemoryDistributedLock.java
│ │ │ └── InMemoryDistributedLockStorage.java
│ ├── coroutines
│ │ ├── build.gradle.kts
│ │ └── src
│ │ │ ├── integrationTest
│ │ │ └── groovy
│ │ │ │ └── com
│ │ │ │ └── coditory
│ │ │ │ └── sherlock
│ │ │ │ └── inmem
│ │ │ │ └── coroutines
│ │ │ │ ├── InMemoryDistributedLockSpec.groovy
│ │ │ │ └── UsesKtInMemorySherlock.groovy
│ │ │ └── main
│ │ │ └── kotlin
│ │ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── inmem
│ │ │ └── coroutines
│ │ │ ├── InMemorySherlock.kt
│ │ │ └── KtInMemoryDistributedLockConnector.kt
│ ├── reactor
│ │ ├── build.gradle.kts
│ │ └── src
│ │ │ ├── integrationTest
│ │ │ └── groovy
│ │ │ │ └── com
│ │ │ │ └── coditory
│ │ │ │ └── sherlock
│ │ │ │ └── inmem
│ │ │ │ └── reactor
│ │ │ │ ├── InMemoryDistributedLockSpec.groovy
│ │ │ │ └── UsesReactorInMemorySherlock.groovy
│ │ │ └── main
│ │ │ └── java
│ │ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── inmem
│ │ │ └── reactor
│ │ │ ├── InMemorySherlock.java
│ │ │ └── ReactorInMemoryDistributedLockConnector.java
│ ├── rxjava
│ │ ├── build.gradle.kts
│ │ └── src
│ │ │ ├── integrationTest
│ │ │ └── groovy
│ │ │ │ └── com
│ │ │ │ └── coditory
│ │ │ │ └── sherlock
│ │ │ │ └── inmem
│ │ │ │ └── rxjava
│ │ │ │ ├── InMemoryDistributedLockSpec.groovy
│ │ │ │ └── UsesReactiveInMemorySherlock.groovy
│ │ │ └── main
│ │ │ └── java
│ │ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── inmem
│ │ │ └── rxjava
│ │ │ ├── InMemoryDistributedLockConnector.java
│ │ │ └── InMemorySherlock.java
│ └── sync
│ │ ├── build.gradle.kts
│ │ └── src
│ │ ├── integrationTest
│ │ └── groovy
│ │ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── inmem
│ │ │ ├── InMemoryDistributedLockSpec.groovy
│ │ │ └── UsesInMemorySherlock.groovy
│ │ └── main
│ │ └── java
│ │ └── com
│ │ └── coditory
│ │ └── sherlock
│ │ └── inmem
│ │ ├── InMemoryDistributedLockConnector.java
│ │ └── InMemorySherlock.java
├── mongo
│ ├── common
│ │ ├── build.gradle.kts
│ │ └── src
│ │ │ └── main
│ │ │ └── java
│ │ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── mongo
│ │ │ ├── MongoDistributedLock.java
│ │ │ └── MongoDistributedLockQueries.java
│ ├── coroutines
│ │ ├── build.gradle.kts
│ │ └── src
│ │ │ ├── integrationTest
│ │ │ └── groovy
│ │ │ │ └── com
│ │ │ │ └── coditory
│ │ │ │ └── sherlock
│ │ │ │ └── mongo
│ │ │ │ └── coroutines
│ │ │ │ ├── MongoClientHolder.groovy
│ │ │ │ ├── MongoDbSpec.groovy
│ │ │ │ ├── MongoDistributedLockSpec.groovy
│ │ │ │ └── UsesKtMongoSherlock.groovy
│ │ │ └── main
│ │ │ └── kotlin
│ │ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── mongo
│ │ │ └── coroutines
│ │ │ ├── MongoCollectionInitializer.kt
│ │ │ ├── MongoDistributedLockConnector.kt
│ │ │ ├── MongoOperations.kt
│ │ │ └── MongoSherlock.kt
│ ├── reactor
│ │ ├── build.gradle.kts
│ │ └── src
│ │ │ ├── integrationTest
│ │ │ └── groovy
│ │ │ │ └── com
│ │ │ │ └── coditory
│ │ │ │ └── sherlock
│ │ │ │ └── mongo
│ │ │ │ └── reactor
│ │ │ │ ├── MongoClientHolder.groovy
│ │ │ │ ├── MongoDbSpec.groovy
│ │ │ │ ├── MongoDistributedLockSpec.groovy
│ │ │ │ └── UsesReactorMongoSherlock.groovy
│ │ │ └── main
│ │ │ └── java
│ │ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── mongo
│ │ │ └── reactor
│ │ │ ├── MongoCollectionInitializer.java
│ │ │ ├── MongoDistributedLockConnector.java
│ │ │ └── MongoSherlock.java
│ ├── rxjava
│ │ ├── build.gradle.kts
│ │ └── src
│ │ │ ├── integrationTest
│ │ │ └── groovy
│ │ │ │ └── com
│ │ │ │ └── coditory
│ │ │ │ └── sherlock
│ │ │ │ └── mongo
│ │ │ │ └── rxjava
│ │ │ │ ├── MongoClientHolder.groovy
│ │ │ │ ├── MongoDbSpec.groovy
│ │ │ │ ├── MongoDistributedLockSpec.groovy
│ │ │ │ └── UsesRxMongoSherlock.groovy
│ │ │ └── main
│ │ │ └── java
│ │ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── mongo
│ │ │ └── rxjava
│ │ │ ├── MongoCollectionInitializer.java
│ │ │ ├── MongoDistributedLockConnector.java
│ │ │ └── MongoSherlock.java
│ ├── sync
│ │ ├── build.gradle.kts
│ │ └── src
│ │ │ ├── integrationTest
│ │ │ └── groovy
│ │ │ │ └── com
│ │ │ │ └── coditory
│ │ │ │ └── sherlock
│ │ │ │ └── mongo
│ │ │ │ ├── MongoDbSpec.groovy
│ │ │ │ ├── MongoDistributedLockSpec.groovy
│ │ │ │ └── UsesMongoSherlock.groovy
│ │ │ └── main
│ │ │ └── java
│ │ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── mongo
│ │ │ ├── MongoCollectionInitializer.java
│ │ │ ├── MongoDistributedLockConnector.java
│ │ │ └── MongoSherlock.java
│ └── tests
│ │ ├── build.gradle.kts
│ │ └── src
│ │ └── main
│ │ └── groovy
│ │ └── com
│ │ └── coditory
│ │ └── sherlock
│ │ └── mongo
│ │ ├── MongoHolder.groovy
│ │ ├── MongoIndexCreationSpec.groovy
│ │ └── MongoLockStorageSpec.groovy
└── sql
│ ├── common-api
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ └── java
│ │ └── com
│ │ └── coditory
│ │ └── sherlock
│ │ └── sql
│ │ ├── BindingMapper.java
│ │ ├── BindingParameter.java
│ │ └── BindingParameterMapping.java
│ ├── common
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ └── java
│ │ └── com
│ │ └── coditory
│ │ └── sherlock
│ │ └── sql
│ │ ├── PrecomputedBindingParameterMapper.java
│ │ ├── SqlLockNamedQueriesTemplate.java
│ │ └── SqlLockQueries.java
│ ├── coroutines
│ ├── build.gradle.kts
│ └── src
│ │ ├── integrationTest
│ │ └── groovy
│ │ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── sql
│ │ │ └── coroutines
│ │ │ ├── MySqlConnectionPoolHolder.groovy
│ │ │ ├── MySqlDbSpec.groovy
│ │ │ ├── MySqlDistributedLockSpec.groovy
│ │ │ ├── PostgresConnectionPoolHolder.groovy
│ │ │ ├── PostgresDbSpec.groovy
│ │ │ ├── PostgresDistributedLockSpec.groovy
│ │ │ ├── SqlConnectionProvider.groovy
│ │ │ └── UsesKtSqlSherlock.groovy
│ │ └── main
│ │ └── kotlin
│ │ └── com
│ │ └── coditory
│ │ └── sherlock
│ │ └── sql
│ │ └── coroutines
│ │ ├── ConnectionExt.kt
│ │ ├── SqlDistributedLockConnector.kt
│ │ ├── SqlSherlock.kt
│ │ ├── SqlStatementBinder.kt
│ │ └── SqlTableInitializer.kt
│ ├── reactor
│ ├── build.gradle.kts
│ └── src
│ │ ├── integrationTest
│ │ └── groovy
│ │ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── sql
│ │ │ └── reactor
│ │ │ ├── MySqlConnectionPoolHolder.groovy
│ │ │ ├── MySqlDbSpec.groovy
│ │ │ ├── MySqlDistributedLockSpec.groovy
│ │ │ ├── PostgresConnectionPoolHolder.groovy
│ │ │ ├── PostgresDbSpec.groovy
│ │ │ ├── PostgresDistributedLockSpec.groovy
│ │ │ ├── SqlConnectionProvider.groovy
│ │ │ └── UsesReactorSqlSherlock.groovy
│ │ └── main
│ │ └── java
│ │ └── com
│ │ └── coditory
│ │ └── sherlock
│ │ └── sql
│ │ └── reactor
│ │ ├── SqlDistributedLockConnector.java
│ │ ├── SqlSherlock.java
│ │ ├── SqlStatementBinder.java
│ │ └── SqlTableInitializer.java
│ ├── rxjava
│ ├── build.gradle.kts
│ └── src
│ │ ├── integrationTest
│ │ └── groovy
│ │ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── sql
│ │ │ └── rxjava
│ │ │ ├── MySqlConnectionPoolHolder.groovy
│ │ │ ├── MySqlDbSpec.groovy
│ │ │ ├── MySqlDistributedLockSpec.groovy
│ │ │ ├── PostgresConnectionPoolHolder.groovy
│ │ │ ├── PostgresDbSpec.groovy
│ │ │ ├── PostgresDistributedLockSpec.groovy
│ │ │ ├── SqlConnectioProvider.groovy
│ │ │ └── UsesRxSqlSherlock.groovy
│ │ └── main
│ │ └── java
│ │ └── com
│ │ └── coditory
│ │ └── sherlock
│ │ └── sql
│ │ └── rxjava
│ │ ├── SqlDistributedLockConnector.java
│ │ ├── SqlSherlock.java
│ │ ├── SqlStatementBinder.java
│ │ └── SqlTableInitializer.java
│ ├── sync
│ ├── build.gradle.kts
│ └── src
│ │ ├── integrationTest
│ │ └── groovy
│ │ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── sql
│ │ │ ├── MySqlDbSpec.groovy
│ │ │ ├── MySqlDistributedLockSpec.groovy
│ │ │ ├── PostgresDbSpec.groovy
│ │ │ ├── PostgresDistributedLockSpec.groovy
│ │ │ ├── SqlConnectioProvider.groovy
│ │ │ └── UsesSqlSherlock.groovy
│ │ └── main
│ │ └── java
│ │ └── com
│ │ └── coditory
│ │ └── sherlock
│ │ └── sql
│ │ ├── SqlDistributedLockConnector.java
│ │ ├── SqlSherlock.java
│ │ └── SqlTableInitializer.java
│ └── tests
│ ├── build.gradle.kts
│ └── src
│ └── main
│ └── groovy
│ └── com
│ └── coditory
│ └── sherlock
│ └── sql
│ ├── DataSourceConfigurer.groovy
│ ├── MySqlHolder.groovy
│ ├── PostgresHolder.groovy
│ ├── SqlDistributedLocksCreator.groovy
│ ├── SqlIndexCreationSpec.groovy
│ ├── SqlLockCommitSpec.groovy
│ ├── SqlLockStorageSpec.groovy
│ └── SqlTableIndexes.groovy
├── docs
├── mkdocs.yml
├── requirements.txt
├── site
│ ├── about.md
│ ├── assets
│ │ └── img
│ │ │ ├── favicon.png
│ │ │ ├── icon.png
│ │ │ └── logo.png
│ ├── connectors
│ │ ├── index.md
│ │ ├── inmem.md
│ │ ├── mongo.md
│ │ └── sql.md
│ ├── index.md
│ ├── locks.md
│ ├── migrator.md
│ └── testing.md
└── theme
│ └── main.html
├── examples
├── inmem-coroutines
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ ├── kotlin
│ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── samples
│ │ │ └── inmem
│ │ │ └── coroutines
│ │ │ ├── InMemKtAnnotatedMigrationSample.kt
│ │ │ ├── InMemKtLockSample.kt
│ │ │ └── InMemKtMigrationSample.kt
│ │ └── resources
│ │ └── logback.xml
├── inmem-reactor
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── samples
│ │ │ └── inmem
│ │ │ └── reactor
│ │ │ ├── InMemReactorAnnotatedMigrationSample.java
│ │ │ ├── InMemReactorLockSample.java
│ │ │ └── InMemReactorMigrationSample.java
│ │ └── resources
│ │ └── logback.xml
├── inmem-rxjava
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── samples
│ │ │ └── inmem
│ │ │ └── rxjava
│ │ │ ├── InMemRxAnnotatedMigrationSample.java
│ │ │ ├── InMemRxLockSample.java
│ │ │ └── InMemRxMigrationSample.java
│ │ └── resources
│ │ └── logback.xml
├── inmem-sync
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── samples
│ │ │ └── inmem
│ │ │ └── sync
│ │ │ ├── InMemSyncAnnotatedMigrationSample.java
│ │ │ ├── InMemSyncLockSample.java
│ │ │ └── InMemSyncMigrationSample.java
│ │ └── resources
│ │ └── logback.xml
├── mongo-coroutines
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ ├── kotlin
│ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── samples
│ │ │ └── mongo
│ │ │ └── coroutines
│ │ │ ├── MongoKtAnnotatedMigrationSample.kt
│ │ │ ├── MongoKtLockSample.kt
│ │ │ └── MongoKtMigrationSample.kt
│ │ └── resources
│ │ └── logback.xml
├── mongo-reactor
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── samples
│ │ │ └── mongo
│ │ │ └── reactor
│ │ │ ├── MongoReactorAnnotatedMigrationSample.java
│ │ │ ├── MongoReactorLockSample.java
│ │ │ └── MongoReactorMigrationSample.java
│ │ └── resources
│ │ └── logback.xml
├── mongo-rxjava
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── samples
│ │ │ └── mongo
│ │ │ └── rxjava
│ │ │ ├── MongoRxAnnotatedMigrationSample.java
│ │ │ ├── MongoRxLockSample.java
│ │ │ └── MongoRxMigrationSample.java
│ │ └── resources
│ │ └── logback.xml
├── mongo-sync
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── samples
│ │ │ └── mongo
│ │ │ └── sync
│ │ │ ├── MongoSyncAnnotatedMigrationSample.java
│ │ │ ├── MongoSyncLockSample.java
│ │ │ └── MongoSyncMigrationSample.java
│ │ └── resources
│ │ └── logback.xml
├── mysql-coroutines
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ ├── kotlin
│ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── samples
│ │ │ └── mysql
│ │ │ └── coroutines
│ │ │ ├── MySqlKtAnnotatedMigrationSample.kt
│ │ │ ├── MySqlKtLockSample.kt
│ │ │ └── MySqlKtMigrationSample.kt
│ │ └── resources
│ │ └── logback.xml
├── mysql-reactor
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── samples
│ │ │ └── mysql
│ │ │ └── reactor
│ │ │ ├── MySqlReactorAnnotatedMigrationSample.java
│ │ │ ├── MySqlReactorLockSample.java
│ │ │ └── MySqlReactorMigrationSample.java
│ │ └── resources
│ │ └── logback.xml
├── mysql-rxjava
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── samples
│ │ │ └── mysql
│ │ │ └── rxjava
│ │ │ ├── MySqlRxAnnotatedMigrationSample.java
│ │ │ ├── MySqlRxLockSample.java
│ │ │ └── MySqlRxMigrationSample.java
│ │ └── resources
│ │ └── logback.xml
├── mysql-sync
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── samples
│ │ │ └── mysql
│ │ │ └── sync
│ │ │ ├── MySqlSyncAnnotatedMigrationSample.java
│ │ │ ├── MySqlSyncLockSample.java
│ │ │ └── MySqlSyncMigrationSample.java
│ │ └── resources
│ │ └── logback.xml
├── postgres-coroutines
│ ├── build.gradle.kts
│ ├── readme.md
│ └── src
│ │ └── main
│ │ ├── kotlin
│ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── samples
│ │ │ └── postgres
│ │ │ └── coroutines
│ │ │ ├── PostgresKtAnnotatedMigrationSample.kt
│ │ │ ├── PostgresKtLockSample.kt
│ │ │ └── PostgresKtMigrationSample.kt
│ │ └── resources
│ │ └── logback.xml
├── postgres-reactor
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── samples
│ │ │ └── postgres
│ │ │ └── reactor
│ │ │ ├── PostgresReactorAnnotatedMigrationSample.java
│ │ │ ├── PostgresReactorLockSample.java
│ │ │ └── PostgresReactorMigrationSample.java
│ │ └── resources
│ │ └── logback.xml
├── postgres-rxjava
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── coditory
│ │ │ └── sherlock
│ │ │ └── samples
│ │ │ └── postgres
│ │ │ └── rxjava
│ │ │ ├── PostgresRxAnnotatedMigrationSample.java
│ │ │ ├── PostgresRxLockSample.java
│ │ │ └── PostgresRxMigrationSample.java
│ │ └── resources
│ │ └── logback.xml
└── postgres-sync
│ ├── build.gradle.kts
│ └── src
│ └── main
│ ├── java
│ └── com
│ │ └── coditory
│ │ └── sherlock
│ │ └── samples
│ │ └── postgres
│ │ └── sync
│ │ ├── PostgresSyncAnnotatedMigrationSample.java
│ │ ├── PostgresSyncLockSample.java
│ │ └── PostgresSyncMigrationSample.java
│ └── resources
│ └── logback.xml
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── scripts
├── docker
│ ├── README.md
│ └── docker-compose.yml
└── git
│ └── pre-commit
├── settings.gradle.kts
└── tests
├── build.gradle.kts
└── src
└── main
├── groovy
└── com
│ └── coditory
│ └── sherlock
│ ├── AcquireLockMultipleTimesSpec.groovy
│ ├── AcquireLockSpec.groovy
│ ├── BlockingReactorSherlockWrapper.groovy
│ ├── BlockingRxSherlockWrapper.groovy
│ ├── HandleDbFailureSpec.groovy
│ ├── InfiniteAcquireLockSpec.groovy
│ ├── LocksBaseSpec.groovy
│ ├── PublisherSubscriber.groovy
│ ├── ReleaseLockSpec.groovy
│ ├── base
│ ├── DatabaseManager.groovy
│ ├── DistributedLocksCreator.groovy
│ ├── JsonAssert.groovy
│ ├── LockAssertions.groovy
│ ├── LockTypes.groovy
│ ├── SpecSimulatedException.groovy
│ └── UpdatableFixedClock.groovy
│ └── migrator
│ ├── MigratorBaseSpec.groovy
│ ├── MigratorChangeSetsSpec.groovy
│ ├── MigratorSpec.groovy
│ └── base
│ ├── BlockingMigrator.groovy
│ └── MigratorCreator.groovy
├── kotlin
└── com
│ └── coditory
│ └── sherlock
│ ├── BlockingKtDistributedLock.kt
│ └── BlockingKtSherlockWrapper.kt
└── resources
└── logback.xml
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | end_of_line = lf
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 |
10 | [*.{java,groovy}]
11 | indent_size = 4
12 |
13 | [*.{kt,kts}]
14 | indent_size = 4
15 | ktlint_code_style = intellij_idea
16 | ktlint_function_signature_body_expression_wrapping = default
17 | ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = unset
18 | ktlint_ignore_back_ticked_identifier = true
19 | ktlint_standard_class-signature = disabled
20 | ktlint_standard_function-expression-body = disabled
21 | ktlint_standard_no-wildcard-imports = enabled
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @coditory/reviewers
2 |
3 | # Exclusions so changes made by dependabot and other bots could be auto reviewed by Coditory App.
4 | # Currently App cannot be a part of a team or CODEOWNERS file.
5 | # https://github.com/orgs/community/discussions/23064
6 | .github/workflows/*
7 | gradle/**
8 | **/build.gradle.kts
9 | **/settings.gradle.kts
10 |
--------------------------------------------------------------------------------
/.github/codecov.yml:
--------------------------------------------------------------------------------
1 | coverage:
2 | status:
3 | patch: off
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "github-actions"
4 | directory: "/"
5 | schedule:
6 | interval: "daily"
7 | groups:
8 | # merged and released instantly
9 | sec-updates:
10 | applies-to: security-updates
11 | patterns:
12 | - "*"
13 | # merged automatically
14 | dev-dependencies:
15 | patterns:
16 | - "*"
17 |
18 | - package-ecosystem: "gradle"
19 | directory: "/"
20 | schedule:
21 | interval: "daily"
22 | groups:
23 | # merged and released instantly
24 | sec-updates:
25 | applies-to: security-updates
26 | patterns:
27 | - "*"
28 | # merged automatically
29 | dev-dependencies:
30 | patterns:
31 | # gradle plugins
32 | - "*ktlint-gradle"
33 | - "*jacoco"
34 | - "*publish-plugin"
35 | # test dependencies
36 | - "org.junit*"
37 | - "org.spockframework*"
38 | - "org.awaitility*"
39 | - "*-test"
40 | - "*assert"
41 | - "*hikaricp"
42 | - "org.testcontainers*"
43 | # merged and released automatically
44 | prod-dependencies:
45 | update-types:
46 | - "patch"
47 | - "minor"
48 | # requires human approval and has higher chance to fail build
49 | prod-dependencies-major:
50 | update-types:
51 | - "major"
52 |
--------------------------------------------------------------------------------
/.github/labeler.yml:
--------------------------------------------------------------------------------
1 | backport:
2 | - head-branch: [ '^v[0-9]+\.x\.x' ]
3 |
4 | code:
5 | - changed-files:
6 | - any-glob-to-any-file:
7 | - "src/**"
8 |
9 | build:
10 | - changed-files:
11 | - any-glob-to-any-file:
12 | - "**/*.gradle*"
13 |
14 | ci:
15 | - changed-files:
16 | - any-glob-to-any-file:
17 | - ".github/**"
18 |
19 | documentation:
20 | - changed-files:
21 | - any-glob-to-any-file:
22 | - "**/*.md"
23 | - "docs/**"
24 |
25 | license:
26 | - changed-files:
27 | - any-glob-to-any-file:
28 | - "LICENSE"
29 |
30 | gradle:
31 | - changed-files:
32 | - any-glob-to-any-file:
33 | - "gradlew*"
34 | - ".gradle/**"
35 | - "gradle/**"
36 | - "setting.gradle*"
37 |
38 | gitignore:
39 | - changed-files:
40 | - any-glob-to-any-file:
41 | - ".gitignore"
42 |
43 | codestyle:
44 | - changed-files:
45 | - any-glob-to-any-file:
46 | - ".editorconfig"
47 | - ".idea/codeStyles/**"
48 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | workflow_dispatch:
5 | pull_request:
6 | push:
7 | branches-ignore:
8 | - 'gh-pages'
9 |
10 | jobs:
11 | build:
12 | uses: coditory/jvm-workflows/.github/workflows/build.yml@v1
13 | # Required to pass codecov token
14 | secrets: inherit
15 | with:
16 | java-version: 21
17 | build-command: ./gradlew build coverage
18 |
--------------------------------------------------------------------------------
/.github/workflows/dependabot.yml:
--------------------------------------------------------------------------------
1 | name: "Dependabot"
2 |
3 | on: pull_request_target
4 |
5 | jobs:
6 | dependabot:
7 | uses: coditory/workflows/.github/workflows/dependabot.yml@v1
8 | secrets: inherit
9 |
--------------------------------------------------------------------------------
/.github/workflows/docs.yml:
--------------------------------------------------------------------------------
1 | name: Docs
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches: [main]
7 | paths:
8 | - "docs/**"
9 |
10 | jobs:
11 | docs:
12 | uses: coditory/workflows/.github/workflows/docs.yml@v1
13 | secrets: inherit
14 |
--------------------------------------------------------------------------------
/.github/workflows/labeler.yml:
--------------------------------------------------------------------------------
1 | name: "Labeler"
2 |
3 | on: pull_request_target
4 |
5 | jobs:
6 | labeler:
7 | uses: coditory/workflows/.github/workflows/labeler.yml@v1
8 | secrets: inherit
9 |
--------------------------------------------------------------------------------
/.github/workflows/release-auto.yml:
--------------------------------------------------------------------------------
1 | name: Auto Release
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | security-updates-only:
7 | description: "Security updates only"
8 | type: boolean
9 | required: false
10 | default: false
11 | consider-snapshot:
12 | description: "Consider snapshot"
13 | type: boolean
14 | required: false
15 | default: false
16 | workflow_run:
17 | workflows: ["Build"]
18 | types: [completed]
19 | branches:
20 | - main
21 | - v*x.x
22 | schedule:
23 | # at 5:30 UTC every other month
24 | - cron: "30 5 1 */2 *"
25 |
26 | jobs:
27 | check:
28 | uses: coditory/workflows/.github/workflows/release-check.yml@v1
29 | secrets: inherit
30 | if: |
31 | github.event_name != 'workflow_run'
32 | || !contains(github.event.workflow_run.head_commit.message, '[ci-skip-build]')
33 | with:
34 | security-updates-only: ${{ inputs.security-updates-only || github.event_name == 'workflow_run' }}
35 |
36 | release:
37 | uses: ./.github/workflows/release.yml
38 | secrets: inherit
39 | needs: check
40 | if: needs.check.outputs.release == 'true'
41 |
42 | snapshot:
43 | uses: ./.github/workflows/release.yml
44 | secrets: inherit
45 | needs: check
46 | if: |
47 | (inputs.consider-snapshot || github.event_name == 'workflow_run')
48 | && needs.check.outputs.release != 'true'
49 | && needs.check.outputs.skip-code != 'no-changes'
50 | with:
51 | snapshot: true
52 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | branch:
7 | type: string
8 | description: Branch name to release
9 | required: true
10 | default: main
11 | section:
12 | type: choice
13 | description: Version section to increment
14 | options:
15 | - patch
16 | - minor
17 | - major
18 | required: true
19 | default: patch
20 | version:
21 | type: string
22 | description: ...or manually define version like 1.2.3, 1.2.3-suffix
23 | required: false
24 | snapshot:
25 | type: boolean
26 | description: Snapshot version
27 | required: true
28 | default: false
29 | # Called from release-auto
30 | workflow_call:
31 | inputs:
32 | branch:
33 | type: string
34 | required: false
35 | default: main
36 | section:
37 | type: string
38 | required: false
39 | default: patch
40 | version:
41 | type: string
42 | required: false
43 | snapshot:
44 | type: boolean
45 | required: false
46 | default: false
47 |
48 | jobs:
49 | release:
50 | uses: coditory/workflows/.github/workflows/release.yml@v1
51 | secrets: inherit
52 | with:
53 | branch: ${{ inputs.branch }}
54 | section: ${{ inputs.section }}
55 | version: ${{ inputs.version }}
56 | snapshot: ${{ inputs.snapshot }}
57 | java-version: 21
58 | release-command: ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository -Pversion=$NEXT_VERSION
59 | snapshot-command: ./gradlew publishToSonatype -Pversion=$NEXT_VERSION
60 | version-command: ./gradlew version --quiet --no-scan
61 |
--------------------------------------------------------------------------------
/.github/workflows/upgrade-gradle-wrapper.yml:
--------------------------------------------------------------------------------
1 | name: Upgrade Gradle Wrapper
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | # at 5:30 UTC every month
7 | - cron: "30 5 1 * *"
8 |
9 | jobs:
10 | upgrade-gradle-wrapper:
11 | uses: coditory/jvm-workflows/.github/workflows/upgrade-gradle-wrapper.yml@v1
12 | secrets: inherit
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Gradle
2 | .log
3 | .gradle
4 | !gradle-wrapper.jar
5 | build
6 | .kotlin
7 |
8 | # IntelliJ
9 | *.iml
10 | .idea
11 | !.idea/codeStyles
12 |
13 | # Docker
14 | scripts/docker/**/data
15 | scripts/docker/**/data+bak
16 |
--------------------------------------------------------------------------------
/api/common/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | id("build.publish")
4 | id("build.coverage")
5 | }
6 |
7 | dependencies {
8 | api(projects.common)
9 | }
10 |
--------------------------------------------------------------------------------
/api/common/src/main/java/com/coditory/sherlock/OwnerIdPolicy.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 |
5 | import static com.coditory.sherlock.Preconditions.expectNonEmpty;
6 |
7 | /**
8 | * Provides ownerId for locks.
9 | * It's executed once for every lock, during lock creation.
10 | *
11 | * If two JVM processes use the same ownerId, they are treated by Sherlock as the same owner.
12 | */
13 | public interface OwnerIdPolicy {
14 | @NotNull
15 | static OwnerIdPolicy defaultOwnerIdPolicy() {
16 | return OwnerIdPolicy.uniqueOwnerId();
17 | }
18 |
19 | @NotNull
20 | static OwnerIdPolicy staticOwnerId(@NotNull String ownerId) {
21 | expectNonEmpty(ownerId, "ownerId");
22 | return new StaticOwnerIdPolicy(ownerId);
23 | }
24 |
25 | @NotNull
26 | static OwnerIdPolicy uniqueOwnerId() {
27 | return StaticOwnerIdPolicy.RANDOM_OWNER_ID_POLICY;
28 | }
29 |
30 | @NotNull
31 | static OwnerIdPolicy staticUniqueOwnerId() {
32 | return StaticOwnerIdPolicy.RANDOM_STATIC_OWNER_ID_POLICY;
33 | }
34 |
35 | @NotNull
36 | String getOwnerId();
37 | }
38 |
39 | class StaticOwnerIdPolicy implements OwnerIdPolicy {
40 | static final OwnerIdPolicy RANDOM_OWNER_ID_POLICY = UuidGenerator::uuid;
41 | static final OwnerIdPolicy RANDOM_STATIC_OWNER_ID_POLICY = new StaticOwnerIdPolicy(UuidGenerator.uuid());
42 |
43 | private final String ownerId;
44 |
45 | StaticOwnerIdPolicy(@NotNull String ownerId) {
46 | expectNonEmpty(ownerId, "ownerId");
47 | this.ownerId = ownerId;
48 | }
49 |
50 | @Override
51 | @NotNull
52 | public String getOwnerId() {
53 | return ownerId;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/api/common/src/main/java/com/coditory/sherlock/SherlockException.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock;
2 |
3 | public class SherlockException extends RuntimeException {
4 | public SherlockException(String message) {
5 | super(message);
6 | }
7 |
8 | public SherlockException(String message, Throwable cause) {
9 | super(message, cause);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/api/common/src/main/java/com/coditory/sherlock/connector/InitializationResult.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.connector;
2 |
3 | import java.util.Objects;
4 |
5 | public final class InitializationResult {
6 | public static InitializationResult of(boolean initialized) {
7 | return initialized ? INITIALIZED : SKIPPED;
8 | }
9 |
10 | public static InitializationResult initializedResult() {
11 | return INITIALIZED;
12 | }
13 |
14 | public static InitializationResult skippedResult() {
15 | return SKIPPED;
16 | }
17 |
18 | private static final InitializationResult INITIALIZED = new InitializationResult(true);
19 | private static final InitializationResult SKIPPED = new InitializationResult(false);
20 |
21 | private final boolean initialized;
22 |
23 | private InitializationResult(boolean initialized) {
24 | this.initialized = initialized;
25 | }
26 |
27 | public boolean initialized() {
28 | return initialized;
29 | }
30 |
31 | public InitializationResult onSkipped(Runnable action) {
32 | if (!initialized) {
33 | action.run();
34 | }
35 | return this;
36 | }
37 |
38 | public InitializationResult onInitialized(Runnable action) {
39 | if (initialized) {
40 | action.run();
41 | }
42 | return this;
43 | }
44 |
45 | @Override
46 | public boolean equals(Object o) {
47 | if (this == o) return true;
48 | if (o == null || getClass() != o.getClass()) return false;
49 | InitializationResult that = (InitializationResult) o;
50 | return initialized == that.initialized;
51 | }
52 |
53 | @Override
54 | public int hashCode() {
55 | return Objects.hash(initialized);
56 | }
57 |
58 | @Override
59 | public String toString() {
60 | return "InitializationResult{" +
61 | "initialized=" + initialized +
62 | '}';
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/api/common/src/main/java/com/coditory/sherlock/connector/LockResultLogger.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.connector;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import static com.coditory.sherlock.Preconditions.expectNonNull;
8 |
9 | public final class LockResultLogger {
10 | private final String lockId;
11 | private final Logger logger;
12 |
13 | public LockResultLogger(@NotNull String lockId, @NotNull Class> lockType) {
14 | expectNonNull(lockId, "lockId");
15 | expectNonNull(lockType, "lockType");
16 | this.lockId = lockId;
17 | this.logger = LoggerFactory.getLogger(lockType);
18 | }
19 |
20 | public void logResult(@NotNull AcquireResult result) {
21 | expectNonNull(result, "result");
22 | if (result.acquired()) {
23 | logger.debug("Lock acquired: {}", lockId);
24 | } else {
25 | logger.debug("Lock not acquired: {}", lockId);
26 | }
27 | }
28 |
29 | public void logResult(@NotNull ReleaseResult result) {
30 | expectNonNull(result, "result");
31 | if (result.released()) {
32 | logger.debug("Lock released: {}", lockId);
33 | } else {
34 | logger.debug("Lock not released: {}", lockId);
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/api/common/src/main/java/com/coditory/sherlock/connector/ReleaseResult.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.connector;
2 |
3 | import java.util.Objects;
4 |
5 | public final class ReleaseResult {
6 | public static ReleaseResult of(boolean released) {
7 | return released ? RELEASED : SKIPPED;
8 | }
9 |
10 | public static ReleaseResult releasedResult() {
11 | return RELEASED;
12 | }
13 |
14 | public static ReleaseResult skippedResult() {
15 | return SKIPPED;
16 | }
17 |
18 | private static final ReleaseResult RELEASED = new ReleaseResult(true);
19 | private static final ReleaseResult SKIPPED = new ReleaseResult(false);
20 |
21 | private final boolean released;
22 |
23 | private ReleaseResult(boolean released) {
24 | this.released = released;
25 | }
26 |
27 | public boolean released() {
28 | return released;
29 | }
30 |
31 | public ReleaseResult onSkipped(Runnable action) {
32 | if (!released) {
33 | action.run();
34 | }
35 | return this;
36 | }
37 |
38 | public ReleaseResult onReleased(Runnable action) {
39 | if (released) {
40 | action.run();
41 | }
42 | return this;
43 | }
44 |
45 | @Override
46 | public boolean equals(Object o) {
47 | if (this == o) return true;
48 | if (o == null || getClass() != o.getClass()) return false;
49 | ReleaseResult that = (ReleaseResult) o;
50 | return released == that.released;
51 | }
52 |
53 | @Override
54 | public int hashCode() {
55 | return Objects.hash(released);
56 | }
57 |
58 | @Override
59 | public String toString() {
60 | return "ReleaseResult{" +
61 | "released=" + released +
62 | '}';
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/api/coroutines-connector/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.kotlin")
3 | id("build.publish")
4 | id("build.coverage")
5 | }
6 |
7 | dependencies {
8 | api(projects.api.apiCommon)
9 | api(projects.api.apiCoroutines)
10 | implementation(projects.common)
11 | implementation(libs.kotlinx.coroutines.core)
12 | implementation(libs.kotlinx.coroutines.reactive)
13 | testImplementation(projects.connectors.inmem.inmemCoroutines)
14 | testImplementation(projects.tests)
15 | }
16 |
--------------------------------------------------------------------------------
/api/coroutines-connector/src/main/kotlin/com/coditory/sherlock/coroutines/SuspendingDistributedLockConnector.kt:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.coroutines
2 |
3 | import com.coditory.sherlock.LockRequest
4 |
5 | interface SuspendingDistributedLockConnector {
6 | /**
7 | * Initializes underlying infrastructure for locks.
8 | * Most frequently triggers database table creation and index creation.
9 | *
10 | *
11 | * If it is not executed explicitly, connector may execute it during first acquire acquisition or
12 | * release.
13 | */
14 | suspend fun initialize()
15 |
16 | /**
17 | * Acquire a lock.
18 | */
19 | suspend fun acquire(lockRequest: LockRequest): Boolean
20 |
21 | /**
22 | * Acquire a lock or prolong it if it was acquired by the same instance.
23 | */
24 | suspend fun acquireOrProlong(lockRequest: LockRequest): Boolean
25 |
26 | /**
27 | * Acquire a lock even if it was already acquired by someone else
28 | */
29 | suspend fun forceAcquire(lockRequest: LockRequest): Boolean
30 |
31 | /**
32 | * Unlock a lock if wat acquired by the same instance.
33 | */
34 | suspend fun release(lockId: String, ownerId: String): Boolean
35 |
36 | /**
37 | * Release a lock without checking its owner or release date.
38 | */
39 | suspend fun forceRelease(lockId: String): Boolean
40 |
41 | /**
42 | * Release all locks without checking their owners or release dates.
43 | */
44 | suspend fun forceReleaseAll(): Boolean
45 | }
46 |
--------------------------------------------------------------------------------
/api/coroutines-connector/src/test/groovy/com/coditory/sherlock/coroutines/InMemoryDistributedLockSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.coroutines
2 |
3 | import com.coditory.sherlock.AcquireLockMultipleTimesSpec
4 | import com.coditory.sherlock.AcquireLockSpec
5 | import com.coditory.sherlock.InfiniteAcquireLockSpec
6 | import com.coditory.sherlock.ReleaseLockSpec
7 | import com.coditory.sherlock.coroutines.base.UsesKtSherlock
8 |
9 | class CoroutinesInMemoryReleaseLockSpec extends ReleaseLockSpec
10 | implements UsesKtSherlock {}
11 |
12 | class CoroutinesInMemoryAcquireLockSpec extends AcquireLockSpec
13 | implements UsesKtSherlock {}
14 |
15 | class CoroutinesInMemoryAcquireLockMultipleTimesSpec extends AcquireLockMultipleTimesSpec
16 | implements UsesKtSherlock {}
17 |
18 | class CoroutinesInMemoryInfiniteAcquireLockSpec extends InfiniteAcquireLockSpec
19 | implements UsesKtSherlock {}
20 |
21 |
--------------------------------------------------------------------------------
/api/coroutines-connector/src/test/groovy/com/coditory/sherlock/coroutines/base/UsesKtSherlock.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.coroutines.base
2 |
3 | import com.coditory.sherlock.BlockingKtSherlockWrapper
4 | import com.coditory.sherlock.base.DistributedLocksCreator
5 | import com.coditory.sherlock.coroutines.Sherlock
6 | import com.coditory.sherlock.inmem.coroutines.InMemorySherlock
7 |
8 | import java.time.Clock
9 | import java.time.Duration
10 |
11 | trait UsesKtSherlock implements DistributedLocksCreator {
12 | @Override
13 | com.coditory.sherlock.Sherlock createSherlock(String ownerId, Duration duration, Clock clock, String collectionName) {
14 | Sherlock reactorSherlock = InMemorySherlock.builder()
15 | .withOwnerId(ownerId)
16 | .withLockDuration(duration)
17 | .withClock(clock)
18 | .build()
19 | return new BlockingKtSherlockWrapper(reactorSherlock)
20 | }
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/api/coroutines/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.kotlin")
3 | id("build.publish")
4 | id("build.coverage")
5 | }
6 |
7 | dependencies {
8 | api(projects.api.apiCommon)
9 | api(libs.kotlinx.coroutines.core)
10 | implementation(projects.common)
11 | implementation(libs.kotlinx.coroutines.reactive)
12 | testImplementation(libs.kotlinx.coroutines.test)
13 | testImplementation(projects.tests)
14 | testImplementation(projects.connectors.inmem.inmemCoroutines)
15 | }
16 |
17 | tasks.named("compileTestGroovy") {
18 | // make groovy test see kotlin test
19 | classpath += files(tasks.compileTestKotlin)
20 | }
21 |
--------------------------------------------------------------------------------
/api/coroutines/src/test/groovy/com/coditory/sherlock/coroutines/MigratorSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.coroutines
2 |
3 | import com.coditory.sherlock.BlockingKtSherlockWrapper
4 | import com.coditory.sherlock.Sherlock
5 | import com.coditory.sherlock.coroutines.base.BlockingKtMigratorBuilder
6 | import com.coditory.sherlock.inmem.coroutines.InMemorySherlock
7 | import com.coditory.sherlock.migrator.MigratorChangeSetsSpec
8 | import com.coditory.sherlock.migrator.MigratorSpec
9 | import com.coditory.sherlock.migrator.base.BlockingMigratorBuilder
10 | import com.coditory.sherlock.migrator.base.MigratorCreator
11 |
12 | import java.time.Clock
13 | import java.time.Duration
14 |
15 | class CoroutinesMigratorSpec extends MigratorSpec implements UsesKtInMemorySherlock {}
16 |
17 | class CoroutinesMigratorChangeSetSpec extends MigratorChangeSetsSpec implements UsesKtInMemorySherlock {}
18 |
19 | trait UsesKtInMemorySherlock implements MigratorCreator {
20 | @Override
21 | Sherlock createSherlock(String ownerId, Duration duration, Clock clock, String collectionName) {
22 | com.coditory.sherlock.coroutines.Sherlock sherlock = InMemorySherlock.builder()
23 | .withOwnerId(ownerId)
24 | .withLockDuration(duration)
25 | .withClock(clock)
26 | .build()
27 | return new BlockingKtSherlockWrapper(sherlock)
28 | }
29 |
30 | @Override
31 | BlockingMigratorBuilder createMigratorBuilder(Sherlock sherlock) {
32 | if (sherlock instanceof BlockingKtSherlockWrapper) {
33 | return new BlockingKtMigratorBuilder(sherlock.unwrap())
34 | }
35 | throw new IllegalArgumentException("Expected ${BlockingKtSherlockWrapper.simpleName}, got: " + sherlock.class.simpleName)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/api/coroutines/src/test/kotlin/com/coditory/sherlock/coroutines/base/Assertions.kt:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.coroutines.base
2 |
3 | import org.junit.jupiter.api.Assertions.fail
4 | import kotlin.reflect.KClass
5 |
6 | @Suppress("UNCHECKED_CAST")
7 | suspend fun assertThrows(
8 | action: suspend () -> Unit,
9 | type: KClass,
10 | ): T {
11 | return try {
12 | action()
13 | fail("Expected thrown exception")
14 | } catch (e: Throwable) {
15 | if (!type.isInstance(e)) {
16 | throw e
17 | }
18 | e as T
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/api/coroutines/src/test/kotlin/com/coditory/sherlock/coroutines/base/BlockingKtMigrator.kt:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.coroutines.base
2 |
3 | import com.coditory.sherlock.coroutines.Sherlock
4 | import com.coditory.sherlock.coroutines.migrator.SherlockMigrator
5 | import com.coditory.sherlock.coroutines.migrator.SherlockMigratorBuilder
6 | import com.coditory.sherlock.migrator.MigrationResult
7 | import com.coditory.sherlock.migrator.base.BlockingMigrator
8 | import com.coditory.sherlock.migrator.base.BlockingMigratorBuilder
9 | import kotlinx.coroutines.runBlocking
10 |
11 | class BlockingKtMigrator(
12 | private val migrator: SherlockMigrator,
13 | ) : BlockingMigrator {
14 | override fun migrate(): MigrationResult {
15 | return runBlocking { migrator.migrate() }
16 | }
17 | }
18 |
19 | class BlockingKtMigratorBuilder(sherlock: Sherlock) : BlockingMigratorBuilder {
20 | private val builder = SherlockMigratorBuilder(sherlock)
21 |
22 | override fun setMigrationId(migrationId: String): BlockingMigratorBuilder {
23 | builder.setMigrationId(migrationId)
24 | return this
25 | }
26 |
27 | override fun addChangeSet(
28 | changeSetId: String,
29 | changeSet: Runnable,
30 | ): BlockingMigratorBuilder {
31 | builder.addChangeSet(changeSetId, changeSet)
32 | return this
33 | }
34 |
35 | override fun addAnnotatedChangeSets(annotated: Any): BlockingMigratorBuilder {
36 | builder.addAnnotatedChangeSets(annotated)
37 | return this
38 | }
39 |
40 | override fun build(): BlockingMigrator {
41 | val migrator = builder.build()
42 | return BlockingKtMigrator(migrator)
43 | }
44 |
45 | override fun migrate(): MigrationResult {
46 | return build().migrate()
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/api/coroutines/src/test/kotlin/com/coditory/sherlock/coroutines/base/DynamicTest.kt:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.coroutines.base
2 |
3 | import kotlinx.coroutines.test.TestScope
4 | import kotlinx.coroutines.test.runTest
5 | import org.junit.jupiter.api.DynamicTest
6 |
7 | fun runDynamicTest(
8 | name: String,
9 | testBody: suspend TestScope.() -> Unit,
10 | ): DynamicTest {
11 | return DynamicTest.dynamicTest(name) {
12 | runTest(testBody = testBody)
13 | }
14 | }
15 |
16 | fun List.runDynamicTest(testBody: suspend TestScope.(T) -> Unit): List {
17 | return this.map {
18 | DynamicTest.dynamicTest(it.name) {
19 | runTest {
20 | testBody(it)
21 | }
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/api/coroutines/src/test/kotlin/com/coditory/sherlock/coroutines/base/Tuple.kt:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.coroutines.base
2 |
3 | fun tuple(p1: T1) = Tuple(p1)
4 |
5 | fun tuple(
6 | p1: T1,
7 | p2: T2,
8 | ) = Tuple2(p1, p2)
9 |
10 | fun tuple(
11 | p1: T1,
12 | p2: T2,
13 | p3: T3,
14 | ) = Tuple3(p1, p2, p3)
15 |
16 | fun tuple(
17 | p1: T1,
18 | p2: T2,
19 | p3: T3,
20 | p4: T4,
21 | ) = Tuple4(p1, p2, p3, p4)
22 |
23 | fun tuple(
24 | p1: T1,
25 | p2: T2,
26 | p3: T3,
27 | p4: T4,
28 | p5: T5,
29 | ) = Tuple5(p1, p2, p3, p4, p5)
30 |
31 | fun tuple(
32 | p1: T1,
33 | p2: T2,
34 | p3: T3,
35 | p4: T4,
36 | p5: T5,
37 | p6: T6,
38 | ): Tuple6 {
39 | return Tuple6(p1, p2, p3, p4, p5, p6)
40 | }
41 |
42 | data class Tuple(val p1: T1)
43 |
44 | data class Tuple2(val p1: T1, val p2: T2)
45 |
46 | data class Tuple3(val p1: T1, val p2: T2, val p3: T3)
47 |
48 | data class Tuple4(val p1: T1, val p2: T2, val p3: T3, val p4: T4)
49 |
50 | data class Tuple5(val p1: T1, val p2: T2, val p3: T3, val p4: T4, val p5: T5)
51 |
52 | data class Tuple6(val p1: T1, val p2: T2, val p3: T3, val p4: T4, val p5: T5, val p6: T6)
53 |
--------------------------------------------------------------------------------
/api/reactor/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | id("build.publish")
4 | id("build.coverage")
5 | }
6 |
7 | dependencies {
8 | api(projects.api.apiCommon)
9 | api(libs.reactor.core)
10 | implementation(projects.common)
11 | testImplementation(projects.tests)
12 | testImplementation(projects.connectors.inmem.inmemReactor)
13 | }
14 |
--------------------------------------------------------------------------------
/api/reactor/src/integrationTest/groovy/com/coditory/sherlock/reactor/InMemoryDistributedLockSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.reactor
2 |
3 | import com.coditory.sherlock.AcquireLockMultipleTimesSpec
4 | import com.coditory.sherlock.AcquireLockSpec
5 | import com.coditory.sherlock.InfiniteAcquireLockSpec
6 | import com.coditory.sherlock.ReleaseLockSpec
7 | import com.coditory.sherlock.reactor.base.UsesReactorSherlock
8 |
9 | class ReactorInMemoryReleaseLockSpec extends ReleaseLockSpec
10 | implements UsesReactorSherlock {}
11 |
12 | class ReactorInMemoryAcquireLockSpec extends AcquireLockSpec
13 | implements UsesReactorSherlock {}
14 |
15 | class ReactorInMemoryAcquireLockMultipleTimesSpec extends AcquireLockMultipleTimesSpec
16 | implements UsesReactorSherlock {}
17 |
18 | class ReactorInMemoryInfiniteAcquireLockSpec extends InfiniteAcquireLockSpec
19 | implements UsesReactorSherlock {}
20 |
--------------------------------------------------------------------------------
/api/reactor/src/integrationTest/groovy/com/coditory/sherlock/reactor/base/UsesReactorSherlock.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.reactor.base
2 |
3 | import com.coditory.sherlock.BlockingReactorSherlockWrapper
4 | import com.coditory.sherlock.Sherlock
5 | import com.coditory.sherlock.base.DistributedLocksCreator
6 | import com.coditory.sherlock.inmem.reactor.InMemorySherlock
7 |
8 | import java.time.Clock
9 | import java.time.Duration
10 |
11 | trait UsesReactorSherlock implements DistributedLocksCreator {
12 | @Override
13 | Sherlock createSherlock(String ownerId, Duration duration, Clock clock, String collectionName) {
14 | com.coditory.sherlock.reactor.Sherlock reactorSherlock = InMemorySherlock.builder()
15 | .withOwnerId(ownerId)
16 | .withLockDuration(duration)
17 | .withClock(clock)
18 | .build()
19 | return new BlockingReactorSherlockWrapper(reactorSherlock)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/api/reactor/src/main/java/com/coditory/sherlock/reactor/DistributedLockConnector.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.reactor;
2 |
3 | import com.coditory.sherlock.LockRequest;
4 | import com.coditory.sherlock.connector.AcquireResult;
5 | import com.coditory.sherlock.connector.InitializationResult;
6 | import com.coditory.sherlock.connector.ReleaseResult;
7 | import org.jetbrains.annotations.NotNull;
8 | import reactor.core.publisher.Mono;
9 |
10 | public interface DistributedLockConnector {
11 | /**
12 | * Initializes underlying infrastructure for locks.
13 | * Most frequently triggers database table creation and index creation.
14 | *
15 | * If it is not executed explicitly, connector may execute it during first acquire acquisition or
16 | * release.
17 | */
18 | @NotNull
19 | Mono initialize();
20 |
21 | /**
22 | * Acquires a lock.
23 | */
24 | @NotNull
25 | Mono acquire(@NotNull LockRequest lockRequest);
26 |
27 | /**
28 | * Acquires a lock or prolongs it if it was acquired by the same instance.
29 | */
30 | @NotNull
31 | Mono acquireOrProlong(LockRequest lockRequest);
32 |
33 | /**
34 | * Acquires a lock even if it was already acquired by someone else.
35 | */
36 | @NotNull
37 | Mono forceAcquire(@NotNull LockRequest lockRequest);
38 |
39 | /**
40 | * Unlocks a lock if wat acquired by the same instance.
41 | */
42 | @NotNull
43 | Mono release(@NotNull String lockId, @NotNull String ownerId);
44 |
45 | /**
46 | * Releases a lock without checking its owner or release date.
47 | */
48 | @NotNull
49 | Mono forceRelease(@NotNull String lockId);
50 |
51 | /**
52 | * Releases all locks without checking their owners or release dates.
53 | */
54 | @NotNull
55 | Mono forceReleaseAll();
56 | }
57 |
--------------------------------------------------------------------------------
/api/reactor/src/test/groovy/com/coditory/sherlock/reactor/migrator/BlockingReactorMigrator.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.reactor.migrator
2 |
3 | import com.coditory.sherlock.migrator.MigrationResult
4 | import com.coditory.sherlock.migrator.base.BlockingMigrator
5 | import com.coditory.sherlock.migrator.base.BlockingMigratorBuilder
6 | import com.coditory.sherlock.reactor.Sherlock
7 |
8 | import static java.util.Objects.requireNonNull
9 |
10 | class BlockingReactorMigrator implements BlockingMigrator {
11 | private final SherlockMigrator migrator
12 |
13 | BlockingReactorMigrator(SherlockMigrator migrator) {
14 | this.migrator = requireNonNull(migrator)
15 | }
16 |
17 | @Override
18 | MigrationResult migrate() {
19 | return migrator.migrate().block()
20 | }
21 | }
22 |
23 | class BlockingReactorMigratorBuilder implements BlockingMigratorBuilder {
24 | private final SherlockMigratorBuilder builder
25 |
26 | BlockingReactorMigratorBuilder(Sherlock sherlock) {
27 | requireNonNull(sherlock)
28 | builder = new SherlockMigratorBuilder(sherlock)
29 | }
30 |
31 | @Override
32 | BlockingMigratorBuilder setMigrationId(String migrationId) {
33 | builder.setMigrationId(migrationId)
34 | return this
35 | }
36 |
37 | @Override
38 | BlockingMigratorBuilder addChangeSet(String changeSetId, Runnable changeSet) {
39 | builder.addChangeSet(changeSetId, changeSet)
40 | return this
41 | }
42 |
43 | @Override
44 | BlockingMigratorBuilder addAnnotatedChangeSets(Object object) {
45 | builder.addAnnotatedChangeSets(object)
46 | return this
47 | }
48 |
49 | @Override
50 | BlockingMigrator build() {
51 | SherlockMigrator migrator = builder.build()
52 | return new BlockingReactorMigrator(migrator)
53 | }
54 |
55 | @Override
56 | MigrationResult migrate() {
57 | return build().migrate()
58 | }
59 | }
--------------------------------------------------------------------------------
/api/reactor/src/test/groovy/com/coditory/sherlock/reactor/migrator/MigratorSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.reactor.migrator
2 |
3 | import com.coditory.sherlock.BlockingReactorSherlockWrapper
4 | import com.coditory.sherlock.Sherlock
5 | import com.coditory.sherlock.migrator.MigratorChangeSetsSpec
6 | import com.coditory.sherlock.migrator.MigratorSpec
7 | import com.coditory.sherlock.migrator.base.BlockingMigratorBuilder
8 | import com.coditory.sherlock.migrator.base.MigratorCreator
9 |
10 | import java.time.Clock
11 | import java.time.Duration
12 |
13 | import static com.coditory.sherlock.inmem.reactor.InMemorySherlock.builder
14 |
15 | class ReactorMigratorSpec extends MigratorSpec implements UsesReactorInMemorySherlock {}
16 |
17 | class ReactorMigratorChangeSetSpec extends MigratorChangeSetsSpec implements UsesReactorInMemorySherlock {}
18 |
19 | trait UsesReactorInMemorySherlock implements MigratorCreator {
20 | @Override
21 | Sherlock createSherlock(String ownerId, Duration duration, Clock clock, String collectionName) {
22 | com.coditory.sherlock.reactor.Sherlock reactorLocks = builder()
23 | .withOwnerId(ownerId)
24 | .withLockDuration(duration)
25 | .withClock(clock)
26 | .build()
27 | return new BlockingReactorSherlockWrapper(reactorLocks)
28 | }
29 |
30 | @Override
31 | BlockingMigratorBuilder createMigratorBuilder(Sherlock sherlock) {
32 | if (sherlock instanceof BlockingReactorSherlockWrapper) {
33 | return new BlockingReactorMigratorBuilder(sherlock.unwrap())
34 | }
35 | throw new IllegalArgumentException("Expected ${BlockingReactorMigratorBuilder.simpleName}, got: " + sherlock.class.simpleName)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/api/rxjava/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | id("build.publish")
4 | id("build.coverage")
5 | }
6 |
7 | dependencies {
8 | api(projects.api.apiCommon)
9 | api(libs.rxjava)
10 | implementation(projects.common)
11 | testImplementation(projects.tests)
12 | testImplementation(projects.connectors.inmem.inmemRxjava)
13 | }
14 |
--------------------------------------------------------------------------------
/api/rxjava/src/integrationTest/groovy/com/coditory/sherlock/rxjava/InMemoryDistributedLockSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.rxjava
2 |
3 | import com.coditory.sherlock.AcquireLockMultipleTimesSpec
4 | import com.coditory.sherlock.AcquireLockSpec
5 | import com.coditory.sherlock.InfiniteAcquireLockSpec
6 | import com.coditory.sherlock.ReleaseLockSpec
7 | import com.coditory.sherlock.rxjava.base.UsesRxSherlock
8 |
9 | class RxInMemoryReleaseLockSpec extends ReleaseLockSpec
10 | implements UsesRxSherlock {}
11 |
12 | class RxInMemoryAcquireLockSpec extends AcquireLockSpec
13 | implements UsesRxSherlock {}
14 |
15 | class RxInMemoryAcquireLockMultipleTimesSpec extends AcquireLockMultipleTimesSpec
16 | implements UsesRxSherlock {}
17 |
18 | class RxInMemoryInfiniteAcquireLockSpec extends InfiniteAcquireLockSpec
19 | implements UsesRxSherlock {}
20 |
--------------------------------------------------------------------------------
/api/rxjava/src/integrationTest/groovy/com/coditory/sherlock/rxjava/base/UsesRxSherlock.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.rxjava.base
2 |
3 | import com.coditory.sherlock.BlockingRxSherlockWrapper
4 | import com.coditory.sherlock.Sherlock
5 | import com.coditory.sherlock.base.DistributedLocksCreator
6 | import com.coditory.sherlock.inmem.rxjava.InMemorySherlock
7 |
8 | import java.time.Clock
9 | import java.time.Duration
10 |
11 | trait UsesRxSherlock implements DistributedLocksCreator {
12 | @Override
13 | Sherlock createSherlock(String ownerId, Duration duration, Clock clock, String collectionName) {
14 | com.coditory.sherlock.rxjava.Sherlock rxSherlock = InMemorySherlock.builder()
15 | .withOwnerId(ownerId)
16 | .withLockDuration(duration)
17 | .withClock(clock)
18 | .build()
19 | return new BlockingRxSherlockWrapper(rxSherlock)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/api/rxjava/src/main/java/com/coditory/sherlock/rxjava/DistributedLockConnector.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.rxjava;
2 |
3 | import com.coditory.sherlock.LockRequest;
4 | import com.coditory.sherlock.connector.AcquireResult;
5 | import com.coditory.sherlock.connector.InitializationResult;
6 | import com.coditory.sherlock.connector.ReleaseResult;
7 | import io.reactivex.rxjava3.core.Single;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | public interface DistributedLockConnector {
11 | /**
12 | * Initializes underlying infrastructure for locks.
13 | * Most frequently triggers database table creation and index creation.
14 | *
15 | * If it is not executed explicitly, connector may execute it during first acquire acquisition or
16 | * release.
17 | */
18 | @NotNull
19 | Single initialize();
20 |
21 | /**
22 | * Acquire a lock.
23 | */
24 | @NotNull
25 | Single acquire(@NotNull LockRequest lockRequest);
26 |
27 | /**
28 | * Acquire a lock or prolong it if it was acquired by the same instance.
29 | */
30 | @NotNull
31 | Single acquireOrProlong(LockRequest lockRequest);
32 |
33 | /**
34 | * Acquire a lock even if it was already acquired by someone else
35 | */
36 | @NotNull
37 | Single forceAcquire(@NotNull LockRequest lockRequest);
38 |
39 | /**
40 | * Unlock a lock if wat acquired by the same instance.
41 | */
42 | @NotNull
43 | Single release(@NotNull String lockId, @NotNull String ownerId);
44 |
45 | /**
46 | * Release a lock without checking its owner or release date.
47 | */
48 | @NotNull
49 | Single forceRelease(@NotNull String lockId);
50 |
51 | /**
52 | * Release all locks without checking their owners or release dates.
53 | */
54 | @NotNull
55 | Single forceReleaseAll();
56 | }
57 |
--------------------------------------------------------------------------------
/api/rxjava/src/test/groovy/com/coditory/sherlock/rxjava/migrator/BlockingRxMigrator.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.rxjava.migrator
2 |
3 | import com.coditory.sherlock.migrator.MigrationResult
4 | import com.coditory.sherlock.migrator.base.BlockingMigrator
5 | import com.coditory.sherlock.migrator.base.BlockingMigratorBuilder
6 | import com.coditory.sherlock.rxjava.Sherlock
7 |
8 | import static java.util.Objects.requireNonNull
9 |
10 | class BlockingRxMigrator implements BlockingMigrator {
11 | private final SherlockMigrator migrator
12 |
13 | BlockingRxMigrator(SherlockMigrator migrator) {
14 | this.migrator = requireNonNull(migrator)
15 | }
16 |
17 | @Override
18 | MigrationResult migrate() {
19 | return migrator.migrate().blockingGet()
20 | }
21 | }
22 |
23 | class BlockingRxMigratorBuilder implements BlockingMigratorBuilder {
24 | private final SherlockMigratorBuilder builder
25 |
26 | BlockingRxMigratorBuilder(Sherlock sherlock) {
27 | requireNonNull(sherlock)
28 | builder = new SherlockMigratorBuilder(sherlock)
29 | }
30 |
31 | @Override
32 | BlockingMigratorBuilder setMigrationId(String migrationId) {
33 | builder.setMigrationId(migrationId)
34 | return this
35 | }
36 |
37 | @Override
38 | BlockingMigratorBuilder addChangeSet(String changeSetId, Runnable changeSet) {
39 | builder.addChangeSet(changeSetId, changeSet)
40 | return this
41 | }
42 |
43 | @Override
44 | BlockingMigratorBuilder addAnnotatedChangeSets(Object object) {
45 | builder.addAnnotatedChangeSets(object)
46 | return this
47 | }
48 |
49 | @Override
50 | BlockingMigrator build() {
51 | SherlockMigrator migrator = builder.build()
52 | return new BlockingRxMigrator(migrator)
53 | }
54 |
55 | @Override
56 | MigrationResult migrate() {
57 | return build().migrate()
58 | }
59 | }
--------------------------------------------------------------------------------
/api/rxjava/src/test/groovy/com/coditory/sherlock/rxjava/migrator/MigratorSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.rxjava.migrator
2 |
3 | import com.coditory.sherlock.BlockingRxSherlockWrapper
4 | import com.coditory.sherlock.Sherlock
5 | import com.coditory.sherlock.inmem.rxjava.InMemorySherlock
6 | import com.coditory.sherlock.migrator.MigratorChangeSetsSpec
7 | import com.coditory.sherlock.migrator.MigratorSpec
8 | import com.coditory.sherlock.migrator.base.BlockingMigratorBuilder
9 | import com.coditory.sherlock.migrator.base.MigratorCreator
10 |
11 | import java.time.Clock
12 | import java.time.Duration
13 |
14 | class RxMigratorSpec extends MigratorSpec implements UsesRxInMemorySherlock {}
15 |
16 | class RxMigratorChangeSetSpec extends MigratorChangeSetsSpec implements UsesRxInMemorySherlock {}
17 |
18 | trait UsesRxInMemorySherlock implements MigratorCreator {
19 | @Override
20 | Sherlock createSherlock(String ownerId, Duration duration, Clock clock, String collectionName) {
21 | com.coditory.sherlock.rxjava.Sherlock rxLocks = InMemorySherlock.builder()
22 | .withOwnerId(ownerId)
23 | .withLockDuration(duration)
24 | .withClock(clock)
25 | .build()
26 | return new BlockingRxSherlockWrapper(rxLocks)
27 | }
28 |
29 | @Override
30 | BlockingMigratorBuilder createMigratorBuilder(Sherlock sherlock) {
31 | if (sherlock instanceof BlockingRxSherlockWrapper) {
32 | return new BlockingRxMigratorBuilder(sherlock.unwrap())
33 | }
34 | throw new IllegalArgumentException("Expected ${BlockingRxSherlockWrapper.simpleName}, got: " + sherlock.class.simpleName)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/api/sync/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | id("build.publish")
4 | id("build.coverage")
5 | }
6 |
7 | dependencies {
8 | api(projects.api.apiCommon)
9 | implementation(projects.common)
10 | testImplementation(projects.tests)
11 | testImplementation(projects.connectors.inmem.inmemSync)
12 | }
13 |
--------------------------------------------------------------------------------
/api/sync/src/main/java/com/coditory/sherlock/DistributedLockConnector.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 |
5 | public interface DistributedLockConnector {
6 | /**
7 | * Initializes underlying infrastructure for locks.
8 | * Most frequently triggers database table creation and index creation.
9 | *
10 | * If it is not executed explicitly, connector may execute it during first acquire acquisition or
11 | * release.
12 | */
13 | void initialize();
14 |
15 | /**
16 | * Acquire a lock.
17 | *
18 | * @return boolean - true if acquire was acquired by this call
19 | */
20 | boolean acquire(@NotNull LockRequest lockRequest);
21 |
22 | /**
23 | * Acquire a lock or prolong it if it was acquired by the same instance.
24 | *
25 | * @return boolean - true if acquire was acquired by this call
26 | */
27 | boolean acquireOrProlong(@NotNull LockRequest lockRequest);
28 |
29 | /**
30 | * Acquire a lock even if it was already acquired by someone else
31 | *
32 | * @return boolean - true if acquire was acquired by this call
33 | */
34 | boolean forceAcquire(@NotNull LockRequest lockRequest);
35 |
36 | /**
37 | * Unlock a lock if wat acquired by the same instance.
38 | *
39 | * @return boolean - true if acquire was released by this call
40 | */
41 | boolean release(@NotNull String lockId, @NotNull String ownerId);
42 |
43 | /**
44 | * Release a lock without checking its owner or release date.
45 | *
46 | * @return boolean - true if acquire was released by this call
47 | */
48 | boolean forceRelease(@NotNull String lockId);
49 |
50 | /**
51 | * Release all locks without checking their owners or release dates.
52 | *
53 | * @return boolean - true if at least one lock was released
54 | */
55 | boolean forceReleaseAll();
56 | }
57 |
--------------------------------------------------------------------------------
/api/sync/src/test/groovy/com/coditory/sherlock/migrator/BlockingSyncMigrator.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.migrator
2 |
3 | import com.coditory.sherlock.Sherlock
4 | import com.coditory.sherlock.migrator.base.BlockingMigrator
5 | import com.coditory.sherlock.migrator.base.BlockingMigratorBuilder
6 |
7 | import static java.util.Objects.requireNonNull
8 |
9 | class BlockingSyncMigrator implements BlockingMigrator {
10 | private final SherlockMigrator migrator
11 |
12 | BlockingSyncMigrator(SherlockMigrator migrator) {
13 | this.migrator = requireNonNull(migrator)
14 | }
15 |
16 | @Override
17 | MigrationResult migrate() {
18 | return migrator.migrate()
19 | }
20 | }
21 |
22 | class BlockingSyncMigratorBuilder implements BlockingMigratorBuilder {
23 | private final SherlockMigratorBuilder builder
24 |
25 | BlockingSyncMigratorBuilder(Sherlock sherlock) {
26 | requireNonNull(sherlock)
27 | builder = new SherlockMigratorBuilder(sherlock)
28 | }
29 |
30 | @Override
31 | BlockingMigratorBuilder setMigrationId(String migrationId) {
32 | builder.setMigrationId(migrationId)
33 | return this
34 | }
35 |
36 | @Override
37 | BlockingMigratorBuilder addChangeSet(String changeSetId, Runnable changeSet) {
38 | builder.addChangeSet(changeSetId, changeSet)
39 | return this
40 | }
41 |
42 | @Override
43 | BlockingMigratorBuilder addAnnotatedChangeSets(Object object) {
44 | builder.addAnnotatedChangeSets(object)
45 | return this
46 | }
47 |
48 | @Override
49 | BlockingMigrator build() {
50 | SherlockMigrator migrator = builder.build()
51 | return new BlockingSyncMigrator(migrator)
52 | }
53 |
54 | @Override
55 | MigrationResult migrate() {
56 | return build().migrate()
57 | }
58 | }
--------------------------------------------------------------------------------
/api/sync/src/test/groovy/com/coditory/sherlock/migrator/SherlockMigratorSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.migrator
2 |
3 | import com.coditory.sherlock.Sherlock
4 | import com.coditory.sherlock.inmem.InMemorySherlock
5 | import com.coditory.sherlock.migrator.base.BlockingMigratorBuilder
6 | import com.coditory.sherlock.migrator.base.MigratorCreator
7 |
8 | import java.time.Clock
9 | import java.time.Duration
10 |
11 | class SyncMigratorSpec extends MigratorSpec implements UsesInMemorySherlock {}
12 |
13 | class SyncMigratorChangeSetSpec extends MigratorChangeSetsSpec implements UsesInMemorySherlock {
14 | }
15 |
16 | trait UsesInMemorySherlock implements MigratorCreator {
17 | @Override
18 | BlockingMigratorBuilder createMigratorBuilder(Sherlock sherlock) {
19 | return new BlockingSyncMigratorBuilder(sherlock)
20 | }
21 |
22 | @Override
23 | Sherlock createSherlock(String ownerId, Duration duration, Clock clock, String collectionName) {
24 | return InMemorySherlock.builder()
25 | .withOwnerId(ownerId)
26 | .withLockDuration(duration)
27 | .withClock(clock)
28 | .build()
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/build-logic/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | `kotlin-dsl`
3 | kotlin("jvm") version embeddedKotlinVersion
4 | }
5 |
6 | repositories {
7 | mavenCentral()
8 | gradlePluginPortal()
9 | }
10 |
11 | dependencies {
12 | implementation(libs.gradle.kotlin)
13 | implementation(libs.gradle.ktlint)
14 | implementation(libs.gradle.nexusPublish)
15 | compileOnly(files(libs::class.java.protectionDomain.codeSource.location))
16 | }
17 |
--------------------------------------------------------------------------------
/build-logic/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "build-logic"
2 |
3 | dependencyResolutionManagement {
4 | versionCatalogs {
5 | create("libs") {
6 | from(files("../gradle/libs.versions.toml"))
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/build-logic/src/main/kotlin/build.coverage-root.gradle.kts:
--------------------------------------------------------------------------------
1 | import gradle.kotlin.dsl.accessors._1d662f89dd4d27eb95f0093e004edd00.main
2 | import gradle.kotlin.dsl.accessors._1d662f89dd4d27eb95f0093e004edd00.sourceSets
3 |
4 | plugins {
5 | id("jacoco")
6 | }
7 |
8 | tasks.register("coverage") {
9 | description = "Creates combined coverage report"
10 |
11 | val coveredProjects =
12 | subprojects
13 | .filter { it.plugins.hasPlugin("jacoco") }
14 |
15 | coveredProjects
16 | .forEach { subproject ->
17 | executionData(fileTree(subproject.layout.buildDirectory).include("jacoco/*.exec"))
18 | sourceSets(subproject.sourceSets.main.get())
19 | }
20 |
21 | dependsOn(
22 | coveredProjects.map { it.tasks.findByName("test") } +
23 | coveredProjects.map { it.tasks.findByName("integrationTest") },
24 | )
25 |
26 | reports {
27 | xml.required.set(true)
28 | html.required.set(true)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/build-logic/src/main/kotlin/build.coverage.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("java-library")
3 | id("groovy")
4 | id("jacoco")
5 | }
6 |
7 | val libs = extensions.getByType(org.gradle.accessors.dm.LibrariesForLibs::class)
8 |
9 | // coverage introduces time overhead
10 | // enable coverage on demand only
11 | // ./gradlew ... -Pcoverage
12 | // ...or with a task
13 | // ./gradlew ... coverage
14 | val coverageEnabled =
15 | (project.hasProperty("coverage") && project.properties["coverage"] != "false") ||
16 | project.gradle.startParameter.taskNames.contains("coverage")
17 |
18 | jacoco {
19 | toolVersion = libs.versions.jacoco.get()
20 | }
21 |
22 | tasks.withType().configureEach {
23 | configure {
24 | isEnabled = coverageEnabled
25 | }
26 | }
27 |
28 | // coverage report combining all types of tests (unit + integration)
29 | tasks.jacocoTestReport.configure {
30 | executionData(fileTree(project.layout.buildDirectory).include("jacoco/*.exec"))
31 | reports {
32 | xml.required.set(true)
33 | html.required.set(true)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/build-logic/src/main/kotlin/build.java.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("java-library")
3 | id("build.test")
4 | }
5 |
6 | val libs = extensions.getByType(org.gradle.accessors.dm.LibrariesForLibs::class)
7 |
8 | java {
9 | toolchain {
10 | languageVersion.set(JavaLanguageVersion.of(libs.versions.java.get()))
11 | }
12 | withJavadocJar()
13 | withSourcesJar()
14 | }
15 |
16 | tasks.withType().configureEach {
17 | options.encoding = "UTF-8"
18 | options.compilerArgs.addAll(listOf("-Werror", "-Xlint", "-Xlint:-serial"))
19 | }
20 |
21 | // make javadoc less strict to limit noisy logs
22 | tasks.withType().configureEach {
23 | val sourceSet = project.extensions.getByType().sourceSets.getByName("main")
24 | source = sourceSet.allJava
25 | classpath = sourceSet.compileClasspath
26 | isFailOnError = false
27 | options {
28 | this as StandardJavadocDocletOptions
29 | addBooleanOption("Xdoclint:none", true)
30 | addStringOption("Xmaxwarns", "1")
31 | memberLevel = JavadocMemberLevel.PUBLIC
32 | outputLevel = JavadocOutputLevel.QUIET
33 | encoding = "UTF-8"
34 | addBooleanOption("html5", true)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/build-logic/src/main/kotlin/build.kotlin.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
2 |
3 | plugins {
4 | id("build.java")
5 | kotlin("jvm")
6 | id("org.jlleitschuh.gradle.ktlint")
7 | }
8 |
9 | val libs = extensions.getByType(org.gradle.accessors.dm.LibrariesForLibs::class)
10 |
11 | ktlint {
12 | version.set(libs.versions.ktlint.get())
13 | }
14 |
15 | kotlin {
16 | target.compilations {
17 | getByName("integrationTest")
18 | .associateWith(getByName("test"))
19 | }
20 | jvmToolchain {
21 | languageVersion.set(JavaLanguageVersion.of(libs.versions.java.get()))
22 | }
23 | }
24 |
25 | tasks.withType().configureEach {
26 | compilerOptions {
27 | allWarningsAsErrors.set(true)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/build-logic/src/main/kotlin/build.publish-root.gradle.kts:
--------------------------------------------------------------------------------
1 | import java.time.Duration
2 |
3 | plugins {
4 | id("io.github.gradle-nexus.publish-plugin")
5 | }
6 |
7 | nexusPublishing {
8 | connectTimeout.set(Duration.ofMinutes(5))
9 | clientTimeout.set(Duration.ofMinutes(5))
10 | repositories {
11 | sonatype {
12 | System.getenv("OSSRH_STAGING_PROFILE_ID")?.let { stagingProfileId = it }
13 | System.getenv("OSSRH_USERNAME")?.let { username.set(it) }
14 | System.getenv("OSSRH_PASSWORD")?.let { password.set(it) }
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/build-logic/src/main/kotlin/build.version.gradle.kts:
--------------------------------------------------------------------------------
1 | // Prints project version.
2 | // Usage: ./gradlew version --quiet
3 | tasks.register("version") {
4 | val version = project.version
5 | doLast { println(version) }
6 | }
7 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.version")
3 | id("build.coverage-root")
4 | id("build.publish-root")
5 | }
6 |
--------------------------------------------------------------------------------
/common/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // This module is NOT a public API - imported as implementation
2 |
3 | plugins {
4 | id("build.java")
5 | id("build.coverage")
6 | id("build.publish")
7 | }
8 |
9 | dependencies {
10 | api(libs.slf4j.api)
11 | api(libs.jetbrains.annotations)
12 | }
13 |
--------------------------------------------------------------------------------
/common/src/main/java/com/coditory/sherlock/LockRequest.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 | import org.jetbrains.annotations.Nullable;
5 |
6 | import java.time.Duration;
7 |
8 | import static com.coditory.sherlock.Preconditions.expectNonEmpty;
9 | import static com.coditory.sherlock.Preconditions.expectTruncatedToMillis;
10 |
11 | public record LockRequest(
12 | @NotNull String lockId,
13 | @NotNull String ownerId,
14 | @Nullable Duration duration
15 | ) {
16 | public LockRequest {
17 | expectNonEmpty(lockId, "lockId");
18 | expectNonEmpty(ownerId, "ownerId");
19 | if (duration != null) {
20 | expectTruncatedToMillis(duration, "duration");
21 | }
22 | }
23 |
24 | public LockRequest(
25 | @NotNull String lockId,
26 | @NotNull String ownerId
27 | ) {
28 | this(lockId, ownerId, null);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/common/src/main/java/com/coditory/sherlock/SherlockDefaults.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock;
2 |
3 | import java.time.Clock;
4 | import java.time.Duration;
5 |
6 | public final class SherlockDefaults {
7 | public static final Duration DEFAULT_LOCK_DURATION = Duration.ofMinutes(5);
8 | public static final Clock DEFAULT_CLOCK = Clock.systemUTC();
9 | public static final String DEFAULT_MIGRATOR_LOCK_ID = "migrator";
10 |
11 | private SherlockDefaults() {
12 | throw new UnsupportedOperationException("Do not instantiate utility class");
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/common/src/main/java/com/coditory/sherlock/Timer.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 |
5 | public class Timer {
6 | public static Timer start() {
7 | return new Timer();
8 | }
9 |
10 | private final long started = System.currentTimeMillis();
11 |
12 | public long elapsedMs() {
13 | return System.currentTimeMillis() - started;
14 | }
15 |
16 | @NotNull
17 | public String elapsed() {
18 | return elapsedMs() + "ms";
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/common/src/main/java/com/coditory/sherlock/UuidGenerator.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 |
5 | import java.util.UUID;
6 |
7 | public final class UuidGenerator {
8 | private UuidGenerator() {
9 | throw new UnsupportedOperationException("Do not instantiate utility class");
10 | }
11 |
12 | @NotNull
13 | public static String uuid() {
14 | return UUID.randomUUID()
15 | .toString();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/common/src/main/java/com/coditory/sherlock/migrator/ChangeSet.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.migrator;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Target(ElementType.METHOD)
9 | @Retention(RetentionPolicy.RUNTIME)
10 | public @interface ChangeSet {
11 | int order();
12 |
13 | String id();
14 |
15 | String description() default "";
16 |
17 | String author() default "";
18 | }
19 |
--------------------------------------------------------------------------------
/common/src/main/java/com/coditory/sherlock/migrator/MigrationChangeSet.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.migrator;
2 |
3 | import java.util.function.Supplier;
4 |
5 | public record MigrationChangeSet(String id, Supplier action) {
6 | static MigrationChangeSet from(MigrationChangeSetMethod changeSetMethod) {
7 | return new MigrationChangeSet<>(
8 | changeSetMethod.id(),
9 | ChangeSetMethodExtractor.invokeMethod(changeSetMethod.method(), changeSetMethod.object(), changeSetMethod.returnType())
10 | );
11 | }
12 |
13 | public R execute() {
14 | return action.get();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/common/src/main/java/com/coditory/sherlock/migrator/MigrationChangeSetMethod.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.migrator;
2 |
3 | import java.lang.reflect.Method;
4 |
5 | public record MigrationChangeSetMethod(String id, Object object, Method method, Class returnType) {
6 | }
7 |
--------------------------------------------------------------------------------
/connectors/inmem/common/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | id("build.publish")
4 | id("build.coverage")
5 | }
6 |
7 | dependencies {
8 | api(projects.common)
9 | }
10 |
--------------------------------------------------------------------------------
/connectors/inmem/coroutines/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.kotlin")
3 | id("build.publish")
4 | id("build.coverage")
5 | }
6 |
7 | dependencies {
8 | api(projects.api.apiCoroutines)
9 | api(projects.api.apiCoroutinesConnector)
10 | implementation(projects.connectors.inmem.inmemCommon)
11 | integrationTestImplementation(projects.tests)
12 | }
13 |
--------------------------------------------------------------------------------
/connectors/inmem/coroutines/src/integrationTest/groovy/com/coditory/sherlock/inmem/coroutines/InMemoryDistributedLockSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.inmem.coroutines
2 |
3 | import com.coditory.sherlock.AcquireLockMultipleTimesSpec
4 | import com.coditory.sherlock.AcquireLockSpec
5 | import com.coditory.sherlock.InfiniteAcquireLockSpec
6 | import com.coditory.sherlock.ReleaseLockSpec
7 |
8 | class KtInMemoryReleaseLockSpec extends ReleaseLockSpec
9 | implements UsesKtInMemorySherlock {}
10 |
11 | class KtInMemoryAcquireLockSpec extends AcquireLockSpec
12 | implements UsesKtInMemorySherlock {}
13 |
14 | class KtInMemoryAcquireLockMultipleTimesSpec extends AcquireLockMultipleTimesSpec
15 | implements UsesKtInMemorySherlock {}
16 |
17 | class KtInMemoryInfiniteAcquireLockSpec extends InfiniteAcquireLockSpec
18 | implements UsesKtInMemorySherlock {}
19 |
20 |
--------------------------------------------------------------------------------
/connectors/inmem/coroutines/src/integrationTest/groovy/com/coditory/sherlock/inmem/coroutines/UsesKtInMemorySherlock.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.inmem.coroutines
2 |
3 | import com.coditory.sherlock.BlockingKtSherlockWrapper
4 | import com.coditory.sherlock.Sherlock
5 | import com.coditory.sherlock.base.DistributedLocksCreator
6 |
7 | import java.time.Clock
8 | import java.time.Duration
9 |
10 | trait UsesKtInMemorySherlock implements DistributedLocksCreator {
11 | @Override
12 | Sherlock createSherlock(String instanceId, Duration duration, Clock clock, String collectionName) {
13 | com.coditory.sherlock.coroutines.Sherlock reactiveLocks = InMemorySherlock.builder()
14 | .withOwnerId(instanceId)
15 | .withLockDuration(duration)
16 | .withClock(clock)
17 | .build()
18 | return new BlockingKtSherlockWrapper(reactiveLocks)
19 | }
20 | }
21 |
22 |
23 |
--------------------------------------------------------------------------------
/connectors/inmem/coroutines/src/main/kotlin/com/coditory/sherlock/inmem/coroutines/KtInMemoryDistributedLockConnector.kt:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.inmem.coroutines
2 |
3 | import com.coditory.sherlock.LockRequest
4 | import com.coditory.sherlock.coroutines.SuspendingDistributedLockConnector
5 | import com.coditory.sherlock.inmem.InMemoryDistributedLockStorage
6 | import java.time.Clock
7 | import java.time.Instant
8 |
9 | internal class KtInMemoryDistributedLockConnector(
10 | private val clock: Clock,
11 | private val storage: InMemoryDistributedLockStorage,
12 | ) : SuspendingDistributedLockConnector {
13 | override suspend fun initialize() {
14 | // deliberately empty
15 | }
16 |
17 | override suspend fun acquire(lockRequest: LockRequest): Boolean {
18 | return storage.acquire(lockRequest, now())
19 | }
20 |
21 | override suspend fun acquireOrProlong(lockRequest: LockRequest): Boolean {
22 | return storage.acquireOrProlong(lockRequest, now())
23 | }
24 |
25 | override suspend fun forceAcquire(lockRequest: LockRequest): Boolean {
26 | return storage.forceAcquire(lockRequest, now())
27 | }
28 |
29 | override suspend fun release(lockId: String, ownerId: String): Boolean {
30 | return storage.release(lockId, now(), ownerId)
31 | }
32 |
33 | override suspend fun forceRelease(lockId: String): Boolean {
34 | return storage.forceRelease(lockId, now())
35 | }
36 |
37 | override suspend fun forceReleaseAll(): Boolean {
38 | return storage.forceReleaseAll(now())
39 | }
40 |
41 | private fun now(): Instant {
42 | return clock.instant()
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/connectors/inmem/reactor/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | id("build.publish")
4 | id("build.coverage")
5 | }
6 |
7 | dependencies {
8 | api(projects.api.apiReactor)
9 | api(projects.connectors.inmem.inmemSync)
10 | implementation(projects.connectors.inmem.inmemCommon)
11 | integrationTestImplementation(projects.tests)
12 | }
13 |
--------------------------------------------------------------------------------
/connectors/inmem/reactor/src/integrationTest/groovy/com/coditory/sherlock/inmem/reactor/InMemoryDistributedLockSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.inmem.reactor
2 |
3 | import com.coditory.sherlock.AcquireLockMultipleTimesSpec
4 | import com.coditory.sherlock.AcquireLockSpec
5 | import com.coditory.sherlock.InfiniteAcquireLockSpec
6 | import com.coditory.sherlock.ReleaseLockSpec
7 |
8 | class ReactorInMemoryReleaseLockSpec extends ReleaseLockSpec
9 | implements UsesReactorInMemorySherlock {}
10 |
11 | class ReactorInMemoryAcquireLockSpec extends AcquireLockSpec
12 | implements UsesReactorInMemorySherlock {}
13 |
14 | class ReactorInMemoryAcquireLockMultipleTimesSpec extends AcquireLockMultipleTimesSpec
15 | implements UsesReactorInMemorySherlock {}
16 |
17 | class ReactorInMemoryInfiniteAcquireLockSpec extends InfiniteAcquireLockSpec
18 | implements UsesReactorInMemorySherlock {}
19 |
20 |
--------------------------------------------------------------------------------
/connectors/inmem/reactor/src/integrationTest/groovy/com/coditory/sherlock/inmem/reactor/UsesReactorInMemorySherlock.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.inmem.reactor
2 |
3 | import com.coditory.sherlock.BlockingReactorSherlockWrapper
4 | import com.coditory.sherlock.Sherlock
5 | import com.coditory.sherlock.base.DistributedLocksCreator
6 |
7 | import java.time.Clock
8 | import java.time.Duration
9 |
10 | trait UsesReactorInMemorySherlock implements DistributedLocksCreator {
11 | @Override
12 | Sherlock createSherlock(String instanceId, Duration duration, Clock clock, String collectionName) {
13 | com.coditory.sherlock.reactor.Sherlock reactorLocks = InMemorySherlock.builder()
14 | .withOwnerId(instanceId)
15 | .withLockDuration(duration)
16 | .withClock(clock)
17 | .build()
18 | return new BlockingReactorSherlockWrapper(reactorLocks)
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/connectors/inmem/rxjava/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | id("build.publish")
4 | id("build.coverage")
5 | }
6 |
7 | dependencies {
8 | api(projects.api.apiRxjava)
9 | api(projects.connectors.inmem.inmemSync)
10 | implementation(projects.connectors.inmem.inmemCommon)
11 | integrationTestImplementation(projects.tests)
12 | }
13 |
--------------------------------------------------------------------------------
/connectors/inmem/rxjava/src/integrationTest/groovy/com/coditory/sherlock/inmem/rxjava/InMemoryDistributedLockSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.inmem.rxjava
2 |
3 | import com.coditory.sherlock.AcquireLockMultipleTimesSpec
4 | import com.coditory.sherlock.AcquireLockSpec
5 | import com.coditory.sherlock.InfiniteAcquireLockSpec
6 | import com.coditory.sherlock.ReleaseLockSpec
7 |
8 | class ReactiveInMemoryReleaseLockSpec extends ReleaseLockSpec
9 | implements UsesReactiveInMemorySherlock {}
10 |
11 | class ReactiveInMemoryAcquireLockSpec extends AcquireLockSpec
12 | implements UsesReactiveInMemorySherlock {}
13 |
14 | class ReactiveInMemoryAcquireLockMultipleTimesSpec extends AcquireLockMultipleTimesSpec
15 | implements UsesReactiveInMemorySherlock {}
16 |
17 | class ReactiveInMemoryInfiniteAcquireLockSpec extends InfiniteAcquireLockSpec
18 | implements UsesReactiveInMemorySherlock {}
19 |
20 |
--------------------------------------------------------------------------------
/connectors/inmem/rxjava/src/integrationTest/groovy/com/coditory/sherlock/inmem/rxjava/UsesReactiveInMemorySherlock.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.inmem.rxjava
2 |
3 | import com.coditory.sherlock.BlockingRxSherlockWrapper
4 | import com.coditory.sherlock.Sherlock
5 | import com.coditory.sherlock.base.DistributedLocksCreator
6 |
7 | import java.time.Clock
8 | import java.time.Duration
9 |
10 | trait UsesReactiveInMemorySherlock implements DistributedLocksCreator {
11 | @Override
12 | Sherlock createSherlock(String instanceId, Duration duration, Clock clock, String collectionName) {
13 | com.coditory.sherlock.rxjava.Sherlock reactiveLocks = InMemorySherlock.builder()
14 | .withOwnerId(instanceId)
15 | .withLockDuration(duration)
16 | .withClock(clock)
17 | .build()
18 | return new BlockingRxSherlockWrapper(reactiveLocks)
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/connectors/inmem/sync/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | id("build.publish")
4 | id("build.coverage")
5 | }
6 |
7 | dependencies {
8 | api(projects.api.apiSync)
9 | implementation(projects.connectors.inmem.inmemCommon)
10 | integrationTestImplementation(projects.tests)
11 | }
12 |
--------------------------------------------------------------------------------
/connectors/inmem/sync/src/integrationTest/groovy/com/coditory/sherlock/inmem/InMemoryDistributedLockSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.inmem
2 |
3 | import com.coditory.sherlock.AcquireLockMultipleTimesSpec
4 | import com.coditory.sherlock.AcquireLockSpec
5 | import com.coditory.sherlock.InfiniteAcquireLockSpec
6 | import com.coditory.sherlock.ReleaseLockSpec
7 |
8 | class InMemoryReleaseLockSpec extends ReleaseLockSpec
9 | implements UsesInMemorySherlock {}
10 |
11 | class InMemoryAcquireLockSpec extends AcquireLockSpec
12 | implements UsesInMemorySherlock {}
13 |
14 | class InMemoryAcquireLockMultipleTimesSpec extends AcquireLockMultipleTimesSpec
15 | implements UsesInMemorySherlock {}
16 |
17 | class InMemoryInfiniteAcquireLockSpec extends InfiniteAcquireLockSpec
18 | implements UsesInMemorySherlock {}
19 |
--------------------------------------------------------------------------------
/connectors/inmem/sync/src/integrationTest/groovy/com/coditory/sherlock/inmem/UsesInMemorySherlock.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.inmem
2 |
3 | import com.coditory.sherlock.Sherlock
4 | import com.coditory.sherlock.base.DistributedLocksCreator
5 |
6 | import java.time.Clock
7 | import java.time.Duration
8 |
9 | import static InMemorySherlock.builder
10 |
11 | trait UsesInMemorySherlock implements DistributedLocksCreator {
12 | @Override
13 | Sherlock createSherlock(String instanceId, Duration duration, Clock clock, String collectionName) {
14 | return builder()
15 | .withOwnerId(instanceId)
16 | .withLockDuration(duration)
17 | .withClock(clock)
18 | .build()
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/connectors/mongo/common/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // This module is NOT a public API - imported as implementation
2 |
3 | plugins {
4 | id("build.java")
5 | id("build.publish")
6 | id("build.coverage")
7 | }
8 |
9 | dependencies {
10 | api(projects.common)
11 | api(libs.mongodb.core)
12 | }
13 |
--------------------------------------------------------------------------------
/connectors/mongo/coroutines/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.kotlin")
3 | id("build.publish")
4 | id("build.coverage")
5 | }
6 |
7 | dependencies {
8 | api(projects.api.apiCoroutines)
9 | api(projects.api.apiCoroutinesConnector)
10 | api(libs.mongodb.coroutine)
11 | implementation(projects.connectors.mongo.mongoCommon)
12 | integrationTestImplementation(projects.connectors.mongo.mongoTests)
13 | }
14 |
--------------------------------------------------------------------------------
/connectors/mongo/coroutines/src/integrationTest/groovy/com/coditory/sherlock/mongo/coroutines/MongoClientHolder.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.mongo.coroutines
2 |
3 | import com.coditory.sherlock.mongo.MongoHolder
4 | import com.mongodb.kotlin.client.coroutine.MongoClient
5 | import groovy.transform.CompileStatic
6 |
7 | @CompileStatic
8 | class MongoClientHolder {
9 | private static MongoClient mongoClient
10 |
11 | synchronized static MongoClient getClient() {
12 | if (mongoClient != null) return mongoClient
13 | MongoHolder.startDb()
14 | String url = MongoHolder.getConnectionString()
15 | mongoClient = MongoClient.@Factory.create(url)
16 | return mongoClient
17 | }
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/connectors/mongo/coroutines/src/integrationTest/groovy/com/coditory/sherlock/mongo/coroutines/MongoDbSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.mongo.coroutines
2 |
3 | import com.coditory.sherlock.mongo.MongoIndexCreationSpec
4 | import com.coditory.sherlock.mongo.MongoLockStorageSpec
5 |
6 | class KtMongoIndexCreationSpec extends MongoIndexCreationSpec
7 | implements UsesKtMongoSherlock {}
8 |
9 | class KtMongoLockStorageSpec extends MongoLockStorageSpec
10 | implements UsesKtMongoSherlock {}
11 |
--------------------------------------------------------------------------------
/connectors/mongo/coroutines/src/integrationTest/groovy/com/coditory/sherlock/mongo/coroutines/MongoDistributedLockSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.mongo.coroutines
2 |
3 | import com.coditory.sherlock.AcquireLockMultipleTimesSpec
4 | import com.coditory.sherlock.AcquireLockSpec
5 | import com.coditory.sherlock.HandleDbFailureSpec
6 | import com.coditory.sherlock.InfiniteAcquireLockSpec
7 | import com.coditory.sherlock.ReleaseLockSpec
8 |
9 | class KtMongoReleaseLockSpec extends ReleaseLockSpec
10 | implements UsesKtMongoSherlock {}
11 |
12 | class KtMongoAcquireLockSpec extends AcquireLockSpec
13 | implements UsesKtMongoSherlock {}
14 |
15 | class KtMongoAcquireLockMultipleTimesSpec extends AcquireLockMultipleTimesSpec
16 | implements UsesKtMongoSherlock {}
17 |
18 | class KtMongoInfiniteAcquireLockSpec extends InfiniteAcquireLockSpec
19 | implements UsesKtMongoSherlock {}
20 |
21 | class KtMongoHandleDbFailureSpec extends HandleDbFailureSpec
22 | implements UsesKtMongoSherlock {}
23 |
--------------------------------------------------------------------------------
/connectors/mongo/coroutines/src/integrationTest/groovy/com/coditory/sherlock/mongo/coroutines/UsesKtMongoSherlock.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.mongo.coroutines
2 |
3 | import com.coditory.sherlock.BlockingKtSherlockWrapper
4 | import com.coditory.sherlock.Sherlock
5 | import com.coditory.sherlock.base.DatabaseManager
6 | import com.coditory.sherlock.base.DistributedLocksCreator
7 | import com.coditory.sherlock.mongo.MongoHolder
8 | import com.mongodb.kotlin.client.coroutine.MongoCollection
9 | import org.bson.Document
10 |
11 | import java.time.Clock
12 | import java.time.Duration
13 |
14 | trait UsesKtMongoSherlock implements DistributedLocksCreator, DatabaseManager {
15 | @Override
16 | Sherlock createSherlock(String instanceId, Duration duration, Clock clock, String collectionName) {
17 | MongoCollection collection = MongoOperations.INSTANCE.getLocksCollection(
18 | MongoClientHolder.getClient(), MongoHolder.databaseName, collectionName)
19 | com.coditory.sherlock.coroutines.Sherlock coroutinesLocks = MongoSherlock.builder()
20 | .withLocksCollection(collection)
21 | .withOwnerId(instanceId)
22 | .withLockDuration(duration)
23 | .withClock(clock)
24 | .build()
25 | return new BlockingKtSherlockWrapper(coroutinesLocks)
26 | }
27 |
28 | @Override
29 | void stopDatabase() {
30 | MongoHolder.stopDb()
31 | }
32 |
33 | @Override
34 | void startDatabase() {
35 | MongoHolder.startDb()
36 | }
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/connectors/mongo/coroutines/src/main/kotlin/com/coditory/sherlock/mongo/coroutines/MongoCollectionInitializer.kt:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.mongo.coroutines
2 |
3 | import com.coditory.sherlock.mongo.MongoDistributedLock.INDEX
4 | import com.coditory.sherlock.mongo.MongoDistributedLock.INDEX_OPTIONS
5 | import com.mongodb.kotlin.client.coroutine.MongoCollection
6 | import org.bson.Document
7 | import java.util.concurrent.atomic.AtomicBoolean
8 |
9 | internal class MongoCollectionInitializer(
10 | private val collection: MongoCollection,
11 | ) {
12 | private val indexesCreated = AtomicBoolean(false)
13 |
14 | suspend fun getInitializedCollection(): MongoCollection {
15 | val shouldCreateIndexes = indexesCreated.compareAndSet(false, true)
16 | if (!shouldCreateIndexes) {
17 | return collection
18 | }
19 | collection.createIndex(INDEX, INDEX_OPTIONS)
20 | return collection
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/connectors/mongo/coroutines/src/main/kotlin/com/coditory/sherlock/mongo/coroutines/MongoOperations.kt:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.mongo.coroutines
2 |
3 | import com.mongodb.kotlin.client.coroutine.MongoClient
4 | import com.mongodb.kotlin.client.coroutine.MongoCollection
5 | import org.bson.Document
6 |
7 | // Used for tests, as some kotlin specific constructs don't work with groovy.
8 | internal object MongoOperations {
9 | @JvmStatic
10 | fun getLocksCollection(
11 | mongoClient: MongoClient,
12 | databaseName: String,
13 | collectionName: String,
14 | ): MongoCollection {
15 | return mongoClient
16 | .getDatabase(databaseName)
17 | .getCollection(collectionName)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/connectors/mongo/reactor/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | id("build.publish")
4 | id("build.coverage")
5 | }
6 |
7 | dependencies {
8 | api(projects.api.apiReactor)
9 | api(libs.mongodb.reactivestreams)
10 | implementation(projects.connectors.mongo.mongoCommon)
11 | integrationTestImplementation(projects.connectors.mongo.mongoTests)
12 | }
13 |
--------------------------------------------------------------------------------
/connectors/mongo/reactor/src/integrationTest/groovy/com/coditory/sherlock/mongo/reactor/MongoClientHolder.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.mongo.reactor
2 |
3 | import com.coditory.sherlock.mongo.MongoHolder
4 | import com.mongodb.reactivestreams.client.MongoClient
5 | import com.mongodb.reactivestreams.client.MongoClients
6 | import groovy.transform.CompileStatic
7 |
8 | @CompileStatic
9 | class MongoClientHolder {
10 | private static MongoClient mongoClient
11 |
12 | synchronized static MongoClient getClient() {
13 | if (mongoClient != null) return mongoClient
14 | MongoHolder.startDb()
15 | String url = MongoHolder.getConnectionString()
16 | mongoClient = MongoClients.create(url)
17 | return mongoClient
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/connectors/mongo/reactor/src/integrationTest/groovy/com/coditory/sherlock/mongo/reactor/MongoDbSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.mongo.reactor
2 |
3 | import com.coditory.sherlock.mongo.MongoIndexCreationSpec
4 | import com.coditory.sherlock.mongo.MongoLockStorageSpec
5 |
6 | class ReactorMongoIndexCreationSpec extends MongoIndexCreationSpec
7 | implements UsesReactorMongoSherlock {}
8 |
9 | class ReactorMongoLockStorageSpec extends MongoLockStorageSpec
10 | implements UsesReactorMongoSherlock {}
11 |
--------------------------------------------------------------------------------
/connectors/mongo/reactor/src/integrationTest/groovy/com/coditory/sherlock/mongo/reactor/MongoDistributedLockSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.mongo.reactor
2 |
3 |
4 | import com.coditory.sherlock.AcquireLockMultipleTimesSpec
5 | import com.coditory.sherlock.AcquireLockSpec
6 | import com.coditory.sherlock.HandleDbFailureSpec
7 | import com.coditory.sherlock.InfiniteAcquireLockSpec
8 | import com.coditory.sherlock.ReleaseLockSpec
9 |
10 | class ReactorMongoReleaseLockSpec extends ReleaseLockSpec
11 | implements UsesReactorMongoSherlock {}
12 |
13 | class ReactorMongoAcquireLockSpec extends AcquireLockSpec
14 | implements UsesReactorMongoSherlock {}
15 |
16 | class ReactorMongoAcquireLockMultipleTimesSpec extends AcquireLockMultipleTimesSpec
17 | implements UsesReactorMongoSherlock {}
18 |
19 | class ReactorMongoInfiniteAcquireLockSpec extends InfiniteAcquireLockSpec
20 | implements UsesReactorMongoSherlock {}
21 |
22 | class ReactorMongoHandleDbFailureSpec extends HandleDbFailureSpec
23 | implements UsesReactorMongoSherlock {}
24 |
--------------------------------------------------------------------------------
/connectors/mongo/reactor/src/integrationTest/groovy/com/coditory/sherlock/mongo/reactor/UsesReactorMongoSherlock.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.mongo.reactor
2 |
3 | import com.coditory.sherlock.BlockingReactorSherlockWrapper
4 | import com.coditory.sherlock.Sherlock
5 | import com.coditory.sherlock.base.DatabaseManager
6 | import com.coditory.sherlock.base.DistributedLocksCreator
7 | import com.coditory.sherlock.mongo.MongoHolder
8 | import com.mongodb.reactivestreams.client.MongoCollection
9 | import org.bson.Document
10 |
11 | import java.time.Clock
12 | import java.time.Duration
13 |
14 | import static com.coditory.sherlock.mongo.MongoHolder.databaseName
15 |
16 | trait UsesReactorMongoSherlock implements DistributedLocksCreator, DatabaseManager {
17 | @Override
18 | Sherlock createSherlock(String ownerId, Duration duration, Clock clock, String collectionName) {
19 | com.coditory.sherlock.reactor.Sherlock reactorLocks = MongoSherlock.builder()
20 | .withLocksCollection(getLocksCollection(collectionName))
21 | .withOwnerId(ownerId)
22 | .withLockDuration(duration)
23 | .withClock(clock)
24 | .build()
25 | return new BlockingReactorSherlockWrapper(reactorLocks)
26 | }
27 |
28 | MongoCollection getLocksCollection(String collectionName) {
29 | return MongoClientHolder.getClient()
30 | .getDatabase(databaseName)
31 | .getCollection(collectionName)
32 | }
33 |
34 | @Override
35 | void stopDatabase() {
36 | MongoHolder.stopDb()
37 | }
38 |
39 | @Override
40 | void startDatabase() {
41 | MongoHolder.startDb()
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/connectors/mongo/reactor/src/main/java/com/coditory/sherlock/mongo/reactor/MongoCollectionInitializer.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.mongo.reactor;
2 |
3 | import com.mongodb.reactivestreams.client.MongoCollection;
4 | import org.bson.Document;
5 | import reactor.core.publisher.Mono;
6 |
7 | import java.util.concurrent.atomic.AtomicBoolean;
8 |
9 | import static com.coditory.sherlock.Preconditions.expectNonNull;
10 | import static com.coditory.sherlock.mongo.MongoDistributedLock.INDEX;
11 | import static com.coditory.sherlock.mongo.MongoDistributedLock.INDEX_OPTIONS;
12 |
13 | class MongoCollectionInitializer {
14 | private final MongoCollection collection;
15 | private final AtomicBoolean indexesCreated = new AtomicBoolean(false);
16 |
17 | MongoCollectionInitializer(MongoCollection collection) {
18 | expectNonNull(collection, "collection");
19 | validateConnection(collection);
20 | this.collection = collection;
21 | }
22 |
23 | Mono> getInitializedCollection() {
24 | boolean shouldCreateIndexes = indexesCreated.compareAndSet(false, true);
25 | if (!shouldCreateIndexes) {
26 | return Mono.just(collection);
27 | }
28 | return Mono.from(collection.createIndex(INDEX, INDEX_OPTIONS))
29 | .map(result -> collection);
30 | }
31 |
32 | private void validateConnection(MongoCollection collection) {
33 | String readPreference = collection.getReadPreference().getName();
34 | if (!"primary".equalsIgnoreCase(readPreference)) {
35 | throw new IllegalArgumentException("Expected Mongo connection with readPreference=primary");
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/connectors/mongo/rxjava/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | id("build.publish")
4 | id("build.coverage")
5 | }
6 |
7 | dependencies {
8 | api(projects.api.apiRxjava)
9 | api(libs.mongodb.reactivestreams)
10 | implementation(projects.connectors.mongo.mongoCommon)
11 | integrationTestImplementation(projects.connectors.mongo.mongoTests)
12 | }
13 |
--------------------------------------------------------------------------------
/connectors/mongo/rxjava/src/integrationTest/groovy/com/coditory/sherlock/mongo/rxjava/MongoClientHolder.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.mongo.rxjava
2 |
3 | import com.coditory.sherlock.mongo.MongoHolder
4 | import com.mongodb.reactivestreams.client.MongoClient
5 | import com.mongodb.reactivestreams.client.MongoClients
6 | import groovy.transform.CompileStatic
7 |
8 | @CompileStatic
9 | class MongoClientHolder {
10 | private static MongoClient mongoClient
11 |
12 | synchronized static MongoClient getClient() {
13 | if (mongoClient != null) return mongoClient
14 | MongoHolder.startDb()
15 | String url = MongoHolder.getConnectionString()
16 | mongoClient = MongoClients.create(url)
17 | return mongoClient
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/connectors/mongo/rxjava/src/integrationTest/groovy/com/coditory/sherlock/mongo/rxjava/MongoDbSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.mongo.rxjava
2 |
3 | import com.coditory.sherlock.mongo.MongoIndexCreationSpec
4 | import com.coditory.sherlock.mongo.MongoLockStorageSpec
5 |
6 | class RxMongoIndexCreationSpec extends MongoIndexCreationSpec
7 | implements UsesRxMongoSherlock {}
8 |
9 | class RxMongoLockStorageSpec extends MongoLockStorageSpec
10 | implements UsesRxMongoSherlock {}
11 |
--------------------------------------------------------------------------------
/connectors/mongo/rxjava/src/integrationTest/groovy/com/coditory/sherlock/mongo/rxjava/MongoDistributedLockSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.mongo.rxjava
2 |
3 | import com.coditory.sherlock.AcquireLockMultipleTimesSpec
4 | import com.coditory.sherlock.AcquireLockSpec
5 | import com.coditory.sherlock.HandleDbFailureSpec
6 | import com.coditory.sherlock.InfiniteAcquireLockSpec
7 | import com.coditory.sherlock.ReleaseLockSpec
8 |
9 | class RxMongoReleaseLockSpec extends ReleaseLockSpec
10 | implements UsesRxMongoSherlock {}
11 |
12 | class RxMongoAcquireLockSpec extends AcquireLockSpec
13 | implements UsesRxMongoSherlock {}
14 |
15 | class RxMongoAcquireLockMultipleTimesSpec extends AcquireLockMultipleTimesSpec
16 | implements UsesRxMongoSherlock {}
17 |
18 | class RxMongoInfiniteAcquireLockSpec extends InfiniteAcquireLockSpec
19 | implements UsesRxMongoSherlock {}
20 |
21 | class RxMongoHandleDbFailureSpec extends HandleDbFailureSpec
22 | implements UsesRxMongoSherlock {}
23 |
--------------------------------------------------------------------------------
/connectors/mongo/rxjava/src/integrationTest/groovy/com/coditory/sherlock/mongo/rxjava/UsesRxMongoSherlock.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.mongo.rxjava
2 |
3 | import com.coditory.sherlock.BlockingRxSherlockWrapper
4 | import com.coditory.sherlock.Sherlock
5 | import com.coditory.sherlock.base.DatabaseManager
6 | import com.coditory.sherlock.base.DistributedLocksCreator
7 | import com.coditory.sherlock.mongo.MongoHolder
8 | import com.mongodb.reactivestreams.client.MongoCollection
9 | import org.bson.Document
10 |
11 | import java.time.Clock
12 | import java.time.Duration
13 |
14 | trait UsesRxMongoSherlock implements DistributedLocksCreator, DatabaseManager {
15 | @Override
16 | Sherlock createSherlock(String ownerId, Duration duration, Clock clock, String collectionName) {
17 | com.coditory.sherlock.rxjava.Sherlock reactiveLocks = MongoSherlock.builder()
18 | .withLocksCollection(getLocksCollection(collectionName))
19 | .withOwnerId(ownerId)
20 | .withLockDuration(duration)
21 | .withClock(clock)
22 | .build()
23 | return new BlockingRxSherlockWrapper(reactiveLocks)
24 | }
25 |
26 | MongoCollection getLocksCollection(String collectionName) {
27 | return MongoClientHolder.getClient()
28 | .getDatabase(MongoHolder.databaseName)
29 | .getCollection(collectionName)
30 | }
31 |
32 | @Override
33 | void stopDatabase() {
34 | MongoHolder.stopDb()
35 | }
36 |
37 | @Override
38 | void startDatabase() {
39 | MongoHolder.startDb()
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/connectors/mongo/rxjava/src/main/java/com/coditory/sherlock/mongo/rxjava/MongoCollectionInitializer.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.mongo.rxjava;
2 |
3 | import com.mongodb.reactivestreams.client.MongoCollection;
4 | import io.reactivex.rxjava3.core.Single;
5 | import org.bson.Document;
6 |
7 | import java.util.concurrent.atomic.AtomicBoolean;
8 |
9 | import static com.coditory.sherlock.Preconditions.expectNonNull;
10 | import static com.coditory.sherlock.mongo.MongoDistributedLock.INDEX;
11 | import static com.coditory.sherlock.mongo.MongoDistributedLock.INDEX_OPTIONS;
12 |
13 | class MongoCollectionInitializer {
14 | private final MongoCollection collection;
15 | private final AtomicBoolean indexesCreated = new AtomicBoolean(false);
16 |
17 | MongoCollectionInitializer(MongoCollection collection) {
18 | expectNonNull(collection, "collection");
19 | validateConnection(collection);
20 | this.collection = collection;
21 | }
22 |
23 | Single> getInitializedCollection() {
24 | boolean shouldCreateIndexes = indexesCreated.compareAndSet(false, true);
25 | if (!shouldCreateIndexes) {
26 | return Single.just(collection);
27 | }
28 | return Single.fromPublisher(collection.createIndex(INDEX, INDEX_OPTIONS))
29 | .map(result -> collection);
30 | }
31 |
32 | private void validateConnection(MongoCollection collection) {
33 | String readPreference = collection.getReadPreference().getName();
34 | if (!"primary".equalsIgnoreCase(readPreference)) {
35 | throw new IllegalArgumentException("Expected Mongo connection with readPreference=primary");
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/connectors/mongo/sync/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | id("build.publish")
4 | id("build.coverage")
5 | }
6 |
7 | dependencies {
8 | api(projects.api.apiSync)
9 | api(libs.mongodb.sync)
10 | implementation(projects.connectors.mongo.mongoCommon)
11 | integrationTestImplementation(projects.connectors.mongo.mongoTests)
12 | }
13 |
--------------------------------------------------------------------------------
/connectors/mongo/sync/src/integrationTest/groovy/com/coditory/sherlock/mongo/MongoDbSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.mongo
2 |
3 | class RxMongoIndexCreationSpec extends MongoIndexCreationSpec
4 | implements UsesMongoSherlock {}
5 |
6 | class RxMongoLockStorageSpec extends MongoLockStorageSpec
7 | implements UsesMongoSherlock {}
8 |
--------------------------------------------------------------------------------
/connectors/mongo/sync/src/integrationTest/groovy/com/coditory/sherlock/mongo/MongoDistributedLockSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.mongo
2 |
3 | import com.coditory.sherlock.AcquireLockMultipleTimesSpec
4 | import com.coditory.sherlock.AcquireLockSpec
5 | import com.coditory.sherlock.HandleDbFailureSpec
6 | import com.coditory.sherlock.InfiniteAcquireLockSpec
7 | import com.coditory.sherlock.ReleaseLockSpec
8 |
9 | class MongoReleaseLockSpec extends ReleaseLockSpec
10 | implements UsesMongoSherlock {}
11 |
12 | class MongoAcquireLockSpec extends AcquireLockSpec
13 | implements UsesMongoSherlock {}
14 |
15 | class MongoAcquireLockMultipleTimesSpec extends AcquireLockMultipleTimesSpec
16 | implements UsesMongoSherlock {}
17 |
18 | class MongoInfiniteAcquireLockSpec extends InfiniteAcquireLockSpec
19 | implements UsesMongoSherlock {}
20 |
21 | class MongoHandleDbFailureSpec extends HandleDbFailureSpec
22 | implements UsesMongoSherlock {}
23 |
--------------------------------------------------------------------------------
/connectors/mongo/sync/src/integrationTest/groovy/com/coditory/sherlock/mongo/UsesMongoSherlock.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.mongo
2 |
3 |
4 | import com.coditory.sherlock.Sherlock
5 | import com.coditory.sherlock.base.DatabaseManager
6 | import com.coditory.sherlock.base.DistributedLocksCreator
7 | import com.mongodb.client.MongoCollection
8 | import org.bson.Document
9 |
10 | import java.time.Clock
11 | import java.time.Duration
12 |
13 | trait UsesMongoSherlock implements DistributedLocksCreator, DatabaseManager {
14 | @Override
15 | Sherlock createSherlock(String instanceId, Duration duration, Clock clock, String collectionName) {
16 | return MongoSherlock.builder()
17 | .withLocksCollection(getLocksCollection(collectionName))
18 | .withOwnerId(instanceId)
19 | .withLockDuration(duration)
20 | .withClock(clock)
21 | .build()
22 | }
23 |
24 | MongoCollection getLocksCollection(String collectionName) {
25 | return MongoHolder.getClient()
26 | .getDatabase(MongoHolder.databaseName)
27 | .getCollection(collectionName)
28 | }
29 |
30 | @Override
31 | void stopDatabase() {
32 | MongoHolder.stopDb()
33 | }
34 |
35 | @Override
36 | void startDatabase() {
37 | MongoHolder.startDb()
38 | }
39 | }
40 |
41 |
--------------------------------------------------------------------------------
/connectors/mongo/sync/src/main/java/com/coditory/sherlock/mongo/MongoCollectionInitializer.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.mongo;
2 |
3 | import com.mongodb.client.MongoCollection;
4 | import org.bson.Document;
5 |
6 | import java.util.concurrent.atomic.AtomicBoolean;
7 |
8 | import static com.coditory.sherlock.Preconditions.expectNonNull;
9 | import static com.coditory.sherlock.mongo.MongoDistributedLock.INDEX;
10 | import static com.coditory.sherlock.mongo.MongoDistributedLock.INDEX_OPTIONS;
11 |
12 | class MongoCollectionInitializer {
13 | private final MongoCollection collection;
14 | private final AtomicBoolean indexesCreated = new AtomicBoolean(false);
15 |
16 | MongoCollectionInitializer(MongoCollection collection) {
17 | expectNonNull(collection, "collection");
18 | this.collection = collection;
19 | }
20 |
21 | MongoCollection getInitializedCollection() {
22 | boolean shouldCreateIndexes = indexesCreated.compareAndSet(false, true);
23 | if (!shouldCreateIndexes) {
24 | return collection;
25 | }
26 | collection.createIndex(INDEX, INDEX_OPTIONS);
27 | return collection;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/connectors/mongo/tests/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | }
4 |
5 | dependencies {
6 | api(projects.common)
7 | api(projects.tests)
8 | api(projects.connectors.mongo.mongoCommon)
9 | api(libs.mongodb.sync)
10 | api(libs.spock.core)
11 | api(libs.jsonassert)
12 | implementation(libs.testcontainers.mongodb)
13 | }
14 |
--------------------------------------------------------------------------------
/connectors/sql/common-api/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | id("build.publish")
4 | id("build.coverage")
5 | }
6 |
7 | dependencies {
8 | implementation(projects.common)
9 | }
10 |
--------------------------------------------------------------------------------
/connectors/sql/common-api/src/main/java/com/coditory/sherlock/sql/BindingMapper.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 |
5 | import static com.coditory.sherlock.Preconditions.expect;
6 | import static com.coditory.sherlock.Preconditions.expectNonNull;
7 |
8 | public interface BindingMapper {
9 | BindingMapper ORDERED_QUESTION_MARK = (param) -> new BindingParameterMapping("?", param.getIndex());
10 | BindingMapper INDEXED_QUESTION_MARK = (param) -> {
11 | int oneBasedIndex = param.getIndex() + 1;
12 | String value = "$" + oneBasedIndex;
13 | return new BindingParameterMapping(value, value);
14 | };
15 | BindingMapper AT_NAME_MARK = (param) -> {
16 | String queryKey = "@" + param.getName();
17 | return new BindingParameterMapping(queryKey, param.getName());
18 | };
19 | BindingMapper MYSQL_MAPPER = ORDERED_QUESTION_MARK;
20 | BindingMapper POSTGRES_MAPPER = INDEXED_QUESTION_MARK;
21 | BindingMapper H2_MAPPER = INDEXED_QUESTION_MARK;
22 | BindingMapper MSSQL_MAPPER = AT_NAME_MARK;
23 | BindingMapper SPANNER_MAPPER = AT_NAME_MARK;
24 | BindingMapper JDBC_MAPPER = ORDERED_QUESTION_MARK;
25 |
26 | @NotNull
27 | default BindingParameterMapping mapBinding(int index, @NotNull String name) {
28 | expect(index >= 0, "Expected index >= 0. Got: %d", index);
29 | expectNonNull(name, "name");
30 | return mapBinding(new BindingParameter(index, name));
31 | }
32 |
33 | @NotNull
34 | BindingParameterMapping mapBinding(@NotNull BindingParameter bindingParameter);
35 | }
36 |
--------------------------------------------------------------------------------
/connectors/sql/common-api/src/main/java/com/coditory/sherlock/sql/BindingParameter.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 |
5 | import java.util.Objects;
6 |
7 | import static com.coditory.sherlock.Preconditions.expect;
8 | import static com.coditory.sherlock.Preconditions.expectNonNull;
9 |
10 | public final class BindingParameter {
11 | private final int index;
12 | private final String name;
13 |
14 | public BindingParameter(int index, @NotNull String name) {
15 | expect(index >= 0, "Expected index >= 0. Got: %d", index);
16 | expectNonNull(name, "name");
17 | this.index = index;
18 | this.name = name;
19 | }
20 |
21 | public int getIndex() {
22 | return index;
23 | }
24 |
25 | @NotNull
26 | public String getName() {
27 | return name;
28 | }
29 |
30 | @Override
31 | public boolean equals(Object o) {
32 | if (this == o) return true;
33 | if (o == null || getClass() != o.getClass()) return false;
34 | BindingParameter that = (BindingParameter) o;
35 | return index == that.index && Objects.equals(name, that.name);
36 | }
37 |
38 | @Override
39 | public int hashCode() {
40 | return Objects.hash(index, name);
41 | }
42 |
43 | @Override
44 | public String toString() {
45 | return "BindingParameter{" +
46 | "index=" + index +
47 | ", name='" + name + '\'' +
48 | '}';
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/connectors/sql/common-api/src/main/java/com/coditory/sherlock/sql/BindingParameterMapping.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 |
5 | import java.util.Objects;
6 |
7 | import static com.coditory.sherlock.Preconditions.expectNonEmpty;
8 | import static com.coditory.sherlock.Preconditions.expectNonNull;
9 |
10 | public final class BindingParameterMapping {
11 | private final String queryMarker;
12 | private final Object bindingKey;
13 |
14 | public BindingParameterMapping(@NotNull String queryMarker, @NotNull Object bindingKey) {
15 | expectNonEmpty(queryMarker, "queryMarker");
16 | expectNonNull(bindingKey, "bindingKey");
17 | this.queryMarker = queryMarker;
18 | this.bindingKey = bindingKey;
19 | }
20 |
21 | @NotNull
22 | public String getQueryMarker() {
23 | return queryMarker;
24 | }
25 |
26 | @NotNull
27 | public Object getBindingKey() {
28 | return bindingKey;
29 | }
30 |
31 | @Override
32 | public boolean equals(Object o) {
33 | if (this == o) return true;
34 | if (o == null || getClass() != o.getClass()) return false;
35 | BindingParameterMapping that = (BindingParameterMapping) o;
36 | return Objects.equals(queryMarker, that.queryMarker) && Objects.equals(bindingKey, that.bindingKey);
37 | }
38 |
39 | @Override
40 | public int hashCode() {
41 | return Objects.hash(queryMarker, bindingKey);
42 | }
43 |
44 | @Override
45 | public String toString() {
46 | return "BindingParameter{" +
47 | "queryMarker='" + queryMarker + '\'' +
48 | ", bindingKey=" + bindingKey +
49 | '}';
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/connectors/sql/common/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // This module is NOT a public API - imported as implementation
2 |
3 | plugins {
4 | id("build.java")
5 | id("build.publish")
6 | id("build.coverage")
7 | }
8 |
9 | dependencies {
10 | api(projects.common)
11 | api(projects.connectors.sql.sqlCommonApi)
12 | }
13 |
--------------------------------------------------------------------------------
/connectors/sql/common/src/main/java/com/coditory/sherlock/sql/PrecomputedBindingParameterMapper.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 |
5 | import java.util.HashMap;
6 | import java.util.Map;
7 |
8 | import static com.coditory.sherlock.Preconditions.expectNonNull;
9 |
10 | public final class PrecomputedBindingParameterMapper implements BindingMapper {
11 | @NotNull
12 | public static PrecomputedBindingParameterMapper from(@NotNull BindingMapper mapper) {
13 | expectNonNull(mapper, "mapper");
14 | Map result = new HashMap<>();
15 | for (String param : SqlLockNamedQueriesTemplate.ParameterNames.ALL_PARAMS) {
16 | for (int i = 0; i < 10; ++i) {
17 | BindingParameter bindingParameter = new BindingParameter(i, param);
18 | BindingParameterMapping mapping = mapper.mapBinding(bindingParameter);
19 | result.put(bindingParameter, mapping);
20 | }
21 | }
22 | return new PrecomputedBindingParameterMapper(result);
23 | }
24 |
25 | private final Map mapping;
26 |
27 | private PrecomputedBindingParameterMapper(Map mapping) {
28 | this.mapping = mapping;
29 | }
30 |
31 | @Override
32 | @NotNull
33 | public BindingParameterMapping mapBinding(@NotNull BindingParameter bindingParameter) {
34 | BindingParameterMapping result = mapping.get(bindingParameter);
35 | if (result == null) {
36 | throw new IllegalArgumentException("Could not find precomputed binding parameter for: " + bindingParameter);
37 | }
38 | return result;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/connectors/sql/coroutines/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.kotlin")
3 | id("build.publish")
4 | id("build.coverage")
5 | }
6 |
7 | dependencies {
8 | api(projects.api.apiCoroutines)
9 | api(libs.r2dbc.spi)
10 | api(projects.connectors.sql.sqlCommonApi)
11 | api(projects.api.apiCoroutinesConnector)
12 | implementation(projects.connectors.sql.sqlCommon)
13 | implementation(libs.kotlinx.coroutines.core)
14 | implementation(libs.kotlinx.coroutines.reactive)
15 | integrationTestImplementation(projects.connectors.sql.sqlTests)
16 | integrationTestImplementation(libs.r2dbc.pool)
17 | // integration: postgres
18 | integrationTestImplementation(libs.r2dbc.postgresql)
19 | integrationTestImplementation(libs.testcontainers.postgresql)
20 | // integration: mysql
21 | integrationTestImplementation(libs.r2dbc.mysql)
22 | integrationTestImplementation(libs.testcontainers.mysql)
23 | }
24 |
--------------------------------------------------------------------------------
/connectors/sql/coroutines/src/integrationTest/groovy/com/coditory/sherlock/sql/coroutines/MySqlConnectionPoolHolder.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql.coroutines
2 |
3 | import com.coditory.sherlock.sql.MySqlHolder
4 | import groovy.transform.CompileStatic
5 | import io.r2dbc.spi.ConnectionFactories
6 | import io.r2dbc.spi.ConnectionFactory
7 | import io.r2dbc.spi.ConnectionFactoryOptions
8 | import io.r2dbc.spi.Option
9 |
10 | @CompileStatic
11 | class MySqlConnectionPoolHolder {
12 | private static ConnectionFactory connectionFactory = null
13 |
14 | synchronized static ConnectionFactory getConnectionFactory() {
15 | MySqlHolder.startDb()
16 | if (connectionFactory != null) {
17 | return connectionFactory
18 | }
19 | ConnectionFactoryOptions options = ConnectionFactoryOptions
20 | .parse(MySqlHolder.getJdbcUrl().replace("jdbc:", "r2dbc:"))
21 | .mutate()
22 | .option(Option.valueOf("connectionTimeZone"), "UTC")
23 | .option(ConnectionFactoryOptions.USER, MySqlHolder.getUsername())
24 | .option(ConnectionFactoryOptions.PASSWORD, MySqlHolder.getPassword())
25 | .option(ConnectionFactoryOptions.DATABASE, MySqlHolder.getDatabaseName())
26 | .build()
27 | connectionFactory = ConnectionFactories.get(options)
28 | return connectionFactory
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/connectors/sql/coroutines/src/integrationTest/groovy/com/coditory/sherlock/sql/coroutines/MySqlDbSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql.coroutines
2 |
3 | import com.coditory.sherlock.sql.SqlIndexCreationSpec
4 | import com.coditory.sherlock.sql.SqlLockStorageSpec
5 |
6 | class KtMySqlLockStorageSpec extends SqlLockStorageSpec
7 | implements UsesKtSqlSherlock, MySqlConnectionProvider {}
8 |
9 | class KtMySqlIndexCreationSpec extends SqlIndexCreationSpec
10 | implements UsesKtSqlSherlock, MySqlConnectionProvider {
11 | List expectedIndexNames = ["PRIMARY", tableName + "_IDX"].sort()
12 | }
13 |
--------------------------------------------------------------------------------
/connectors/sql/coroutines/src/integrationTest/groovy/com/coditory/sherlock/sql/coroutines/MySqlDistributedLockSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql.coroutines
2 |
3 | import com.coditory.sherlock.AcquireLockMultipleTimesSpec
4 | import com.coditory.sherlock.AcquireLockSpec
5 | import com.coditory.sherlock.HandleDbFailureSpec
6 | import com.coditory.sherlock.InfiniteAcquireLockSpec
7 | import com.coditory.sherlock.ReleaseLockSpec
8 |
9 | class MySqlReleaseLockSpec extends ReleaseLockSpec
10 | implements UsesKtSqlSherlock, MySqlConnectionProvider {}
11 |
12 | class MySqlAcquireLockSpec extends AcquireLockSpec
13 | implements UsesKtSqlSherlock, MySqlConnectionProvider {}
14 |
15 | class MySqlAcquireLockMultipleTimesSpec extends AcquireLockMultipleTimesSpec
16 | implements UsesKtSqlSherlock, MySqlConnectionProvider {}
17 |
18 | class MySqlInfiniteAcquireLockSpec extends InfiniteAcquireLockSpec
19 | implements UsesKtSqlSherlock, MySqlConnectionProvider {}
20 |
21 | class MySqlHandleDbFailureSpec extends HandleDbFailureSpec
22 | implements UsesKtSqlSherlock, MySqlConnectionProvider {}
23 |
--------------------------------------------------------------------------------
/connectors/sql/coroutines/src/integrationTest/groovy/com/coditory/sherlock/sql/coroutines/PostgresDbSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql.coroutines
2 |
3 | import com.coditory.sherlock.sql.SqlIndexCreationSpec
4 | import com.coditory.sherlock.sql.SqlLockStorageSpec
5 |
6 | class KtPostgresLockStorageSpec extends SqlLockStorageSpec
7 | implements UsesKtSqlSherlock, PostgresConnectionProvider {}
8 |
9 | class KtPostgresIndexCreationSpec extends SqlIndexCreationSpec
10 | implements UsesKtSqlSherlock, PostgresConnectionProvider {
11 | List expectedIndexNames = [tableName + "_pkey", tableName + "_idx"].sort()
12 | }
13 |
--------------------------------------------------------------------------------
/connectors/sql/coroutines/src/integrationTest/groovy/com/coditory/sherlock/sql/coroutines/PostgresDistributedLockSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql.coroutines
2 |
3 | import com.coditory.sherlock.AcquireLockMultipleTimesSpec
4 | import com.coditory.sherlock.AcquireLockSpec
5 | import com.coditory.sherlock.HandleDbFailureSpec
6 | import com.coditory.sherlock.InfiniteAcquireLockSpec
7 | import com.coditory.sherlock.ReleaseLockSpec
8 |
9 | class PostgresReleaseLockSpec extends ReleaseLockSpec
10 | implements UsesKtSqlSherlock, PostgresConnectionProvider {}
11 |
12 | class PostgresAcquireLockSpec extends AcquireLockSpec
13 | implements UsesKtSqlSherlock, PostgresConnectionProvider {}
14 |
15 | class PostgresAcquireLockMultipleTimesSpec extends AcquireLockMultipleTimesSpec
16 | implements UsesKtSqlSherlock, PostgresConnectionProvider {}
17 |
18 | class PostgresInfiniteAcquireLockSpec extends InfiniteAcquireLockSpec
19 | implements UsesKtSqlSherlock, PostgresConnectionProvider {}
20 |
21 | class PostgresHandleDbFailureSpec extends HandleDbFailureSpec
22 | implements UsesKtSqlSherlock, PostgresConnectionProvider {}
23 |
--------------------------------------------------------------------------------
/connectors/sql/coroutines/src/integrationTest/groovy/com/coditory/sherlock/sql/coroutines/UsesKtSqlSherlock.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql.coroutines
2 |
3 | import com.coditory.sherlock.BlockingKtSherlockWrapper
4 | import com.coditory.sherlock.Sherlock
5 | import com.coditory.sherlock.base.DistributedLocksCreator
6 | import com.coditory.sherlock.sql.BindingMapper
7 | import io.r2dbc.spi.ConnectionFactory
8 |
9 | import java.sql.Connection
10 | import java.time.Clock
11 | import java.time.Duration
12 |
13 | trait UsesKtSqlSherlock implements DistributedLocksCreator {
14 | abstract ConnectionFactory getConnectionFactory()
15 |
16 | abstract Connection getBlockingConnection()
17 |
18 | abstract BindingMapper getBindingMapper()
19 |
20 | @Override
21 | Sherlock createSherlock(String instanceId, Duration duration, Clock clock, String tableName) {
22 | com.coditory.sherlock.coroutines.Sherlock coroutineSqlSherlock = SqlSherlock.builder()
23 | .withConnectionFactory(connectionFactory)
24 | .withBindingMapper(bindingMapper)
25 | .withLocksTable(tableName)
26 | .withOwnerId(instanceId)
27 | .withLockDuration(duration)
28 | .withClock(clock)
29 | .build()
30 | return new BlockingKtSherlockWrapper(coroutineSqlSherlock)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/connectors/sql/coroutines/src/main/kotlin/com/coditory/sherlock/sql/coroutines/ConnectionExt.kt:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql.coroutines
2 |
3 | import io.r2dbc.spi.Connection
4 | import kotlinx.coroutines.reactive.awaitFirstOrNull
5 |
6 | suspend inline fun Connection.use(block: (Connection) -> R): R {
7 | var exception: Throwable? = null
8 | try {
9 | return block(this)
10 | } catch (e: Throwable) {
11 | exception = e
12 | throw e
13 | } finally {
14 | when (exception) {
15 | null -> close().awaitFirstOrNull()
16 |
17 | else ->
18 | try {
19 | close().awaitFirstOrNull()
20 | } catch (closeException: Throwable) {
21 | // cause.addSuppressed(closeException) // ignored here
22 | }
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/connectors/sql/reactor/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | id("build.publish")
4 | id("build.coverage")
5 | }
6 |
7 | dependencies {
8 | api(projects.api.apiReactor)
9 | api(libs.r2dbc.spi)
10 | api(projects.connectors.sql.sqlCommonApi)
11 | implementation(projects.connectors.sql.sqlCommon)
12 | integrationTestImplementation(projects.connectors.sql.sqlTests)
13 | integrationTestImplementation(libs.r2dbc.pool)
14 | // integration: postgres
15 | integrationTestImplementation(libs.r2dbc.postgresql)
16 | integrationTestImplementation(libs.testcontainers.postgresql)
17 | // integration: mysql
18 | integrationTestImplementation(libs.r2dbc.mysql)
19 | integrationTestImplementation(libs.testcontainers.mysql)
20 | }
21 |
--------------------------------------------------------------------------------
/connectors/sql/reactor/src/integrationTest/groovy/com/coditory/sherlock/sql/reactor/MySqlConnectionPoolHolder.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql.reactor
2 |
3 | import com.coditory.sherlock.sql.MySqlHolder
4 | import groovy.transform.CompileStatic
5 | import io.r2dbc.spi.ConnectionFactories
6 | import io.r2dbc.spi.ConnectionFactory
7 | import io.r2dbc.spi.ConnectionFactoryOptions
8 | import io.r2dbc.spi.Option
9 |
10 | @CompileStatic
11 | class MySqlConnectionPoolHolder {
12 | private static ConnectionFactory connectionFactory = null
13 |
14 | synchronized static ConnectionFactory getConnectionFactory() {
15 | MySqlHolder.startDb()
16 | if (connectionFactory != null) {
17 | return connectionFactory
18 | }
19 | ConnectionFactoryOptions options = ConnectionFactoryOptions
20 | .parse(MySqlHolder.getJdbcUrl().replace("jdbc:", "r2dbc:"))
21 | .mutate()
22 | .option(Option.valueOf("connectionTimeZone"), "UTC")
23 | .option(ConnectionFactoryOptions.USER, MySqlHolder.getUsername())
24 | .option(ConnectionFactoryOptions.PASSWORD, MySqlHolder.getPassword())
25 | .option(ConnectionFactoryOptions.DATABASE, MySqlHolder.getDatabaseName())
26 | .build()
27 | connectionFactory = ConnectionFactories.get(options)
28 | return connectionFactory
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/connectors/sql/reactor/src/integrationTest/groovy/com/coditory/sherlock/sql/reactor/MySqlDbSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql.reactor
2 |
3 | import com.coditory.sherlock.sql.SqlIndexCreationSpec
4 | import com.coditory.sherlock.sql.SqlLockStorageSpec
5 |
6 | class ReactorMySqlLockStorageSpec extends SqlLockStorageSpec
7 | implements UsesReactorSqlSherlock, MySqlConnectionProvider {}
8 |
9 | class ReactorMySqlIndexCreationSpec extends SqlIndexCreationSpec
10 | implements UsesReactorSqlSherlock, MySqlConnectionProvider {
11 | List expectedIndexNames = ["PRIMARY", tableName + "_IDX"].sort()
12 | }
13 |
--------------------------------------------------------------------------------
/connectors/sql/reactor/src/integrationTest/groovy/com/coditory/sherlock/sql/reactor/MySqlDistributedLockSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql.reactor
2 |
3 | import com.coditory.sherlock.AcquireLockMultipleTimesSpec
4 | import com.coditory.sherlock.AcquireLockSpec
5 | import com.coditory.sherlock.HandleDbFailureSpec
6 | import com.coditory.sherlock.InfiniteAcquireLockSpec
7 | import com.coditory.sherlock.ReleaseLockSpec
8 |
9 | class ReactorMySqlReleaseLockSpec extends ReleaseLockSpec
10 | implements UsesReactorSqlSherlock, MySqlConnectionProvider {}
11 |
12 | class ReactorMySqlAcquireLockSpec extends AcquireLockSpec
13 | implements UsesReactorSqlSherlock, MySqlConnectionProvider {}
14 |
15 | class ReactorMySqlAcquireLockMultipleTimesSpec extends AcquireLockMultipleTimesSpec
16 | implements UsesReactorSqlSherlock, MySqlConnectionProvider {}
17 |
18 | class ReactorMySqlInfiniteAcquireLockSpec extends InfiniteAcquireLockSpec
19 | implements UsesReactorSqlSherlock, MySqlConnectionProvider {}
20 |
21 | class ReactorMySqlHandleDbFailureSpec extends HandleDbFailureSpec
22 | implements UsesReactorSqlSherlock, MySqlConnectionProvider {}
23 |
--------------------------------------------------------------------------------
/connectors/sql/reactor/src/integrationTest/groovy/com/coditory/sherlock/sql/reactor/PostgresDbSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql.reactor
2 |
3 | import com.coditory.sherlock.sql.SqlIndexCreationSpec
4 | import com.coditory.sherlock.sql.SqlLockStorageSpec
5 |
6 | class ReactorPostgresLockStorageSpec extends SqlLockStorageSpec
7 | implements UsesReactorSqlSherlock, PostgresConnectionProvider {}
8 |
9 | class ReactorPostgresIndexCreationSpec extends SqlIndexCreationSpec
10 | implements UsesReactorSqlSherlock, PostgresConnectionProvider {
11 | List expectedIndexNames = [tableName + "_pkey", tableName + "_idx"].sort()
12 | }
13 |
--------------------------------------------------------------------------------
/connectors/sql/reactor/src/integrationTest/groovy/com/coditory/sherlock/sql/reactor/PostgresDistributedLockSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql.reactor
2 |
3 | import com.coditory.sherlock.AcquireLockMultipleTimesSpec
4 | import com.coditory.sherlock.AcquireLockSpec
5 | import com.coditory.sherlock.HandleDbFailureSpec
6 | import com.coditory.sherlock.InfiniteAcquireLockSpec
7 | import com.coditory.sherlock.ReleaseLockSpec
8 |
9 | class ReactorPostgresReleaseLockSpec extends ReleaseLockSpec
10 | implements UsesReactorSqlSherlock, PostgresConnectionProvider {}
11 |
12 | class ReactorPostgresAcquireLockSpec extends AcquireLockSpec
13 | implements UsesReactorSqlSherlock, PostgresConnectionProvider {}
14 |
15 | class ReactorPostgresAcquireLockMultipleTimesSpec extends AcquireLockMultipleTimesSpec
16 | implements UsesReactorSqlSherlock, PostgresConnectionProvider {}
17 |
18 | class ReactorPostgresInfiniteAcquireLockSpec extends InfiniteAcquireLockSpec
19 | implements UsesReactorSqlSherlock, PostgresConnectionProvider {}
20 |
21 | class ReactorPostgresHandleDbFailureSpec extends HandleDbFailureSpec
22 | implements UsesReactorSqlSherlock, PostgresConnectionProvider {}
23 |
--------------------------------------------------------------------------------
/connectors/sql/reactor/src/integrationTest/groovy/com/coditory/sherlock/sql/reactor/UsesReactorSqlSherlock.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql.reactor
2 |
3 | import com.coditory.sherlock.BlockingReactorSherlockWrapper
4 | import com.coditory.sherlock.Sherlock
5 | import com.coditory.sherlock.base.DistributedLocksCreator
6 | import com.coditory.sherlock.sql.BindingMapper
7 | import io.r2dbc.spi.ConnectionFactory
8 |
9 | import java.sql.Connection
10 | import java.time.Clock
11 | import java.time.Duration
12 |
13 | trait UsesReactorSqlSherlock implements DistributedLocksCreator {
14 | abstract ConnectionFactory getConnectionFactory()
15 |
16 | abstract Connection getBlockingConnection()
17 |
18 | abstract BindingMapper getBindingMapper()
19 |
20 | @Override
21 | Sherlock createSherlock(String instanceId, Duration duration, Clock clock, String tableName) {
22 | com.coditory.sherlock.reactor.Sherlock reactorLocks = SqlSherlock.builder()
23 | .withConnectionFactory(connectionFactory)
24 | .withLocksTable(tableName)
25 | .withBindingMapper(bindingMapper)
26 | .withOwnerId(instanceId)
27 | .withLockDuration(duration)
28 | .withClock(clock)
29 | .build()
30 | return new BlockingReactorSherlockWrapper(reactorLocks)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/connectors/sql/rxjava/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | id("build.publish")
4 | id("build.coverage")
5 | }
6 |
7 | dependencies {
8 | api(projects.api.apiRxjava)
9 | api(libs.r2dbc.spi)
10 | api(projects.connectors.sql.sqlCommonApi)
11 | implementation(projects.connectors.sql.sqlCommon)
12 | integrationTestImplementation(projects.connectors.sql.sqlTests)
13 | integrationTestImplementation(libs.r2dbc.pool)
14 | // integration: postgres
15 | integrationTestImplementation(libs.r2dbc.postgresql)
16 | integrationTestImplementation(libs.testcontainers.postgresql)
17 | // integration: mysql
18 | integrationTestImplementation(libs.r2dbc.mysql)
19 | integrationTestImplementation(libs.testcontainers.mysql)
20 | }
21 |
--------------------------------------------------------------------------------
/connectors/sql/rxjava/src/integrationTest/groovy/com/coditory/sherlock/sql/rxjava/MySqlConnectionPoolHolder.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql.rxjava
2 |
3 | import com.coditory.sherlock.sql.MySqlHolder
4 | import groovy.transform.CompileStatic
5 | import io.r2dbc.spi.ConnectionFactories
6 | import io.r2dbc.spi.ConnectionFactory
7 | import io.r2dbc.spi.ConnectionFactoryOptions
8 | import io.r2dbc.spi.Option
9 |
10 | @CompileStatic
11 | class MySqlConnectionPoolHolder {
12 | private static ConnectionFactory connectionFactory = null
13 |
14 | synchronized static ConnectionFactory getConnectionFactory() {
15 | MySqlHolder.startDb()
16 | if (connectionFactory != null) {
17 | return connectionFactory
18 | }
19 | ConnectionFactoryOptions options = ConnectionFactoryOptions
20 | .parse(MySqlHolder.getJdbcUrl().replace("jdbc:", "r2dbc:"))
21 | .mutate()
22 | .option(Option.valueOf("connectionTimeZone"), "UTC")
23 | .option(ConnectionFactoryOptions.USER, MySqlHolder.getUsername())
24 | .option(ConnectionFactoryOptions.PASSWORD, MySqlHolder.getPassword())
25 | .option(ConnectionFactoryOptions.DATABASE, MySqlHolder.getDatabaseName())
26 | .build()
27 | connectionFactory = ConnectionFactories.get(options)
28 | return connectionFactory
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/connectors/sql/rxjava/src/integrationTest/groovy/com/coditory/sherlock/sql/rxjava/MySqlDbSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql.rxjava
2 |
3 | import com.coditory.sherlock.sql.SqlIndexCreationSpec
4 | import com.coditory.sherlock.sql.SqlLockStorageSpec
5 |
6 | class RxMySqlLockStorageSpec extends SqlLockStorageSpec
7 | implements UsesRxSqlSherlock, MySqlConnectionProvider {}
8 |
9 | class RxMySqlIndexCreationSpec extends SqlIndexCreationSpec
10 | implements UsesRxSqlSherlock, MySqlConnectionProvider {
11 | List expectedIndexNames = ["PRIMARY", tableName + "_IDX"].sort()
12 | }
13 |
--------------------------------------------------------------------------------
/connectors/sql/rxjava/src/integrationTest/groovy/com/coditory/sherlock/sql/rxjava/MySqlDistributedLockSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql.rxjava
2 |
3 | import com.coditory.sherlock.AcquireLockMultipleTimesSpec
4 | import com.coditory.sherlock.AcquireLockSpec
5 | import com.coditory.sherlock.HandleDbFailureSpec
6 | import com.coditory.sherlock.InfiniteAcquireLockSpec
7 | import com.coditory.sherlock.ReleaseLockSpec
8 |
9 | class RxMySqlReleaseLockSpec extends ReleaseLockSpec
10 | implements UsesRxSqlSherlock, MySqlConnectionProvider {}
11 |
12 | class RxMySqlAcquireLockSpec extends AcquireLockSpec
13 | implements UsesRxSqlSherlock, MySqlConnectionProvider {}
14 |
15 | class RxMySqlAcquireLockMultipleTimesSpec extends AcquireLockMultipleTimesSpec
16 | implements UsesRxSqlSherlock, MySqlConnectionProvider {}
17 |
18 | class RxMySqlInfiniteAcquireLockSpec extends InfiniteAcquireLockSpec
19 | implements UsesRxSqlSherlock, MySqlConnectionProvider {}
20 |
21 | class RxMySqlHandleDbFailureSpec extends HandleDbFailureSpec
22 | implements UsesRxSqlSherlock, MySqlConnectionProvider {}
23 |
--------------------------------------------------------------------------------
/connectors/sql/rxjava/src/integrationTest/groovy/com/coditory/sherlock/sql/rxjava/PostgresDbSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql.rxjava
2 |
3 | import com.coditory.sherlock.sql.SqlIndexCreationSpec
4 | import com.coditory.sherlock.sql.SqlLockStorageSpec
5 |
6 | class RxPostgresLockStorageSpec extends SqlLockStorageSpec
7 | implements UsesRxSqlSherlock, PostgresConnectionProvider {}
8 |
9 | class RxPostgresIndexCreationSpec extends SqlIndexCreationSpec
10 | implements UsesRxSqlSherlock, PostgresConnectionProvider {
11 | List expectedIndexNames = [tableName + "_pkey", tableName + "_idx"].sort()
12 | }
13 |
--------------------------------------------------------------------------------
/connectors/sql/rxjava/src/integrationTest/groovy/com/coditory/sherlock/sql/rxjava/PostgresDistributedLockSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql.rxjava
2 |
3 | import com.coditory.sherlock.AcquireLockMultipleTimesSpec
4 | import com.coditory.sherlock.AcquireLockSpec
5 | import com.coditory.sherlock.HandleDbFailureSpec
6 | import com.coditory.sherlock.InfiniteAcquireLockSpec
7 | import com.coditory.sherlock.ReleaseLockSpec
8 |
9 | class RxPostgresReleaseLockSpec extends ReleaseLockSpec
10 | implements UsesRxSqlSherlock, PostgresConnectionProvider {}
11 |
12 | class RxPostgresAcquireLockSpec extends AcquireLockSpec
13 | implements UsesRxSqlSherlock, PostgresConnectionProvider {}
14 |
15 | class RxPostgresAcquireLockMultipleTimesSpec extends AcquireLockMultipleTimesSpec
16 | implements UsesRxSqlSherlock, PostgresConnectionProvider {}
17 |
18 | class RxPostgresInfiniteAcquireLockSpec extends InfiniteAcquireLockSpec
19 | implements UsesRxSqlSherlock, PostgresConnectionProvider {}
20 |
21 | class RxPostgresHandleDbFailureSpec extends HandleDbFailureSpec
22 | implements UsesRxSqlSherlock, PostgresConnectionProvider {}
23 |
--------------------------------------------------------------------------------
/connectors/sql/rxjava/src/integrationTest/groovy/com/coditory/sherlock/sql/rxjava/UsesRxSqlSherlock.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql.rxjava
2 |
3 | import com.coditory.sherlock.BlockingRxSherlockWrapper
4 | import com.coditory.sherlock.Sherlock
5 | import com.coditory.sherlock.base.DistributedLocksCreator
6 | import com.coditory.sherlock.sql.BindingMapper
7 | import io.r2dbc.spi.ConnectionFactory
8 |
9 | import java.sql.Connection
10 | import java.time.Clock
11 | import java.time.Duration
12 |
13 | trait UsesRxSqlSherlock implements DistributedLocksCreator {
14 | abstract ConnectionFactory getConnectionFactory()
15 |
16 | abstract Connection getBlockingConnection()
17 |
18 | abstract BindingMapper getBindingMapper()
19 |
20 | @Override
21 | Sherlock createSherlock(String instanceId, Duration duration, Clock clock, String tableName) {
22 | com.coditory.sherlock.rxjava.Sherlock reactorLocks = SqlSherlock.builder()
23 | .withConnectionFactory(connectionFactory)
24 | .withLocksTable(tableName)
25 | .withBindingMapper(bindingMapper)
26 | .withOwnerId(instanceId)
27 | .withLockDuration(duration)
28 | .withClock(clock)
29 | .build()
30 | return new BlockingRxSherlockWrapper(reactorLocks)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/connectors/sql/sync/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | id("build.publish")
4 | id("build.coverage")
5 | }
6 |
7 | dependencies {
8 | api(projects.api.apiSync)
9 | api(projects.connectors.sql.sqlCommonApi)
10 | implementation(projects.connectors.sql.sqlCommon)
11 | integrationTestImplementation(projects.connectors.sql.sqlTests)
12 | }
13 |
--------------------------------------------------------------------------------
/connectors/sql/sync/src/integrationTest/groovy/com/coditory/sherlock/sql/MySqlDbSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql
2 |
3 | class MySqlLockStorageSpec extends SqlLockStorageSpec
4 | implements UsesSqlSherlock, MySqlConnectionProvider {}
5 |
6 | class MySqlIndexCreationSpec extends SqlIndexCreationSpec
7 | implements UsesSqlSherlock, MySqlConnectionProvider {
8 | List expectedIndexNames = ["PRIMARY", SqlIndexCreationSpec.tableName + "_IDX"].sort()
9 | }
10 |
--------------------------------------------------------------------------------
/connectors/sql/sync/src/integrationTest/groovy/com/coditory/sherlock/sql/MySqlDistributedLockSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql
2 |
3 | import com.coditory.sherlock.AcquireLockMultipleTimesSpec
4 | import com.coditory.sherlock.AcquireLockSpec
5 | import com.coditory.sherlock.HandleDbFailureSpec
6 | import com.coditory.sherlock.InfiniteAcquireLockSpec
7 | import com.coditory.sherlock.ReleaseLockSpec
8 |
9 | class MySqlReleaseLockSpec extends ReleaseLockSpec
10 | implements UsesSqlSherlock, MySqlConnectionProvider {}
11 |
12 | class MySqlAcquireLockSpec extends AcquireLockSpec
13 | implements UsesSqlSherlock, MySqlConnectionProvider {}
14 |
15 | class MySqlAcquireLockMultipleTimesSpec extends AcquireLockMultipleTimesSpec
16 | implements UsesSqlSherlock, MySqlConnectionProvider {}
17 |
18 | class MySqlInfiniteAcquireLockSpec extends InfiniteAcquireLockSpec
19 | implements UsesSqlSherlock, MySqlConnectionProvider {}
20 |
21 | class MySqlHandleDbFailureSpec extends HandleDbFailureSpec
22 | implements UsesSqlSherlock, MySqlConnectionProvider {}
23 |
24 | class MySqlLockCommitSpec extends SqlLockCommitSpec
25 | implements UsesSqlSherlock, MySqlConnectionProvider {}
26 |
--------------------------------------------------------------------------------
/connectors/sql/sync/src/integrationTest/groovy/com/coditory/sherlock/sql/PostgresDbSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql
2 |
3 | class PostgresLockStorageSpec extends SqlLockStorageSpec
4 | implements UsesSqlSherlock, PostgresConnectionProvider {}
5 |
6 | class PostgresIndexCreationSpec extends SqlIndexCreationSpec
7 | implements UsesSqlSherlock, PostgresConnectionProvider {
8 | List expectedIndexNames = [SqlIndexCreationSpec.tableName + "_pkey", SqlIndexCreationSpec.tableName + "_idx"].sort()
9 | }
10 |
--------------------------------------------------------------------------------
/connectors/sql/sync/src/integrationTest/groovy/com/coditory/sherlock/sql/PostgresDistributedLockSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql
2 |
3 | import com.coditory.sherlock.AcquireLockMultipleTimesSpec
4 | import com.coditory.sherlock.AcquireLockSpec
5 | import com.coditory.sherlock.HandleDbFailureSpec
6 | import com.coditory.sherlock.InfiniteAcquireLockSpec
7 | import com.coditory.sherlock.ReleaseLockSpec
8 |
9 | class PostgresReleaseLockSpec extends ReleaseLockSpec
10 | implements UsesSqlSherlock, PostgresConnectionProvider {}
11 |
12 | class PostgresAcquireLockSpec extends AcquireLockSpec
13 | implements UsesSqlSherlock, PostgresConnectionProvider {}
14 |
15 | class PostgresAcquireLockMultipleTimesSpec extends AcquireLockMultipleTimesSpec
16 | implements UsesSqlSherlock, PostgresConnectionProvider {}
17 |
18 | class PostgresInfiniteAcquireLockSpec extends InfiniteAcquireLockSpec
19 | implements UsesSqlSherlock, PostgresConnectionProvider {}
20 |
21 | class PostgresHandleDbFailureSpec extends HandleDbFailureSpec
22 | implements UsesSqlSherlock, PostgresConnectionProvider {}
23 |
24 | class PostgresSqlLockCommitSpec extends SqlLockCommitSpec
25 | implements UsesSqlSherlock, PostgresConnectionProvider {}
26 |
--------------------------------------------------------------------------------
/connectors/sql/sync/src/integrationTest/groovy/com/coditory/sherlock/sql/SqlConnectioProvider.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql
2 |
3 |
4 | import com.coditory.sherlock.base.DatabaseManager
5 | import groovy.transform.CompileStatic
6 |
7 | import javax.sql.DataSource
8 |
9 | @CompileStatic
10 | interface SqlConnectionProvider {
11 | DataSource getDataSource();
12 |
13 | DataSource getDataSource(DataSourceConfigurer configurer);
14 | }
15 |
16 | @CompileStatic
17 | trait PostgresConnectionProvider implements SqlConnectionProvider, DatabaseManager {
18 | @Override
19 | DataSource getDataSource() {
20 | return PostgresHolder.getDataSource()
21 | }
22 |
23 | @Override
24 | DataSource getDataSource(DataSourceConfigurer configurer) {
25 | return PostgresHolder.getDataSource(configurer)
26 | }
27 |
28 | @Override
29 | void stopDatabase() {
30 | PostgresHolder.stopDb()
31 | }
32 |
33 | @Override
34 | void startDatabase() {
35 | PostgresHolder.startDb()
36 | }
37 | }
38 |
39 | @CompileStatic
40 | trait MySqlConnectionProvider implements SqlConnectionProvider, DatabaseManager {
41 | @Override
42 | DataSource getDataSource() {
43 | return MySqlHolder.getDataSource()
44 | }
45 |
46 | @Override
47 | DataSource getDataSource(DataSourceConfigurer configurer) {
48 | return MySqlHolder.getDataSource(configurer)
49 | }
50 |
51 | @Override
52 | void stopDatabase() {
53 | MySqlHolder.stopDb()
54 | }
55 |
56 | @Override
57 | void startDatabase() {
58 | MySqlHolder.startDb()
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/connectors/sql/sync/src/integrationTest/groovy/com/coditory/sherlock/sql/UsesSqlSherlock.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql
2 |
3 |
4 | import com.coditory.sherlock.Sherlock
5 |
6 | import javax.sql.DataSource
7 | import java.time.Clock
8 | import java.time.Duration
9 |
10 | trait UsesSqlSherlock implements SqlDistributedLocksCreator {
11 | abstract DataSource getDataSource()
12 |
13 | abstract DataSource getDataSource(DataSourceConfigurer configurer)
14 |
15 | @Override
16 | Sherlock createSherlock(String instanceId, Duration duration, Clock clock, String tableName) {
17 | return SqlSherlock.builder()
18 | .withDataSource(dataSource)
19 | .withLocksTable(tableName)
20 | .withOwnerId(instanceId)
21 | .withLockDuration(duration)
22 | .withClock(clock)
23 | .build()
24 | }
25 |
26 | @Override
27 | Sherlock createSherlock(DataSourceConfigurer configurer) {
28 | return SqlSherlock.builder()
29 | .withDataSource(getDataSource(configurer))
30 | .build()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/connectors/sql/tests/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | }
4 |
5 | dependencies {
6 | api(projects.common)
7 | api(projects.tests)
8 | api(projects.connectors.sql.sqlCommon)
9 | api(libs.spock.core)
10 | api(libs.hikaricp)
11 | // integration: postgres
12 | api(libs.postgresql)
13 | implementation(libs.testcontainers.postgresql)
14 | // integration: mysql
15 | api(libs.mysql)
16 | implementation(libs.testcontainers.mysql)
17 | }
18 |
--------------------------------------------------------------------------------
/connectors/sql/tests/src/main/groovy/com/coditory/sherlock/sql/DataSourceConfigurer.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql
2 |
3 | import com.zaxxer.hikari.HikariConfig
4 |
5 | interface DataSourceConfigurer {
6 | void configure(HikariConfig config)
7 | }
8 |
--------------------------------------------------------------------------------
/connectors/sql/tests/src/main/groovy/com/coditory/sherlock/sql/SqlDistributedLocksCreator.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql
2 |
3 | import com.coditory.sherlock.Sherlock
4 | import com.coditory.sherlock.base.DistributedLocksCreator
5 | import groovy.transform.CompileStatic
6 |
7 | @CompileStatic
8 | interface SqlDistributedLocksCreator extends DistributedLocksCreator {
9 | Sherlock createSherlock(DataSourceConfigurer configurer)
10 | }
11 |
--------------------------------------------------------------------------------
/connectors/sql/tests/src/main/groovy/com/coditory/sherlock/sql/SqlLockCommitSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql
2 |
3 | import com.coditory.sherlock.DistributedLock
4 | import com.coditory.sherlock.LocksBaseSpec
5 | import com.coditory.sherlock.Sherlock
6 | import spock.lang.Unroll
7 |
8 | abstract class SqlLockCommitSpec extends LocksBaseSpec implements SqlDistributedLocksCreator {
9 | @Unroll
10 | def "should preserve commit lock with dataSource auto-commit=#autoCommit"() {
11 | given:
12 | Sherlock sherlock = createSherlock({
13 | it.setAutoCommit(autoCommit)
14 | it.setMaximumPoolSize(2)
15 | })
16 | DistributedLock lock = sherlock.createLock("lock")
17 | when:
18 | boolean firstResult = lock.acquire()
19 | boolean secondResult = lock.acquire()
20 | then:
21 | firstResult == true
22 | secondResult == false
23 | where:
24 | autoCommit << [true, false]
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/connectors/sql/tests/src/main/groovy/com/coditory/sherlock/sql/SqlTableIndexes.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.sql
2 |
3 | import groovy.transform.CompileStatic
4 |
5 | import java.sql.Connection
6 | import java.sql.DatabaseMetaData
7 | import java.sql.ResultSet
8 |
9 | @CompileStatic
10 | class SqlTableIndexes {
11 | static List listTableIndexes(Connection connection, String tableName) {
12 | Set indexes = new HashSet<>()
13 | DatabaseMetaData metaData = connection.getMetaData()
14 | List schemaList = getSchemaList(metaData)
15 | for (int i = 0; i < schemaList.size(); i++) {
16 | ResultSet indexValues
17 | try {
18 | indexValues = metaData.getIndexInfo(null, schemaList.get(i), tableName, false, false)
19 | while (indexValues.next()) {
20 | String dbIndexName = indexValues.getString("INDEX_NAME")
21 | if (dbIndexName != null) {
22 | indexes.add(dbIndexName)
23 | }
24 | }
25 | } finally {
26 | if (indexValues != null) indexValues.close()
27 | }
28 | }
29 | return indexes.toList().sort()
30 | }
31 |
32 | static private List getSchemaList(DatabaseMetaData metaData) {
33 | List schemaList = new ArrayList<>()
34 | ResultSet rs
35 | try {
36 | rs = metaData.getSchemas()
37 | while (rs.next()) {
38 | String tableSchema = rs.getString(1)
39 | if (tableSchema != null) {
40 | schemaList.add(tableSchema)
41 | }
42 | }
43 | if (schemaList.isEmpty()) {
44 | schemaList.add(null)
45 | }
46 | } finally {
47 | if (rs) rs.close()
48 | }
49 | return schemaList
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | mkdocs>=1
2 | mkdocs-minify-plugin>=0.3
3 | mkdocs-material>=9
4 | mkdocs-markdownextradata-plugin>=0.2.5
5 | Pygments>=2.17
6 | pymdown-extensions>=10
--------------------------------------------------------------------------------
/docs/site/about.md:
--------------------------------------------------------------------------------
1 | - [GitHub Repository](https://github.com/coditory/sherlock-distributed-lock/)
2 | - [License](https://github.com/coditory/sherlock-distributed-lock/blob/master/LICENSE)
3 | - [Changelog](https://github.com/coditory/sherlock-distributed-lock/releases)
--------------------------------------------------------------------------------
/docs/site/assets/img/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coditory/sherlock-distributed-lock/01e3ff4628833dd34db146661236f6905a615944/docs/site/assets/img/favicon.png
--------------------------------------------------------------------------------
/docs/site/assets/img/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coditory/sherlock-distributed-lock/01e3ff4628833dd34db146661236f6905a615944/docs/site/assets/img/icon.png
--------------------------------------------------------------------------------
/docs/site/assets/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coditory/sherlock-distributed-lock/01e3ff4628833dd34db146661236f6905a615944/docs/site/assets/img/logo.png
--------------------------------------------------------------------------------
/docs/site/connectors/index.md:
--------------------------------------------------------------------------------
1 | # Sherlock Connectors
2 |
3 | Sherlock multiple database connectors to fit in your infrastructure. Provided connectors:
4 |
5 | - [Mongo Connector](mongo.md) - Uses [MongoDB](https://www.mongodb.com/) and its JVM drivers to store locks
6 | - [SQL Connector](sql.md) - Uses SQL database (tested on [Postgres](https://www.postgresql.org/) and [MySQL](https://www.mysql.com/)) and JDBC drivers to store locks
7 | - [In-Memory Connector](inmem.md) - Stores locks in memory. Created for local development and testing.
8 |
--------------------------------------------------------------------------------
/docs/site/index.md:
--------------------------------------------------------------------------------
1 | # Sherlock Distributed Lock
2 |
3 | [](https://github.com/coditory/sherlock-distributed-lock/actions/workflows/build.yml)
4 | [](https://codecov.io/gh/coditory/sherlock-distributed-lock)
5 | [](https://search.maven.org/search?q=com.coditory.sherlock)
6 | [](http://www.javadoc.io/doc/com.coditory.sherlock/sherlock-api)
7 |
8 |
9 |

10 |
11 |
12 | [Sherlock](https://github.com/coditory/sherlock-distributed-lock) is a distributed locking library for JVM projects.
13 | It exposes both synchronous and reactive APIs (Reactor, RxJava, Kotlin Coroutines)
14 | and uses database [connectors](connectors/index.md) to store locks.
15 | It was created as a simple solution to manage distributed locks among microservices.
16 |
17 |
18 | ## How it works?
19 |
20 | Locks are acquired for a [specific duration](locks.md#lock-duration).
21 | When lock owning instance unexpectedly goes down,
22 | lock is automatically released after expiration.
23 |
24 | ## Quick start
25 |
26 | - [MongoDB](connectors/mongo.md) - using sherlock with MongoDB
27 | - [SQL](connectors/sql.md) - using sherlock with SQL databases
28 | - [In-Memory](connectors/inmem.md) - using in-memory Sherlock for local development or testing
29 | - [Testing](testing.md) - stubbing and mocking Sherlock in unit tests
30 | - [Migrator](migrator.md) - using Sherlock as for database migration
31 |
32 |
--------------------------------------------------------------------------------
/docs/site/testing.md:
--------------------------------------------------------------------------------
1 | # Testing
2 |
3 | Sherlock provides stubs and mocks for testing purposes. Try it out:
4 | `SherlockStub`, `ReactorSherlockStub`, `DistributedLockMock` and `ReactorDistributedLockMock`.
5 |
6 | !!! info "Creating own stub and mocks"
7 | Sherlock API consists mostly of interfaces, so it's easy to create stubs and mocks for your own purposes.
8 |
9 | Sample usage in spock tests:
10 |
11 | ```groovy
12 | def "should release a lock after operation"() {
13 | given: "there is a released lock"
14 | DistributedLockMock lock = DistributedLockMock.alwaysReleasedLock()
15 | when: "single instance action is executed"
16 | boolean taskPerformed = singleInstanceAction(lock)
17 | then: "the task was performed"
18 | taskPerformed == true
19 | and: "lock was acquired and released"
20 | lock.wasAcquiredAndReleased == true
21 | }
22 |
23 | def "should not perform single instance action when lock is locked"() {
24 | given: "there is a lock acquired by other instance"
25 | DistributedLockMock lock = DistributedLockMock.alwaysAcquiredLock()
26 | when: "single instance action is executed"
27 | boolean taskPerformed = singleInstanceAction(lock)
28 | then: "action did not perform the task"
29 | taskPerformed == false
30 | and: "action failed acquiring the lock"
31 | lock.wasAcquireRejected == true
32 | and: "action did not release the lock"
33 | lock.wasReleaseInvoked == false
34 | }
35 | ```
36 |
37 | !!! info "In Memory Connector"
38 | The easiest way to setup Sherlock in tests is to use [In-Memory Connector](connectors/inmem.md).
39 | Use stubs when you need more control over the locking mechanism.
40 |
--------------------------------------------------------------------------------
/docs/theme/main.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block extrahead %}
4 |
5 |
6 |
11 |
12 | {% endblock %}
13 |
14 | {% block header %}
15 |
16 |
20 |
21 | {% include "partials/header.html" %}
22 | {% endblock %}
23 |
--------------------------------------------------------------------------------
/examples/inmem-coroutines/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.kotlin")
3 | }
4 |
5 | dependencies {
6 | implementation(projects.connectors.inmem.inmemCoroutines)
7 | implementation(libs.logback.classic)
8 | implementation(libs.kotlinx.coroutines.core)
9 | }
10 |
--------------------------------------------------------------------------------
/examples/inmem-coroutines/src/main/kotlin/com/coditory/sherlock/samples/inmem/coroutines/InMemKtLockSample.kt:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.inmem.coroutines
2 |
3 | import com.coditory.sherlock.inmem.coroutines.InMemorySherlock
4 | import kotlinx.coroutines.runBlocking
5 | import org.slf4j.Logger
6 | import org.slf4j.LoggerFactory
7 |
8 | object InMemKtLockSample {
9 | private val logger: Logger = LoggerFactory.getLogger(this.javaClass)
10 |
11 | private suspend fun sample() {
12 | val sherlock = InMemorySherlock.create()
13 | val lock = sherlock.createLock("sample-lock")
14 | lock.runLocked {
15 | logger.info("Lock acquired!")
16 | }
17 | }
18 |
19 | @JvmStatic
20 | fun main(args: Array) {
21 | runBlocking { sample() }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/inmem-coroutines/src/main/kotlin/com/coditory/sherlock/samples/inmem/coroutines/InMemKtMigrationSample.kt:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.inmem.coroutines
2 |
3 | import com.coditory.sherlock.coroutines.migrator.SherlockMigrator
4 | import com.coditory.sherlock.inmem.coroutines.InMemorySherlock
5 | import kotlinx.coroutines.runBlocking
6 | import org.slf4j.Logger
7 | import org.slf4j.LoggerFactory
8 |
9 | object InMemKtMigrationSample {
10 | private val logger: Logger = LoggerFactory.getLogger(this.javaClass)
11 |
12 | private suspend fun sample() {
13 | val sherlock = InMemorySherlock.create()
14 | // first commit - all migrations are executed
15 | SherlockMigrator.builder(sherlock)
16 | .addChangeSet("change-set-1") { logger.info("Change-set 1") }
17 | .addChangeSet("change-set-2") { logger.info("Change-set 2") }
18 | .migrate()
19 | // second commit - only new change-set is executed
20 | SherlockMigrator.builder(sherlock)
21 | .addChangeSet("change-set-1") { logger.info("Change-set 1") }
22 | .addChangeSet("change-set-2") { logger.info("Change-set 2") }
23 | .addChangeSet("change-set-3") { logger.info("Change-set 3") }
24 | .migrate()
25 | }
26 |
27 | @JvmStatic
28 | fun main(args: Array) {
29 | runBlocking { sample() }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/examples/inmem-coroutines/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/inmem-reactor/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | }
4 |
5 | dependencies {
6 | implementation(projects.connectors.inmem.inmemReactor)
7 | implementation(libs.logback.classic)
8 | }
9 |
--------------------------------------------------------------------------------
/examples/inmem-reactor/src/main/java/com/coditory/sherlock/samples/inmem/reactor/InMemReactorLockSample.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.inmem.reactor;
2 |
3 | import com.coditory.sherlock.inmem.reactor.InMemorySherlock;
4 | import com.coditory.sherlock.reactor.DistributedLock;
5 | import com.coditory.sherlock.reactor.Sherlock;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 | import reactor.core.publisher.Mono;
9 |
10 | public class InMemReactorLockSample {
11 | private static final Logger logger = LoggerFactory.getLogger(InMemReactorLockSample.class);
12 |
13 | public static void main(String[] args) {
14 | Sherlock sherlock = InMemorySherlock.create();
15 | DistributedLock lock = sherlock.createLock("sample-lock");
16 | lock.runLocked(Mono.fromCallable(() -> {
17 | logger.info("Lock acquired!");
18 | return true;
19 | })).block();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/examples/inmem-reactor/src/main/java/com/coditory/sherlock/samples/inmem/reactor/InMemReactorMigrationSample.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.inmem.reactor;
2 |
3 | import com.coditory.sherlock.inmem.reactor.InMemorySherlock;
4 | import com.coditory.sherlock.reactor.Sherlock;
5 | import com.coditory.sherlock.reactor.migrator.SherlockMigrator;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 | import reactor.core.publisher.Mono;
9 |
10 | public class InMemReactorMigrationSample {
11 | private static final Logger logger = LoggerFactory.getLogger(InMemReactorMigrationSample.class);
12 |
13 | public static void main(String[] args) {
14 | Sherlock sherlock = InMemorySherlock.create();
15 | // first commit - all migrations are executed
16 | // acceptable changesets types: () -> {}, Mono>, () -> Mono>
17 | SherlockMigrator.builder(sherlock)
18 | .addChangeSet("change-set-1", Mono.fromRunnable(() -> logger.info("Change-set 1")))
19 | .addChangeSet("change-set-2", () -> Mono.fromRunnable(() -> logger.info("Change-set 2")))
20 | .migrate()
21 | .block();
22 | // second commit - only new change-set is executed
23 | SherlockMigrator.builder(sherlock)
24 | .addChangeSet("change-set-1", () -> logger.info("Change-set 1"))
25 | .addChangeSet("change-set-2", () -> logger.info("Change-set 2"))
26 | .addChangeSet("change-set-3", () -> logger.info("Change-set 3"))
27 | .migrate()
28 | .block();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/examples/inmem-reactor/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/inmem-rxjava/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | }
4 |
5 | dependencies {
6 | implementation(projects.connectors.inmem.inmemRxjava)
7 | implementation(libs.logback.classic)
8 | }
9 |
--------------------------------------------------------------------------------
/examples/inmem-rxjava/src/main/java/com/coditory/sherlock/samples/inmem/rxjava/InMemRxLockSample.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.inmem.rxjava;
2 |
3 | import com.coditory.sherlock.inmem.rxjava.InMemorySherlock;
4 | import com.coditory.sherlock.rxjava.DistributedLock;
5 | import com.coditory.sherlock.rxjava.Sherlock;
6 | import io.reactivex.rxjava3.core.Single;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | public class InMemRxLockSample {
11 | private static final Logger logger = LoggerFactory.getLogger(InMemRxLockSample.class);
12 |
13 | public static void main(String[] args) {
14 | Sherlock sherlock = InMemorySherlock.create();
15 | DistributedLock lock = sherlock.createLock("sample-lock");
16 | lock.runLocked(Single.fromCallable(() -> {
17 | logger.info("Lock acquired!");
18 | return true;
19 | })).blockingGet();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/examples/inmem-rxjava/src/main/java/com/coditory/sherlock/samples/inmem/rxjava/InMemRxMigrationSample.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.inmem.rxjava;
2 |
3 | import com.coditory.sherlock.inmem.rxjava.InMemorySherlock;
4 | import com.coditory.sherlock.rxjava.Sherlock;
5 | import com.coditory.sherlock.rxjava.migrator.SherlockMigrator;
6 | import io.reactivex.rxjava3.core.Completable;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | public class InMemRxMigrationSample {
11 | private static final Logger logger = LoggerFactory.getLogger(InMemRxMigrationSample.class);
12 |
13 | public static void main(String[] args) {
14 | Sherlock sherlock = InMemorySherlock.create();
15 | // first commit - all migrations are executed
16 | // acceptable changesets types: () -> {}, Completable, () -> Completable
17 | SherlockMigrator.builder(sherlock)
18 | .addChangeSet("change-set-1", Completable.fromRunnable(() -> logger.info("Change-set 1")))
19 | .addChangeSet("change-set-2", () -> Completable.fromRunnable(() -> logger.info("Change-set 2")))
20 | .migrate()
21 | .blockingGet();
22 | // second commit - only new change-set is executed
23 | SherlockMigrator.builder(sherlock)
24 | .addChangeSet("change-set-1", () -> logger.info("Change-set 1"))
25 | .addChangeSet("change-set-2", () -> logger.info("Change-set 2"))
26 | .addChangeSet("change-set-3", () -> logger.info("Change-set 3"))
27 | .migrate()
28 | .blockingGet();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/examples/inmem-rxjava/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/inmem-sync/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | id("build.kotlin")
4 | }
5 |
6 | dependencies {
7 | implementation(projects.connectors.inmem.inmemSync)
8 | implementation(libs.logback.classic)
9 | }
10 |
--------------------------------------------------------------------------------
/examples/inmem-sync/src/main/java/com/coditory/sherlock/samples/inmem/sync/InMemSyncLockSample.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.inmem.sync;
2 |
3 | import com.coditory.sherlock.DistributedLock;
4 | import com.coditory.sherlock.Sherlock;
5 | import com.coditory.sherlock.inmem.InMemorySherlock;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 |
9 | public class InMemSyncLockSample {
10 | private static final Logger logger = LoggerFactory.getLogger(InMemSyncLockSample.class);
11 |
12 | public static void main(String[] args) {
13 | Sherlock sherlock = InMemorySherlock.create();
14 | DistributedLock lock = sherlock.createLock("sample-lock");
15 | lock.runLocked(() -> logger.info("Lock acquired!"));
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/examples/inmem-sync/src/main/java/com/coditory/sherlock/samples/inmem/sync/InMemSyncMigrationSample.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.inmem.sync;
2 |
3 | import com.coditory.sherlock.Sherlock;
4 | import com.coditory.sherlock.inmem.InMemorySherlock;
5 | import com.coditory.sherlock.migrator.SherlockMigrator;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 |
9 | public class InMemSyncMigrationSample {
10 | private static final Logger logger = LoggerFactory.getLogger(InMemSyncMigrationSample.class);
11 |
12 | public static void main(String[] args) {
13 | Sherlock sherlock = InMemorySherlock.create();
14 | // first commit - all migrations are executed
15 | SherlockMigrator.builder(sherlock)
16 | .addChangeSet("change-set-1", () -> logger.info("Change-set 1"))
17 | .addChangeSet("change-set-2", () -> logger.info("Change-set 2"))
18 | .migrate();
19 | // second commit - only new change-set is executed
20 | SherlockMigrator.builder(sherlock)
21 | .addChangeSet("change-set-1", () -> logger.info("Change-set 1"))
22 | .addChangeSet("change-set-2", () -> logger.info("Change-set 2"))
23 | .addChangeSet("change-set-3", () -> logger.info("Change-set 3"))
24 | .migrate();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/examples/inmem-sync/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/mongo-coroutines/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.kotlin")
3 | }
4 |
5 | dependencies {
6 | implementation(projects.connectors.mongo.mongoCoroutines)
7 | implementation(libs.logback.classic)
8 | }
9 |
--------------------------------------------------------------------------------
/examples/mongo-coroutines/src/main/kotlin/com/coditory/sherlock/samples/mongo/coroutines/MongoKtLockSample.kt:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.mongo.coroutines
2 |
3 | import com.coditory.sherlock.mongo.coroutines.MongoSherlock
4 | import com.mongodb.kotlin.client.coroutine.MongoClient
5 | import com.mongodb.kotlin.client.coroutine.MongoCollection
6 | import kotlinx.coroutines.runBlocking
7 | import org.bson.Document
8 | import org.slf4j.Logger
9 | import org.slf4j.LoggerFactory
10 |
11 | object MongoKtLockSample {
12 | private val logger: Logger = LoggerFactory.getLogger(this.javaClass)
13 |
14 | private fun getCollection(): MongoCollection {
15 | val database = "sherlock"
16 | val mongoClient = MongoClient.create("mongodb://localhost:27017/$database")
17 | return mongoClient
18 | .getDatabase(database)
19 | .getCollection("locks")
20 | }
21 |
22 | private suspend fun sample() {
23 | val sherlock = MongoSherlock.create(getCollection())
24 | val lock = sherlock.createLock("sample-lock")
25 | lock.runLocked { logger.info("Lock acquired!") }
26 | }
27 |
28 | @JvmStatic
29 | fun main(args: Array) {
30 | runBlocking { sample() }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/examples/mongo-coroutines/src/main/kotlin/com/coditory/sherlock/samples/mongo/coroutines/MongoKtMigrationSample.kt:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.mongo.coroutines
2 |
3 | import com.coditory.sherlock.coroutines.migrator.SherlockMigrator
4 | import com.coditory.sherlock.mongo.coroutines.MongoSherlock
5 | import com.mongodb.kotlin.client.coroutine.MongoClient
6 | import com.mongodb.kotlin.client.coroutine.MongoCollection
7 | import kotlinx.coroutines.runBlocking
8 | import org.bson.Document
9 | import org.slf4j.Logger
10 | import org.slf4j.LoggerFactory
11 |
12 | object MongoKtMigrationSample {
13 | private val logger: Logger = LoggerFactory.getLogger(this.javaClass)
14 |
15 | private fun locksCollection(): MongoCollection {
16 | val database = "sherlock"
17 | val mongoClient = MongoClient.create("mongodb://localhost:27017/$database")
18 | return mongoClient
19 | .getDatabase(database)
20 | .getCollection("locks")
21 | }
22 |
23 | private suspend fun sample() {
24 | val sherlock = MongoSherlock.create(locksCollection())
25 | // first commit - all migrations are executed
26 | SherlockMigrator.builder(sherlock)
27 | .addChangeSet("change-set-1") { logger.info("Change-set 1") }
28 | .addChangeSet("change-set-2") { logger.info("Change-set 2") }
29 | .migrate()
30 | // second commit - only new change-set is executed
31 | SherlockMigrator.builder(sherlock)
32 | .addChangeSet("change-set-1") { logger.info("Change-set 1") }
33 | .addChangeSet("change-set-2") { logger.info("Change-set 2") }
34 | .addChangeSet("change-set-3") { logger.info("Change-set 3") }
35 | .migrate()
36 | }
37 |
38 | @JvmStatic
39 | fun main(args: Array) {
40 | runBlocking { sample() }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/examples/mongo-coroutines/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/mongo-reactor/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | }
4 |
5 | dependencies {
6 | implementation(projects.connectors.mongo.mongoReactor)
7 | implementation(libs.logback.classic)
8 | }
9 |
--------------------------------------------------------------------------------
/examples/mongo-reactor/src/main/java/com/coditory/sherlock/samples/mongo/reactor/MongoReactorLockSample.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.mongo.reactor;
2 |
3 | import com.coditory.sherlock.mongo.reactor.MongoSherlock;
4 | import com.coditory.sherlock.reactor.DistributedLock;
5 | import com.coditory.sherlock.reactor.Sherlock;
6 | import com.mongodb.reactivestreams.client.MongoClient;
7 | import com.mongodb.reactivestreams.client.MongoClients;
8 | import com.mongodb.reactivestreams.client.MongoCollection;
9 | import org.bson.Document;
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 |
13 | public class MongoReactorLockSample {
14 | private static final Logger logger = LoggerFactory.getLogger(MongoReactorLockSample.class);
15 |
16 | private static MongoCollection getCollection() {
17 | String database = "sherlock";
18 | MongoClient mongoClient = MongoClients.create("mongodb://localhost:27017/" + database);
19 | return mongoClient
20 | .getDatabase("sherlock")
21 | .getCollection("locks");
22 | }
23 |
24 | public static void main(String[] args) {
25 | Sherlock sherlock = MongoSherlock.create(getCollection());
26 | DistributedLock lock = sherlock.createLock("sample-lock2");
27 | lock.runLocked(() -> logger.info("Lock acquired!"))
28 | .block();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/examples/mongo-reactor/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/mongo-rxjava/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | }
4 |
5 | dependencies {
6 | implementation(projects.connectors.mongo.mongoRxjava)
7 | implementation(libs.logback.classic)
8 | }
9 |
--------------------------------------------------------------------------------
/examples/mongo-rxjava/src/main/java/com/coditory/sherlock/samples/mongo/rxjava/MongoRxLockSample.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.mongo.rxjava;
2 |
3 | import com.coditory.sherlock.mongo.rxjava.MongoSherlock;
4 | import com.coditory.sherlock.rxjava.DistributedLock;
5 | import com.coditory.sherlock.rxjava.Sherlock;
6 | import com.mongodb.reactivestreams.client.MongoClient;
7 | import com.mongodb.reactivestreams.client.MongoClients;
8 | import com.mongodb.reactivestreams.client.MongoCollection;
9 | import org.bson.Document;
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 |
13 | public class MongoRxLockSample {
14 | private static final Logger logger = LoggerFactory.getLogger(MongoRxLockSample.class);
15 |
16 | private static MongoCollection getCollection() {
17 | String database = "sherlock";
18 | MongoClient mongoClient = MongoClients.create("mongodb://localhost:27017/" + database);
19 | return mongoClient
20 | .getDatabase("sherlock")
21 | .getCollection("locks");
22 | }
23 |
24 | public static void main(String[] args) {
25 | Sherlock sherlock = MongoSherlock.create(getCollection());
26 | DistributedLock lock = sherlock.createLock("sample-lock");
27 | lock.runLocked(() -> logger.info("Lock acquired!"))
28 | .blockingGet();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/examples/mongo-rxjava/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/mongo-sync/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | }
4 |
5 | dependencies {
6 | implementation(projects.connectors.mongo.mongoSync)
7 | implementation(libs.logback.classic)
8 | }
9 |
--------------------------------------------------------------------------------
/examples/mongo-sync/src/main/java/com/coditory/sherlock/samples/mongo/sync/MongoSyncLockSample.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.mongo.sync;
2 |
3 | import com.coditory.sherlock.DistributedLock;
4 | import com.coditory.sherlock.Sherlock;
5 | import com.coditory.sherlock.mongo.MongoSherlock;
6 | import com.mongodb.client.MongoClient;
7 | import com.mongodb.client.MongoClients;
8 | import com.mongodb.client.MongoCollection;
9 | import org.bson.Document;
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 |
13 | public class MongoSyncLockSample {
14 | private static final Logger logger = LoggerFactory.getLogger(MongoSyncLockSample.class);
15 |
16 | private static MongoCollection getCollection() {
17 | String database = "sherlock";
18 | String connectionString = "mongodb://localhost:27017/" + database;
19 | MongoClient mongoClient = MongoClients.create(connectionString);
20 | return mongoClient
21 | .getDatabase("sherlock")
22 | .getCollection("locks");
23 | }
24 |
25 | public static void main(String[] args) {
26 | Sherlock sherlock = MongoSherlock.create(getCollection());
27 | DistributedLock lock = sherlock.createLock("sample-lock");
28 | lock.runLocked(() -> logger.info("Lock acquired!"));
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/examples/mongo-sync/src/main/java/com/coditory/sherlock/samples/mongo/sync/MongoSyncMigrationSample.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.mongo.sync;
2 |
3 | import com.coditory.sherlock.Sherlock;
4 | import com.coditory.sherlock.migrator.SherlockMigrator;
5 | import com.coditory.sherlock.mongo.MongoSherlock;
6 | import com.mongodb.client.MongoClient;
7 | import com.mongodb.client.MongoClients;
8 | import com.mongodb.client.MongoCollection;
9 | import org.bson.Document;
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 |
13 | public class MongoSyncMigrationSample {
14 | private static final Logger logger = LoggerFactory.getLogger(MongoSyncMigrationSample.class);
15 |
16 | private static MongoCollection locksCollection() {
17 | String database = "sherlock";
18 | MongoClient mongoClient = MongoClients.create("mongodb://localhost:27017/" + database);
19 | return mongoClient
20 | .getDatabase("sherlock")
21 | .getCollection("locks");
22 | }
23 |
24 | public static void main(String[] args) {
25 | Sherlock sherlock = MongoSherlock.create(locksCollection());
26 | // first commit - all migrations are executed
27 | SherlockMigrator.builder(sherlock)
28 | .addChangeSet("change-set-1", () -> logger.info("Change-set 1"))
29 | .addChangeSet("change-set-2", () -> logger.info("Change-set 2"))
30 | .migrate();
31 | // second commit - only new change-set is executed
32 | SherlockMigrator.builder(sherlock)
33 | .addChangeSet("change-set-1", () -> logger.info("Change-set 1"))
34 | .addChangeSet("change-set-2", () -> logger.info("Change-set 2"))
35 | .addChangeSet("change-set-3", () -> logger.info("Change-set 3"))
36 | .migrate();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/examples/mongo-sync/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/mysql-coroutines/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.kotlin")
3 | }
4 |
5 | dependencies {
6 | implementation(projects.connectors.sql.sqlCoroutines)
7 | implementation(libs.r2dbc.mysql)
8 | implementation(libs.hikaricp)
9 | implementation(libs.logback.classic)
10 | }
11 |
--------------------------------------------------------------------------------
/examples/mysql-coroutines/src/main/kotlin/com/coditory/sherlock/samples/mysql/coroutines/MySqlKtLockSample.kt:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.mysql.coroutines
2 |
3 | import com.coditory.sherlock.sql.BindingMapper.MYSQL_MAPPER
4 | import com.coditory.sherlock.sql.coroutines.SqlSherlock
5 | import io.r2dbc.spi.ConnectionFactories
6 | import io.r2dbc.spi.ConnectionFactory
7 | import io.r2dbc.spi.ConnectionFactoryOptions
8 | import kotlinx.coroutines.runBlocking
9 | import org.slf4j.Logger
10 | import org.slf4j.LoggerFactory
11 |
12 | object MySqlKtLockSample {
13 | private val logger: Logger = LoggerFactory.getLogger(this.javaClass)
14 |
15 | private fun getConnectionFactory(): ConnectionFactory {
16 | val database = "test"
17 | val options =
18 | ConnectionFactoryOptions
19 | .parse("r2dbc:mysql://localhost:3306/$database")
20 | .mutate()
21 | .option(ConnectionFactoryOptions.USER, "mysql")
22 | .option(ConnectionFactoryOptions.PASSWORD, "mysql")
23 | .option(ConnectionFactoryOptions.DATABASE, database)
24 | .build()
25 | return ConnectionFactories.get(options)
26 | }
27 |
28 | private suspend fun sample() {
29 | val sherlock = SqlSherlock.create(getConnectionFactory(), MYSQL_MAPPER)
30 | val lock = sherlock.createLock("sample-lock")
31 | lock
32 | .runLocked { logger.info("Lock acquired!") }
33 | }
34 |
35 | @JvmStatic
36 | fun main(args: Array) {
37 | runBlocking { sample() }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/examples/mysql-coroutines/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/mysql-reactor/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | }
4 |
5 | dependencies {
6 | implementation(projects.connectors.sql.sqlReactor)
7 | implementation(libs.r2dbc.mysql)
8 | implementation(libs.mysql)
9 | implementation(libs.logback.classic)
10 | implementation(libs.hikaricp)
11 | }
12 |
--------------------------------------------------------------------------------
/examples/mysql-reactor/src/main/java/com/coditory/sherlock/samples/mysql/reactor/MySqlReactorLockSample.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.mysql.reactor;
2 |
3 | import com.coditory.sherlock.reactor.DistributedLock;
4 | import com.coditory.sherlock.reactor.Sherlock;
5 | import com.coditory.sherlock.sql.BindingMapper;
6 | import com.coditory.sherlock.sql.reactor.SqlSherlock;
7 | import io.r2dbc.spi.ConnectionFactories;
8 | import io.r2dbc.spi.ConnectionFactory;
9 | import io.r2dbc.spi.ConnectionFactoryOptions;
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 |
13 | public class MySqlReactorLockSample {
14 | private static final Logger logger = LoggerFactory.getLogger(MySqlReactorLockSample.class);
15 |
16 | private static ConnectionFactory getConnectionFactory() {
17 | String database = "test";
18 | ConnectionFactoryOptions options = ConnectionFactoryOptions
19 | .parse("r2dbc:mysql://localhost:3306/" + database)
20 | .mutate()
21 | .option(ConnectionFactoryOptions.USER, "mysql")
22 | .option(ConnectionFactoryOptions.PASSWORD, "mysql")
23 | .option(ConnectionFactoryOptions.DATABASE, database)
24 | .build();
25 | return ConnectionFactories.get(options);
26 | }
27 |
28 | public static void main(String[] args) {
29 | Sherlock sherlock = SqlSherlock.create(getConnectionFactory(), BindingMapper.MYSQL_MAPPER);
30 | DistributedLock lock = sherlock.createLock("sample-lock");
31 | lock
32 | .runLocked(() -> logger.info("Lock acquired!"))
33 | .block();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/examples/mysql-reactor/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/mysql-rxjava/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | }
4 |
5 | dependencies {
6 | implementation(projects.connectors.sql.sqlRxjava)
7 | implementation(libs.r2dbc.mysql)
8 | implementation(libs.mysql)
9 | implementation(libs.logback.classic)
10 | implementation(libs.hikaricp)
11 | }
12 |
--------------------------------------------------------------------------------
/examples/mysql-rxjava/src/main/java/com/coditory/sherlock/samples/mysql/rxjava/MySqlRxLockSample.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.mysql.rxjava;
2 |
3 | import com.coditory.sherlock.rxjava.DistributedLock;
4 | import com.coditory.sherlock.rxjava.Sherlock;
5 | import com.coditory.sherlock.sql.BindingMapper;
6 | import com.coditory.sherlock.sql.rxjava.SqlSherlock;
7 | import io.r2dbc.spi.ConnectionFactories;
8 | import io.r2dbc.spi.ConnectionFactory;
9 | import io.r2dbc.spi.ConnectionFactoryOptions;
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 |
13 | public class MySqlRxLockSample {
14 | private static final Logger logger = LoggerFactory.getLogger(MySqlRxLockSample.class);
15 |
16 | private static ConnectionFactory getConnectionFactory() {
17 | String database = "test";
18 | ConnectionFactoryOptions options = ConnectionFactoryOptions
19 | .parse("r2dbc:mysql://localhost:3306/" + database)
20 | .mutate()
21 | .option(ConnectionFactoryOptions.USER, "mysql")
22 | .option(ConnectionFactoryOptions.PASSWORD, "mysql")
23 | .option(ConnectionFactoryOptions.DATABASE, database)
24 | .build();
25 | return ConnectionFactories.get(options);
26 | }
27 |
28 | public static void main(String[] args) {
29 | Sherlock sherlock = SqlSherlock.create(getConnectionFactory(), BindingMapper.MYSQL_MAPPER);
30 | DistributedLock lock = sherlock.createLock("sample-lock");
31 | lock
32 | .runLocked(() -> logger.info("Lock acquired!"))
33 | .blockingGet();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/examples/mysql-rxjava/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/mysql-sync/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | }
4 |
5 | dependencies {
6 | implementation(projects.connectors.sql.sqlSync)
7 | implementation(libs.mysql)
8 | implementation(libs.logback.classic)
9 | implementation(libs.hikaricp)
10 | }
11 |
--------------------------------------------------------------------------------
/examples/mysql-sync/src/main/java/com/coditory/sherlock/samples/mysql/sync/MySqlSyncLockSample.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.mysql.sync;
2 |
3 | import com.coditory.sherlock.DistributedLock;
4 | import com.coditory.sherlock.Sherlock;
5 | import com.coditory.sherlock.sql.SqlSherlock;
6 | import com.zaxxer.hikari.HikariConfig;
7 | import com.zaxxer.hikari.HikariDataSource;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | import javax.sql.DataSource;
12 |
13 | public class MySqlSyncLockSample {
14 | private static final Logger logger = LoggerFactory.getLogger(MySqlSyncLockSample.class);
15 |
16 | private static DataSource dataSource() {
17 | HikariConfig config = new HikariConfig();
18 | config.setJdbcUrl("jdbc:mysql://localhost:3306/test");
19 | config.setUsername("mysql");
20 | config.setPassword("mysql");
21 | return new HikariDataSource(config);
22 | }
23 |
24 | public static void main(String[] args) {
25 | Sherlock sherlock = SqlSherlock.create(dataSource());
26 | DistributedLock lock = sherlock.createLock("sample-lock");
27 | lock.runLocked(() -> logger.info("Lock acquired!"));
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/examples/mysql-sync/src/main/java/com/coditory/sherlock/samples/mysql/sync/MySqlSyncMigrationSample.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.mysql.sync;
2 |
3 | import com.coditory.sherlock.Sherlock;
4 | import com.coditory.sherlock.migrator.SherlockMigrator;
5 | import com.coditory.sherlock.sql.SqlSherlock;
6 | import com.zaxxer.hikari.HikariConfig;
7 | import com.zaxxer.hikari.HikariDataSource;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | import javax.sql.DataSource;
12 |
13 | public class MySqlSyncMigrationSample {
14 | private static final Logger logger = LoggerFactory.getLogger(MySqlSyncMigrationSample.class);
15 |
16 | private static DataSource dataSource() {
17 | HikariConfig config = new HikariConfig();
18 | config.setJdbcUrl("jdbc:mysql://localhost:3306/test");
19 | config.setUsername("mysql");
20 | config.setPassword("mysql");
21 | return new HikariDataSource(config);
22 | }
23 |
24 | public static void main(String[] args) {
25 | Sherlock sherlock = SqlSherlock.create(dataSource());
26 | // first commit - all migrations are executed
27 | SherlockMigrator.builder(sherlock)
28 | .addChangeSet("change-set-1", () -> logger.info("Change-set 1"))
29 | .addChangeSet("change-set-2", () -> logger.info("Change-set 2"))
30 | .migrate();
31 | // second commit - only new change-set is executed
32 | SherlockMigrator.builder(sherlock)
33 | .addChangeSet("change-set-1", () -> logger.info("Change-set 1"))
34 | .addChangeSet("change-set-2", () -> logger.info("Change-set 2"))
35 | .addChangeSet("change-set-3", () -> logger.info("Change-set 3"))
36 | .migrate();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/examples/mysql-sync/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/postgres-coroutines/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.kotlin")
3 | }
4 |
5 | dependencies {
6 | implementation(projects.connectors.sql.sqlCoroutines)
7 | implementation(libs.r2dbc.postgresql)
8 | implementation(libs.hikaricp)
9 | implementation(libs.logback.classic)
10 | }
11 |
--------------------------------------------------------------------------------
/examples/postgres-coroutines/readme.md:
--------------------------------------------------------------------------------
1 | # Sample client code for distributed-lock
2 |
3 | Used only for sandbox testing
4 |
5 | - [In-memory Storage + Reactor API](src/main/java/com/coditory/sherlock/samples/inmem/InMemReactorSample.java)
6 | - [In-memory Storage + RxJava API](src/main/java/com/coditory/sherlock/samples/inmem/InMemRxJavaSample.java)
7 | - [In-memory Storage + Sync API](src/main/java/com/coditory/sherlock/samples/inmem/InMemSyncSample.java)
8 | - [Mongo Storage + Reactor API](src/main/java/com/coditory/sherlock/samples/mongo/MongoReactorSample.java)
9 | - [Mongo Storage + RxJava API](src/main/java/com/coditory/sherlock/samples/mongo/MongoRxJavaSample.java)
10 | - [Mongo Storage + Sync API](src/main/java/com/coditory/sherlock/samples/mongo/MongoSyncSample.java)
11 | - [Postgres Storage + Sync API](src/main/java/com/coditory/sherlock/samples/postgres/PostgresSyncSample.java)
12 | - [MySql Storage + Sync API](src/main/java/com/coditory/sherlock/samples/mysql/MySqlSyncSample.java)
--------------------------------------------------------------------------------
/examples/postgres-coroutines/src/main/kotlin/com/coditory/sherlock/samples/postgres/coroutines/PostgresKtLockSample.kt:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.postgres.coroutines
2 |
3 | import com.coditory.sherlock.sql.BindingMapper.POSTGRES_MAPPER
4 | import com.coditory.sherlock.sql.coroutines.SqlSherlock
5 | import io.r2dbc.spi.ConnectionFactories
6 | import io.r2dbc.spi.ConnectionFactory
7 | import io.r2dbc.spi.ConnectionFactoryOptions
8 | import kotlinx.coroutines.runBlocking
9 | import org.slf4j.Logger
10 | import org.slf4j.LoggerFactory
11 |
12 | object PostgresKtLockSample {
13 | private val logger: Logger = LoggerFactory.getLogger(this.javaClass)
14 |
15 | private fun getConnectionFactory(): ConnectionFactory {
16 | val database = "test"
17 | val options =
18 | ConnectionFactoryOptions
19 | .parse("r2dbc:postgresql://localhost:5432/$database")
20 | .mutate()
21 | .option(ConnectionFactoryOptions.USER, "postgres")
22 | .option(ConnectionFactoryOptions.PASSWORD, "postgres")
23 | .option(ConnectionFactoryOptions.DATABASE, database)
24 | .build()
25 | return ConnectionFactories.get(options)
26 | }
27 |
28 | private suspend fun sample() {
29 | val sherlock = SqlSherlock.create(getConnectionFactory(), POSTGRES_MAPPER)
30 | val lock = sherlock.createLock("sample-lock")
31 | lock
32 | .runLocked { logger.info("Lock acquired!") }
33 | }
34 |
35 | @JvmStatic
36 | fun main(args: Array) {
37 | runBlocking { sample() }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/examples/postgres-coroutines/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/postgres-reactor/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | }
4 |
5 | dependencies {
6 | implementation(projects.connectors.sql.sqlReactor)
7 | implementation(libs.r2dbc.postgresql)
8 | implementation(libs.postgresql)
9 | implementation(libs.logback.classic)
10 | implementation(libs.hikaricp)
11 | }
12 |
--------------------------------------------------------------------------------
/examples/postgres-reactor/src/main/java/com/coditory/sherlock/samples/postgres/reactor/PostgresReactorLockSample.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.postgres.reactor;
2 |
3 | import com.coditory.sherlock.reactor.DistributedLock;
4 | import com.coditory.sherlock.reactor.Sherlock;
5 | import com.coditory.sherlock.sql.reactor.SqlSherlock;
6 | import io.r2dbc.spi.ConnectionFactories;
7 | import io.r2dbc.spi.ConnectionFactory;
8 | import io.r2dbc.spi.ConnectionFactoryOptions;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 |
12 | import static com.coditory.sherlock.sql.BindingMapper.POSTGRES_MAPPER;
13 |
14 | public class PostgresReactorLockSample {
15 | private static final Logger logger = LoggerFactory.getLogger(PostgresReactorLockSample.class);
16 |
17 | private static ConnectionFactory getConnectionFactory() {
18 | String database = "test";
19 | ConnectionFactoryOptions options = ConnectionFactoryOptions
20 | .parse("r2dbc:postgresql://localhost:5432/" + database)
21 | .mutate()
22 | .option(ConnectionFactoryOptions.USER, "postgres")
23 | .option(ConnectionFactoryOptions.PASSWORD, "postgres")
24 | .option(ConnectionFactoryOptions.DATABASE, database)
25 | .build();
26 | return ConnectionFactories.get(options);
27 | }
28 |
29 | public static void main(String[] args) {
30 | Sherlock sherlock = SqlSherlock.create(getConnectionFactory(), POSTGRES_MAPPER);
31 | DistributedLock lock = sherlock.createLock("sample-lock");
32 | lock.runLocked(() -> logger.info("Lock acquired!"))
33 | .block();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/examples/postgres-reactor/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/postgres-rxjava/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | }
4 |
5 | dependencies {
6 | implementation(projects.connectors.sql.sqlRxjava)
7 | implementation(libs.r2dbc.postgresql)
8 | implementation(libs.postgresql)
9 | implementation(libs.logback.classic)
10 | implementation(libs.hikaricp)
11 | }
12 |
--------------------------------------------------------------------------------
/examples/postgres-rxjava/src/main/java/com/coditory/sherlock/samples/postgres/rxjava/PostgresRxLockSample.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.postgres.rxjava;
2 |
3 | import com.coditory.sherlock.rxjava.DistributedLock;
4 | import com.coditory.sherlock.rxjava.Sherlock;
5 | import com.coditory.sherlock.sql.rxjava.SqlSherlock;
6 | import io.r2dbc.spi.ConnectionFactories;
7 | import io.r2dbc.spi.ConnectionFactory;
8 | import io.r2dbc.spi.ConnectionFactoryOptions;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 |
12 | import static com.coditory.sherlock.sql.BindingMapper.POSTGRES_MAPPER;
13 |
14 | public class PostgresRxLockSample {
15 | private static final Logger logger = LoggerFactory.getLogger(PostgresRxLockSample.class);
16 |
17 | private static ConnectionFactory getConnectionFactory() {
18 | String database = "test";
19 | ConnectionFactoryOptions options = ConnectionFactoryOptions
20 | .parse("r2dbc:postgresql://localhost:5432/" + database)
21 | .mutate()
22 | .option(ConnectionFactoryOptions.USER, "postgres")
23 | .option(ConnectionFactoryOptions.PASSWORD, "postgres")
24 | .option(ConnectionFactoryOptions.DATABASE, database)
25 | .build();
26 | return ConnectionFactories.get(options);
27 | }
28 |
29 | public static void main(String[] args) {
30 | Sherlock sherlock = SqlSherlock.create(getConnectionFactory(), POSTGRES_MAPPER);
31 | DistributedLock lock = sherlock.createLock("sample-lock");
32 | lock.runLocked(() -> logger.info("Lock acquired!"))
33 | .blockingGet();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/examples/postgres-rxjava/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/postgres-sync/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | }
4 |
5 | dependencies {
6 | implementation(projects.connectors.sql.sqlSync)
7 | implementation(libs.postgresql)
8 | implementation(libs.logback.classic)
9 | implementation(libs.hikaricp)
10 | }
11 |
--------------------------------------------------------------------------------
/examples/postgres-sync/src/main/java/com/coditory/sherlock/samples/postgres/sync/PostgresSyncLockSample.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.postgres.sync;
2 |
3 | import com.coditory.sherlock.DistributedLock;
4 | import com.coditory.sherlock.Sherlock;
5 | import com.coditory.sherlock.sql.SqlSherlock;
6 | import com.zaxxer.hikari.HikariConfig;
7 | import com.zaxxer.hikari.HikariDataSource;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | import javax.sql.DataSource;
12 |
13 | public class PostgresSyncLockSample {
14 | private static final Logger logger = LoggerFactory.getLogger(PostgresSyncLockSample.class);
15 |
16 | private static DataSource dataSource() {
17 | HikariConfig config = new HikariConfig();
18 | config.setJdbcUrl("jdbc:postgresql://localhost:5432/test");
19 | config.setUsername("postgres");
20 | config.setPassword("postgres");
21 | return new HikariDataSource(config);
22 | }
23 |
24 | public static void main(String[] args) {
25 | Sherlock sherlock = SqlSherlock.create(dataSource());
26 | DistributedLock lock = sherlock.createLock("sample-lock");
27 | lock.runLocked(() -> logger.info("Lock acquired!"));
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/examples/postgres-sync/src/main/java/com/coditory/sherlock/samples/postgres/sync/PostgresSyncMigrationSample.java:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.samples.postgres.sync;
2 |
3 | import com.coditory.sherlock.Sherlock;
4 | import com.coditory.sherlock.migrator.SherlockMigrator;
5 | import com.coditory.sherlock.sql.SqlSherlock;
6 | import com.zaxxer.hikari.HikariConfig;
7 | import com.zaxxer.hikari.HikariDataSource;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | import javax.sql.DataSource;
12 |
13 | public class PostgresSyncMigrationSample {
14 | private static final Logger logger = LoggerFactory.getLogger(PostgresSyncMigrationSample.class);
15 |
16 | private static DataSource dataSource() {
17 | HikariConfig config = new HikariConfig();
18 | config.setJdbcUrl("jdbc:postgresql://localhost:5432/test");
19 | config.setUsername("postgres");
20 | config.setPassword("postgres");
21 | return new HikariDataSource(config);
22 | }
23 |
24 | public static void main(String[] args) {
25 | Sherlock sherlock = SqlSherlock.create(dataSource());
26 | // first commit - all migrations are executed
27 | SherlockMigrator.builder(sherlock)
28 | .addChangeSet("change-set-1", () -> logger.info("Change-set 1"))
29 | .addChangeSet("change-set-2", () -> logger.info("Change-set 2"))
30 | .migrate();
31 | // second commit - only new change-set is executed
32 | SherlockMigrator.builder(sherlock)
33 | .addChangeSet("change-set-1", () -> logger.info("Change-set 1"))
34 | .addChangeSet("change-set-2", () -> logger.info("Change-set 2"))
35 | .addChangeSet("change-set-3", () -> logger.info("Change-set 3"))
36 | .migrate();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/examples/postgres-sync/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | version=1.0.3
2 | org.gradle.jvmargs=-Xmx3g
3 | org.gradle.configuration-cache=true
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coditory/sherlock-distributed-lock/01e3ff4628833dd34db146661236f6905a615944/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/scripts/docker/README.md:
--------------------------------------------------------------------------------
1 | # Databases
2 |
3 | A [Docker](https://www.docker.com) composition with databases used by sherlock.
4 |
5 | ## Sample usage
6 |
7 | ```
8 | docker-compose up
9 | ```
10 |
11 | ## Important values
12 |
13 | ### Mongo
14 | - Connection string: `mongodb://localhost:27017`
15 |
16 | ### MySql
17 | - database: test
18 | - username: mysql
19 | - password: mysql
20 | - Jdbc connection: `jdbc:mysql://localhost:3306/test`
21 |
22 | ### PostgreSQL
23 | - database: test
24 | - username: postgres
25 | - password: postgres
26 | - Jdbc connection: `jdbc:postgresql://localhost:5432/test`
27 |
--------------------------------------------------------------------------------
/scripts/docker/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.8"
2 | name: sherlock_dbs
3 | services:
4 | mongo:
5 | image: mongo:7
6 | container_name: sherlock_dbs-mongo
7 | restart: on-failure
8 | volumes:
9 | - ./data/mongo:/data/db
10 | ports:
11 | - 27017:27017
12 | mysql:
13 | image: mysql:8
14 | container_name: sherlock_dbs-mysql
15 | command: --default-authentication-plugin=mysql_native_password
16 | environment:
17 | MYSQL_DATABASE: "test"
18 | MYSQL_USER: "mysql"
19 | MYSQL_PASSWORD: "mysql"
20 | # root username is 'root'
21 | MYSQL_ROOT_PASSWORD: "root"
22 | volumes:
23 | - ./data/mysql:/var/lib/mysql
24 | ports:
25 | - 3306:3306
26 | postgres:
27 | image: postgres:11
28 | container_name: sherlock_dbs-postgres
29 | environment:
30 | POSTGRES_DB: "test"
31 | POSTGRES_USER: "postgres"
32 | POSTGRES_PASSWORD: "postgres"
33 | volumes:
34 | - ./data/postgres:/var/lib/postgresql/data
35 | ports:
36 | - 5432:5432
37 |
--------------------------------------------------------------------------------
/scripts/git/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euf -o pipefail
3 |
4 | function run_ktlint {
5 | if ! command -v ktlint &>/dev/null; then
6 | echo -e "${RED}Please install Ktlint${ENDCOLOR} (https://pinterest.github.io/ktlint/latest/install/cli/#download-and-verification)"
7 | exit 1
8 | fi
9 | # https://pinterest.github.io/ktlint/0.49.0/install/cli/#git-hooks
10 | KT_FILES=$(git diff --name-only --cached --relative --diff-filter=ACMR -- '*.kt' '*.kts')
11 | if [ -n "$KT_FILES" ]; then
12 | echo -e "${BLUE}Ktlint: linting $(echo "$KT_FILES" | wc -l) files${ENDCOLOR}"
13 | start="$(date +%s)"
14 | echo -n "$KT_FILES" | tr '\n' ',' | ktlint --relative --format --patterns-from-stdin=','
15 | echo -e "${GREEN}Ktlint: finished in $(($(date +%s) - start))s${ENDCOLOR}"
16 | echo "$KT_FILES" | xargs git add
17 | fi
18 | }
19 |
20 | if [ "${NO_COLOR:-}" != "false" ]; then
21 | RED="\e[31m"
22 | GREEN="\e[32m"
23 | BLUE="\e[34m"
24 | ENDCOLOR="\e[0m"
25 | else
26 | RED=""
27 | GREEN=""
28 | BLUE=""
29 | ENDCOLOR=""
30 | fi
31 |
32 | prestart="$(date +%s)"
33 | echo -e "${BLUE}Pre-commit: Starting${ENDCOLOR}"
34 |
35 | if [ ./scripts/git/pre-commit -nt .git/hooks/pre-commit ]; then
36 | cp -f ./scripts/git/pre-commit .git/hooks/pre-commit
37 | echo -e "${RED}Updated git pre-commit hook. Please re-run commit.${ENDCOLOR}"
38 | exit 1
39 | fi
40 |
41 | run_ktlint
42 |
43 | echo -e "${GREEN}Pre-commit: finished in $(($(date +%s) - prestart))s${ENDCOLOR}"
44 |
--------------------------------------------------------------------------------
/tests/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("build.java")
3 | id("build.kotlin")
4 | }
5 |
6 | dependencies {
7 | api(projects.api.apiCommon)
8 | api(projects.api.apiSync)
9 | api(projects.api.apiReactor)
10 | api(projects.api.apiRxjava)
11 | api(projects.api.apiCoroutines)
12 | api(projects.common)
13 | api(libs.spock.core)
14 | api(libs.jsonassert)
15 | api(libs.awaitility)
16 | api(libs.logback.core)
17 | api(libs.logback.classic)
18 | api(libs.kotlinx.coroutines.core)
19 | }
20 |
--------------------------------------------------------------------------------
/tests/src/main/groovy/com/coditory/sherlock/HandleDbFailureSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock
2 |
3 | import com.coditory.sherlock.base.DatabaseManager
4 | import com.coditory.sherlock.base.LockAssertions
5 | import com.coditory.sherlock.base.LockTypes
6 |
7 | abstract class HandleDbFailureSpec extends LocksBaseSpec implements LockAssertions, DatabaseManager {
8 | String lockId = "acquire-id"
9 | String instanceId = "instance-id"
10 |
11 | def "should reconnect after DB failure"() {
12 | given:
13 | sherlock.initialize()
14 | DistributedLock lock = createLock(LockTypes.SINGLE_ENTRANT, lockId, instanceId)
15 | when:
16 | stopDatabase()
17 | lock.acquire()
18 | then:
19 | SherlockException e = thrown(SherlockException)
20 | e.getMessage().startsWith("Could not acquire lock")
21 |
22 | when:
23 | startDatabase()
24 | boolean result = lock.acquire()
25 | then:
26 | result == true
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/tests/src/main/groovy/com/coditory/sherlock/InfiniteAcquireLockSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock
2 |
3 | import com.coditory.sherlock.base.LockAssertions
4 | import spock.lang.Unroll
5 |
6 | import static com.coditory.sherlock.base.LockTypes.allLockTypes
7 |
8 | abstract class InfiniteAcquireLockSpec extends LocksBaseSpec implements LockAssertions {
9 | @Unroll
10 | def "infinite lock should stay acquired even when default lock duration passes - #type"() {
11 | given:
12 | DistributedLock lock = createLock(type, sampleLockId, sampleOwnerId)
13 | and:
14 | lock.acquireForever()
15 |
16 | when:
17 | fixedClock.tick(defaultLockDuration)
18 | then:
19 | assertAcquired(lock.id)
20 |
21 | where:
22 | type << allLockTypes()
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/tests/src/main/groovy/com/coditory/sherlock/base/DatabaseManager.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.base
2 |
3 | import groovy.transform.CompileStatic
4 |
5 | @CompileStatic
6 | interface DatabaseManager {
7 | void stopDatabase()
8 |
9 | void startDatabase()
10 | }
11 |
--------------------------------------------------------------------------------
/tests/src/main/groovy/com/coditory/sherlock/base/DistributedLocksCreator.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.base
2 |
3 | import com.coditory.sherlock.Sherlock
4 | import groovy.transform.CompileStatic
5 |
6 | import java.time.Clock
7 | import java.time.Duration
8 |
9 | @CompileStatic
10 | interface DistributedLocksCreator {
11 | Sherlock createSherlock(String ownerId, Duration duration, Clock clock, String collectionName);
12 | }
13 |
--------------------------------------------------------------------------------
/tests/src/main/groovy/com/coditory/sherlock/base/JsonAssert.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.base
2 |
3 | import org.skyscreamer.jsonassert.JSONCompare
4 | import org.skyscreamer.jsonassert.JSONCompareMode
5 | import org.skyscreamer.jsonassert.JSONCompareResult
6 |
7 | class JsonAssert {
8 | static boolean assertJsonEqual(String actual, String expected) {
9 | assertJsonEqualWithMode(actual, expected, JSONCompareMode.STRICT)
10 | return true
11 | }
12 |
13 | static boolean assertJsonLenientEqual(String actual, String expected) {
14 | assertJsonEqualWithMode(actual, expected, JSONCompareMode.LENIENT)
15 | return true
16 | }
17 |
18 | private static void assertJsonEqualWithMode(String actual, String expected, JSONCompareMode mode) {
19 | JSONCompareResult result = JSONCompare.compareJSON(expected, actual, mode)
20 | if (result.failed()) {
21 | String message = """
22 | |Error:
23 | |${result.getMessage()}
24 | |
25 | |Actual:
26 | |${actual}
27 | |
28 | |Expected:
29 | |${expected}
30 | """.stripMargin()
31 | assert false: message
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tests/src/main/groovy/com/coditory/sherlock/base/LockAssertions.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.base
2 |
3 | import com.coditory.sherlock.DistributedLock
4 | import com.coditory.sherlock.LocksBaseSpec
5 | import groovy.transform.CompileStatic
6 | import groovy.transform.SelfType
7 |
8 | import java.time.Duration
9 | import java.time.Instant
10 |
11 | import static LockTypes.SINGLE_ENTRANT
12 |
13 | @CompileStatic
14 | @SelfType(LocksBaseSpec)
15 | trait LockAssertions {
16 | boolean assertAcquired(String lockId) {
17 | DistributedLock otherLock = createLockWithRandomOwner(lockId)
18 | assert otherLock.acquire() == false
19 | return true
20 | }
21 |
22 | boolean assertAcquired(String lockId, Duration duration) {
23 | DistributedLock otherLock = createLockWithRandomOwner(lockId)
24 | assert otherLock.acquire() == false
25 | Instant backup = fixedClock.instant()
26 | fixedClock.tick(duration - Duration.ofMillis(1))
27 | assert otherLock.acquire() == false
28 | fixedClock.tick(Duration.ofMillis(1))
29 | assert otherLock.acquire() == true
30 | otherLock.release()
31 | fixedClock.setup(backup)
32 | return true
33 | }
34 |
35 | boolean assertAcquiredForever(String lockId) {
36 | DistributedLock otherLock = createLockWithRandomOwner(lockId)
37 | assert otherLock.acquire() == false
38 | Instant backup = fixedClock.instant()
39 | fixedClock.tick(Duration.ofDays(100))
40 | assert otherLock.acquire() == false
41 | fixedClock.setup(backup)
42 | return true
43 | }
44 |
45 | boolean assertReleased(String lockId) {
46 | DistributedLock otherLock = createLockWithRandomOwner(lockId)
47 | assert otherLock.acquire() == true
48 | otherLock.release()
49 | return true
50 | }
51 |
52 | private DistributedLock createLockWithRandomOwner(String lockId) {
53 | return createLock(SINGLE_ENTRANT, lockId, UUID.randomUUID().toString())
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/tests/src/main/groovy/com/coditory/sherlock/base/LockTypes.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.base
2 |
3 | import com.coditory.sherlock.DistributedLock
4 | import com.coditory.sherlock.DistributedLockBuilder
5 | import com.coditory.sherlock.Sherlock
6 | import groovy.transform.CompileStatic
7 |
8 | @CompileStatic
9 | enum LockTypes {
10 | SINGLE_ENTRANT{
11 | @Override
12 | DistributedLockBuilder createLock(Sherlock sherlock) {
13 | return sherlock.createLock()
14 | }
15 | },
16 | REENTRANT{
17 | @Override
18 | DistributedLockBuilder createLock(Sherlock sherlock) {
19 | return sherlock.createReentrantLock()
20 | }
21 | },
22 | OVERRIDING{
23 | @Override
24 | DistributedLockBuilder createLock(Sherlock sherlock) {
25 | return sherlock.createOverridingLock()
26 | }
27 | };
28 |
29 | abstract DistributedLockBuilder createLock(Sherlock sherlock);
30 |
31 | static List allLockTypes() {
32 | return values().toList()
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tests/src/main/groovy/com/coditory/sherlock/base/SpecSimulatedException.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.base
2 |
3 | import groovy.transform.CompileStatic
4 |
5 | @CompileStatic
6 | class SpecSimulatedException extends RuntimeException {
7 | static throwSpecSimulatedException() {
8 | throw new SpecSimulatedException()
9 | }
10 |
11 | SpecSimulatedException() {
12 | this("Simulated exception for test")
13 | }
14 |
15 | SpecSimulatedException(String message) {
16 | super(message)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/tests/src/main/groovy/com/coditory/sherlock/base/UpdatableFixedClock.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.base
2 |
3 | import groovy.transform.CompileStatic
4 |
5 | import java.time.Clock
6 | import java.time.Duration
7 | import java.time.Instant
8 | import java.time.ZoneId
9 |
10 | import static java.time.Duration.ofDays
11 | import static java.time.Duration.ofNanos
12 | import static java.util.Objects.requireNonNull
13 |
14 | @CompileStatic
15 | class UpdatableFixedClock extends Clock {
16 | // Always use instant with nanos for testing. Some databases (like mongo) trim nanos - you should test for that!
17 | public static final Instant DEFAULT_FIXED_TIME = Instant.parse('2015-12-03T10:15:30.123456Z')
18 | public static final ZoneId DEFAULT_ZONE_ID = ZoneId.of('Europe/Warsaw')
19 |
20 | static final UpdatableFixedClock defaultUpdatableFixedClock() {
21 | return new UpdatableFixedClock(DEFAULT_FIXED_TIME, DEFAULT_ZONE_ID)
22 | }
23 |
24 | private final ZoneId zoneId
25 | private Instant fixedTime
26 |
27 | private UpdatableFixedClock(Instant fixedTime, ZoneId zoneId) {
28 | this.fixedTime = requireNonNull(fixedTime)
29 | this.zoneId = requireNonNull(zoneId)
30 | }
31 |
32 | @Override
33 | ZoneId getZone() {
34 | return zoneId
35 | }
36 |
37 | @Override
38 | UpdatableFixedClock withZone(ZoneId zone) {
39 | return new UpdatableFixedClock(this.fixedTime, zone)
40 | }
41 |
42 | @Override
43 | Instant instant() {
44 | return fixedTime
45 | }
46 |
47 | Instant futureInstant(Duration duration = ofDays(1)) {
48 | return this.fixedTime + duration
49 | }
50 |
51 | Instant pastInstant(Duration duration = ofDays(1)) {
52 | return this.fixedTime - duration
53 | }
54 |
55 | void reset() {
56 | this.fixedTime = DEFAULT_FIXED_TIME
57 | }
58 |
59 | void tick(Duration duration = ofNanos(1)) {
60 | this.fixedTime = this.fixedTime + duration
61 | }
62 |
63 | void setup(Instant instant) {
64 | this.fixedTime = instant
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/tests/src/main/groovy/com/coditory/sherlock/migrator/MigratorBaseSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.migrator
2 |
3 | import com.coditory.sherlock.LocksBaseSpec
4 | import com.coditory.sherlock.migrator.base.MigratorCreator
5 |
6 | abstract class MigratorBaseSpec extends LocksBaseSpec implements MigratorCreator {
7 |
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/tests/src/main/groovy/com/coditory/sherlock/migrator/base/BlockingMigrator.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.migrator.base
2 |
3 | import com.coditory.sherlock.migrator.MigrationResult
4 |
5 | interface BlockingMigrator {
6 | MigrationResult migrate()
7 | }
8 |
9 | interface BlockingMigratorBuilder {
10 |
11 | BlockingMigratorBuilder setMigrationId(String migrationId)
12 |
13 | BlockingMigratorBuilder addChangeSet(String changeSetId, Runnable changeSet)
14 |
15 | BlockingMigratorBuilder addAnnotatedChangeSets(Object object)
16 |
17 | BlockingMigrator build()
18 |
19 | MigrationResult migrate()
20 | }
--------------------------------------------------------------------------------
/tests/src/main/groovy/com/coditory/sherlock/migrator/base/MigratorCreator.groovy:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock.migrator.base
2 |
3 | import com.coditory.sherlock.Sherlock
4 | import com.coditory.sherlock.base.DistributedLocksCreator
5 | import groovy.transform.CompileStatic
6 |
7 | @CompileStatic
8 | interface MigratorCreator extends DistributedLocksCreator {
9 | BlockingMigratorBuilder createMigratorBuilder(Sherlock sherlock)
10 | }
--------------------------------------------------------------------------------
/tests/src/main/kotlin/com/coditory/sherlock/BlockingKtDistributedLock.kt:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock
2 |
3 | import kotlinx.coroutines.runBlocking
4 | import java.time.Duration
5 |
6 | class BlockingKtDistributedLock(
7 | private val lock: com.coditory.sherlock.coroutines.DistributedLock,
8 | ) : DistributedLock {
9 | override fun getId(): String {
10 | return lock.id
11 | }
12 |
13 | override fun acquire(): Boolean = runBlocking {
14 | lock.acquire()
15 | }
16 |
17 | override fun acquire(duration: Duration): Boolean = runBlocking {
18 | lock.acquire(duration)
19 | }
20 |
21 | override fun acquireForever(): Boolean = runBlocking {
22 | lock.acquireForever()
23 | }
24 |
25 | override fun release(): Boolean = runBlocking {
26 | lock.release()
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/tests/src/main/kotlin/com/coditory/sherlock/BlockingKtSherlockWrapper.kt:
--------------------------------------------------------------------------------
1 | package com.coditory.sherlock
2 |
3 | import kotlinx.coroutines.runBlocking
4 |
5 | class BlockingKtSherlockWrapper(
6 | private val sherlock: com.coditory.sherlock.coroutines.Sherlock,
7 | ) : Sherlock {
8 | fun unwrap(): com.coditory.sherlock.coroutines.Sherlock = sherlock
9 |
10 | override fun initialize() = runBlocking {
11 | sherlock.initialize()
12 | }
13 |
14 | override fun createLock(): DistributedLockBuilder {
15 | return blockingLockBuilder(sherlock.createLock())
16 | }
17 |
18 | override fun createReentrantLock(): DistributedLockBuilder {
19 | return blockingLockBuilder(sherlock.createReentrantLock())
20 | }
21 |
22 | override fun createOverridingLock(): DistributedLockBuilder {
23 | return blockingLockBuilder(sherlock.createOverridingLock())
24 | }
25 |
26 | override fun forceReleaseAllLocks(): Boolean = runBlocking {
27 | sherlock.forceReleaseAllLocks()
28 | }
29 |
30 | override fun forceReleaseLock(lockId: String): Boolean {
31 | return createOverridingLock(lockId)
32 | .release()
33 | }
34 |
35 | private fun blockingLockBuilder(
36 | builder: DistributedLockBuilder,
37 | ): DistributedLockBuilder {
38 | return builder.withMappedLock { lock -> BlockingKtDistributedLock(lock) }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/tests/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------