This library adds support for Database sharding in Dropwizard based applications. Make sure you're familiar with Dropwizard, dependency injection framework like Guice and concepts like ThreadLocals before going ahead.
License: Apache 2.0
Stable version: 0.2.18
- Traditionally in Dropwizard, to open a database transaction
@UnitOfWork
is put on every Jersey-resource method. In case@UnitOfWork
is required at places, other than Jersey-resources a more verbose approach is required as mentiond here. - Nested
@UnitOfWork
don't work in Dropwizard, which are some times needed in a sharding use-case. - Dropwizard doesn't support Hibernate's multi-tenancy.
- The original library from where this library has been forked, advocates that database-transaction should only be managed at DAO layer, while it may work in their use-case, for Cleartax being a finance system, clean rollback of transaction (which may include multiple entities in some cases) is very important.
This library solves above problems by:
- Using Guice method interceptors to seamlessly use
@UnitOfWork
in anywhere in the code. Assumption: Methods that are annotated with@UnitOfWork
aren't private and the class must be created via Guice dependency injection. - Handle nested
@UnitOfWork
within the same thread. How? - Uses Hibernate's multi-tenancy support and integrates it with Dropwizard.
- Shard/Tenant mean the same thing, which is the physical database.
- Shard-id/tenant-id also mean the same thing, which is the id of the physical database. (Refer point no. 2 in High-level section)
- Shard-key is the ID on which data is sharded, e.g. if you're sharding by user-id, then user-id becomes your shard-key.
- Bucket is an intermediate virtual-shard to which shard-key gets mapped to.
- Bucket-id gets mapped to the shard-id.
- Include as dependency:
<dependency>
<groupId>in.cleartax.dropwizard</groupId>
<artifactId>sharding-core</artifactId>
<version>0.2.8</version>
</dependency>
for liquibase migration support, you can also include:
<dependency>
<groupId>in.cleartax.dropwizard</groupId>
<artifactId>sharding-migrations</artifactId>
<version>0.2.8</version>
</dependency>
-
Update your Dropwizard YML config to declare all the hosts as described here.
-
Understand Guice's AbstractModule and @Provides.
-
Define all the dependencies in an extension to
AbstractModule
that binds all your classes. e.g. refer this. -
Register your module in your Dropwizard application as described here.
Consider this method which is annotated with @UnitOfWork
.
UnitOfWorkInterceptor by using Guice's AOP would intercept the method call and figure out the shard-id/tenant-id then initiate the transaction. For this to work, you would need to do:
Map all the shard-keys to bucket
UnitOfWorkInterceptor
calls the implementation of ShardKeyProvider to map the shard-key to a bucket.
Map all the buckets to shards
UnitOfWorkInterceptor
calls the implementation of BucketResolver to figure out shard-id. Refer DbBasedShardResolver to understand one example use-case.
In this example, refer this SQL script where the mappings are done.
Connect to right shard, by
-
Setup shard-key for every incoming HTTP request - Refer ShardKeyFeature in the example project. Note:
ShardKeyProvider
in the example is bounded to it's implementation in the Guice module described earlier. -
Setup shard-key manually - Get an instance of
ShardKeyProvider
and then do:
try {
shardKeyProvider.setKey("shard-key")
// Call your method which is annotated with @UnitOfWork
} finally {
shardKeyProvider.clear();
}
Example use-case: In case you're invoking your code outside of HTTP layer, or you're creating a child-thread which may not have all the context of parent.
- Connect to a shard by explicitly mentioning shard-id
try {
DelegatingTenantResolver.getInstance().setDelegate(new ConstTenantIdentifierResolver("your shard-id/tenant-id"));
// Call your method which is annotated with @UnitOfWork
} finally {
if (DelegatingTenantResolver.getInstance().hasTenantIdentifier()) {
DelegatingTenantResolver.getInstance().setDelegate(null);
}
}
Example use-case: This might be useful in case you're aggregating data from across all the shards
- Connect to a shard without using @UnitOfWork
This is not recommended because then you'll need hibernate specific objects in your business code. In case required, you can instantiate TransactionRunner and use it as mentioned here
Note: In case you don't need sharding but still need flexibility of using @UnitOfWork
outside of resources and ability to use nested @UnitOfWork
you can still do so. Refer these tests to understand the use-case.