Skip to content

Commit

Permalink
Add Composite Sharding implementation (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
shashankrnr32 committed Apr 1, 2023
1 parent a1d7e0a commit 77b6665
Show file tree
Hide file tree
Showing 19 changed files with 299 additions and 82 deletions.
24 changes: 12 additions & 12 deletions .github/workflows/maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: maven
- name: Build with Maven
run: mvn -B package --file pom.xml
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: maven
- name: Build with Maven
run: mvn -B package --file pom.xml

# Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
- name: Update dependency graph
uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6
# Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
- name: Update dependency graph
uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6
19 changes: 14 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
[![Maven Package](https://github.com/shashankrnr32/mongodb-application-sharding/actions/workflows/maven-publish.yml/badge.svg)](https://github.com/shashankrnr32/mongodb-application-sharding/actions/workflows/maven-publish.yml)

Mongo DB Application Sharding allows you to shard your Mongo DB cluster from your application using different
strategies.
strategies. This project is inspired by [Apache's shardingsphere](https://github.com/apache/shardingsphere) which
enables the users to shard the relational databases through the application.

Application Sharding Strategies supported by the library

Expand All @@ -20,13 +21,13 @@ as a dependency to your project.
```xml
<!-- sharding-core -->
<dependency>
<groupId>com.alpha</groupId>
<groupId>com.alpha.mongodb</groupId>
<artifactId>sharding-core</artifactId>
<version>${mongodb.sharding.version}</version>
</dependency>
```

## Collection Sharding Strategy
### Collection Sharding Strategy

Sharding strategy where data is divided into multiple collections in a single database identified by a shardHint
(usually a suffix to the collection name)
Expand All @@ -38,7 +39,7 @@ TEST_1
TEST_2
```

### How to use?
#### How to use?

1. Create a custom `MongoTemplate` bean using the snippet given in
[this example](sharding-example/src/main/java/com/alpha/mongodb/sharding/example/configuration/CollectionShardedMongoConfiguration.java)
Expand Down Expand Up @@ -91,7 +92,7 @@ public class TestShardedEntity implements CollectionShardedEntity {

4. Voila!.

### Sharding Hint
#### Sharding Hint

In order to route the write queries, the entities are supposed to implement from CollectionShardedEntity. But, the find
queries can take place with different criterion, with different fields. In order to route the find query to the right
Expand All @@ -116,6 +117,14 @@ If the sharding hint is not set, methods will throw
a [`UnresolvableShardException`](sharding-core/src/main/java/com/alpha/mongodb/sharding/core/exception/UnresolvableShardException.java)
.

### Database Sharding Strategy

Coming soon...!

### Composite Sharding Strategy

Coming soon...!

## Author

[Shashank Sharma](https://github.com/shashankrnr32)
Expand Down
8 changes: 4 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

Expand All @@ -10,7 +10,7 @@
<version>2.7.10</version>
</parent>

<groupId>com.alpha</groupId>
<groupId>com.alpha.mongodb</groupId>
<artifactId>mongodb-application-sharding</artifactId>
<packaging>pom</packaging>
<version>${revision}</version>
Expand All @@ -20,11 +20,11 @@
</modules>

<properties>
<revision>0.0.1</revision>
<project.java.version>1.8</project.java.version>
<maven.compiler.source>${project.java.version}</maven.compiler.source>
<maven.compiler.target>${project.java.version}</maven.compiler.target>
<delombok.output>target/docs</delombok.output>
<revision>0.0.1</revision>
</properties>

<dependencies>
Expand Down
7 changes: 3 additions & 4 deletions sharding-core/pom.xml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>mongodb-application-sharding</artifactId>
<groupId>com.alpha</groupId>
<groupId>com.alpha.mongodb</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
Expand Down Expand Up @@ -60,7 +60,6 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>

</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,14 @@ public boolean collectionExists(String collectionName) {
}

public boolean collectionExists(String collectionName, final String collectionHint) {
return super.collectionExists(resolveName(collectionName, collectionHint));
return super.collectionExists(getShardingOptions().resolveCollectionName(collectionName, collectionHint));
}

@Override
protected String resolveCollectionNameWithoutEntityContext(String collectionName) throws UnresolvableCollectionShardException {
String hint = resolveCollectionHintWithoutEntityContext();
validateCollectionHint(collectionName, hint);
return resolveName(collectionName, hint);
return getShardingOptions().resolveCollectionName(collectionName, hint);
}

@NonNull
Expand All @@ -158,13 +158,13 @@ private <T> String resolveCollectionNameWithEntityContext(final String collectio
if (entity instanceof CollectionShardedEntity) {
String hint = ((CollectionShardedEntity) entity).resolveCollectionHint();
validateCollectionHint(collectionName, hint);
resolvedCollectionName = resolveName(collectionName, hint);
resolvedCollectionName = getShardingOptions().resolveCollectionName(collectionName, hint);
} else {
Optional<ShardingHint> shardingHint = ShardingHintManager.get();
if (shardingHint.isPresent() && null != shardingHint.get().getCollectionHint()) {
String hint = shardingHint.get().getCollectionHint();
validateCollectionHint(collectionName, hint);
resolvedCollectionName = resolveName(collectionName, hint);
resolvedCollectionName = getShardingOptions().resolveCollectionName(collectionName, hint);
} else {
throw new UnresolvableCollectionShardException();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.alpha.mongodb.sharding.core;

import com.alpha.mongodb.sharding.core.configuration.CompositeShardingOptions;
import com.mongodb.client.MongoClient;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.query.Query;

import java.util.Map;

/**
* Composite Sharded Mongo Template to be used with collections that are
* sharded both by Database and Collection
*
* @author Shashank Sharma
*/
public class CompositeShardedMongoTemplate extends DatabaseShardedMongoTemplate {

public CompositeShardedMongoTemplate(MongoClient mongoClient, String databaseName, CompositeShardingOptions shardingOptions) {
super(mongoClient, databaseName, shardingOptions);
}

public CompositeShardedMongoTemplate(Map<String, MongoDatabaseFactory> delegatedDatabaseFactory, CompositeShardingOptions shardingOptions) {
super(delegatedDatabaseFactory, shardingOptions);
}

public CompositeShardedMongoTemplate(Map<String, MongoDatabaseFactory> delegatedDatabaseFactory, MongoConverter mongoConverter, CompositeShardingOptions shardingOptions) {
super(delegatedDatabaseFactory, mongoConverter, shardingOptions);
}

@Override
public long countFromAll(Query query, Class<?> entityClass) {
return this.getDelegatedShardedMongoTemplateMap().values().stream().mapToLong(mongoTemplate -> ((CollectionShardedMongoTemplate) mongoTemplate).countFromAll(query, entityClass)).sum();
}

@Override
public long countFromAll(Query query, String collectionName) {
return this.getDelegatedShardedMongoTemplateMap().values().stream().mapToLong(mongoTemplate -> ((CollectionShardedMongoTemplate) mongoTemplate).countFromAll(query, collectionName)).sum();
}

@Override
public long countFromAll(Query query, Class<?> entityClass, String collectionName) {
return this.getDelegatedShardedMongoTemplateMap().values().stream().mapToLong(mongoTemplate -> ((CollectionShardedMongoTemplate) mongoTemplate).countFromAll(query, entityClass, collectionName)).sum();
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.alpha.mongodb.sharding.core;

import com.alpha.mongodb.sharding.core.configuration.CompositeShardingOptions;
import com.alpha.mongodb.sharding.core.configuration.DatabaseShardingOptions;
import com.alpha.mongodb.sharding.core.entity.DatabaseShardedEntity;
import com.alpha.mongodb.sharding.core.exception.UnresolvableDatabaseShardException;
import com.mongodb.client.MongoClient;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import lombok.AccessLevel;
import lombok.Getter;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.core.FindAndModifyOptions;
import org.springframework.data.mongodb.core.FindAndReplaceOptions;
Expand All @@ -19,32 +22,54 @@
import java.util.*;

/**
* Database Sharded Mongo Template. To be used for collections with same names across multiple database shards
* Database Sharded Mongo Template. To be used for collections with same
* names across multiple database shards
*
* @author Shashank Sharma
*/
public class DatabaseShardedMongoTemplate extends ShardedMongoTemplate {

Map<String, MongoTemplate> delegatedShardedMongoTemplateMap = new HashMap<>();
@Getter(value = AccessLevel.PROTECTED)
private final Map<String, MongoTemplate> delegatedShardedMongoTemplateMap = new HashMap<>();

public DatabaseShardedMongoTemplate(MongoClient mongoClient, String databaseName, DatabaseShardingOptions shardingOptions) {
super(mongoClient, resolveName(databaseName, shardingOptions.getShardSeparator(), shardingOptions.getDefaultDatabaseHint()), shardingOptions);
shardingOptions.getDatabaseHintsSet().forEach(shardHint -> {
delegatedShardedMongoTemplateMap.put(shardHint, new MongoTemplate(new SimpleMongoClientDatabaseFactory(mongoClient, databaseName), (MongoConverter) null));
super(mongoClient, shardingOptions.resolveDatabaseName(databaseName, shardingOptions.getDefaultDatabaseHint()), shardingOptions);
shardingOptions.getDefaultDatabaseHintsSet().forEach(shardHint -> {
if (shardingOptions instanceof CompositeShardingOptions) {
delegatedShardedMongoTemplateMap.put(shardHint, new CollectionShardedMongoTemplate(
new SimpleMongoClientDatabaseFactory(mongoClient, databaseName),
((CompositeShardingOptions) shardingOptions).getDelegatedCollectionShardingOptions()));
} else {
delegatedShardedMongoTemplateMap.put(shardHint, new MongoTemplate(new SimpleMongoClientDatabaseFactory(mongoClient, databaseName), null));
}
});
}

public DatabaseShardedMongoTemplate(Map<String, MongoDatabaseFactory> delegatedDatabaseFactory, DatabaseShardingOptions shardingOptions) {
super(delegatedDatabaseFactory.get(shardingOptions.getDefaultDatabaseHint()), shardingOptions);
shardingOptions.getDatabaseHintsSet().forEach(shardHint -> {
delegatedShardedMongoTemplateMap.put(shardHint, new MongoTemplate(delegatedDatabaseFactory.get(shardHint), null));
shardingOptions.getDefaultDatabaseHintsSet().forEach(shardHint -> {
if (shardingOptions instanceof CompositeShardingOptions) {
delegatedShardedMongoTemplateMap.put(shardHint, new CollectionShardedMongoTemplate(
delegatedDatabaseFactory.get(shardHint),
((CompositeShardingOptions) shardingOptions).getDelegatedCollectionShardingOptions()));
} else {
delegatedShardedMongoTemplateMap.put(shardHint, new MongoTemplate(delegatedDatabaseFactory.get(shardHint), null));
}
});
}

public DatabaseShardedMongoTemplate(Map<String, MongoDatabaseFactory> delegatedDatabaseFactory, MongoConverter mongoConverter, DatabaseShardingOptions shardingOptions) {
super(delegatedDatabaseFactory.get(shardingOptions.getDefaultDatabaseHint()), mongoConverter, shardingOptions);
shardingOptions.getDatabaseHintsSet().forEach(shardHint -> {
delegatedShardedMongoTemplateMap.put(shardHint, new MongoTemplate(delegatedDatabaseFactory.get(shardHint), mongoConverter));
shardingOptions.getDefaultDatabaseHintsSet().forEach(shardHint -> {
if (shardingOptions instanceof CompositeShardingOptions) {
delegatedShardedMongoTemplateMap.put(shardHint, new CollectionShardedMongoTemplate(
delegatedDatabaseFactory.get(shardHint),
mongoConverter,
((CompositeShardingOptions) shardingOptions).getDelegatedCollectionShardingOptions()));
} else {
delegatedShardedMongoTemplateMap.put(shardHint, new MongoTemplate(delegatedDatabaseFactory.get(shardHint), mongoConverter));
}

});
}

Expand Down Expand Up @@ -373,17 +398,17 @@ public <T> List<T> findDistinct(Query query, String field, String collectionName

@Override
public long countFromAll(Query query, Class<?> entityClass) {
return 0;
return this.delegatedShardedMongoTemplateMap.values().stream().mapToLong(mongoTemplate -> mongoTemplate.count(query, entityClass)).sum();
}

@Override
public long countFromAll(Query query, String collectionName) {
return 0;
return this.delegatedShardedMongoTemplateMap.values().stream().mapToLong(mongoTemplate -> mongoTemplate.count(query, collectionName)).sum();
}

@Override
public long countFromAll(Query query, Class<?> entityClass, String collectionName) {
return 0;
return this.delegatedShardedMongoTemplateMap.values().stream().mapToLong(mongoTemplate -> mongoTemplate.count(query, entityClass, collectionName)).sum();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;

import java.util.Optional;

/**
* Abstract Base Sharded Mongo Template
*
* @author Shashank Sharma
*/
public abstract class ShardedMongoTemplate extends MongoTemplate {

@Getter
Expand All @@ -36,14 +40,6 @@ public ShardedMongoTemplate(MongoDatabaseFactory mongoDbFactory, MongoConverter
this.shardingOptions = shardingOptions;
}

protected String resolveName(@NonNull final String name, @NonNull final String hint) {
return String.format("%s%s%s", name, shardingOptions.getShardSeparator(), hint);
}

protected static String resolveName(@NonNull final String name, @NonNull final String shardSeparator, @NonNull final String hint) {
return String.format("%s%s%s", name, shardSeparator, hint);
}

/**
* CCount the records from all shards that satisfies the query
*
Expand Down Expand Up @@ -108,7 +104,7 @@ protected String resolveDatabaseHintWithoutEntityContext() throws UnresolvableDa
*/
protected String resolveCollectionNameWithoutEntityContext(final String collectionName)
throws UnresolvableCollectionShardException {
return resolveName(collectionName, resolveCollectionHintWithoutEntityContext());
return this.shardingOptions.resolveCollectionName(collectionName, resolveCollectionHintWithoutEntityContext());
}

/**
Expand All @@ -121,6 +117,6 @@ protected String resolveCollectionNameWithoutEntityContext(final String collecti
*/
protected String resolveDatabaseNameWithoutEntityContext(final String databaseName)
throws UnresolvableDatabaseShardException {
return resolveName(databaseName, resolveDatabaseHintWithoutEntityContext());
return this.shardingOptions.resolveDatabaseName(databaseName, resolveDatabaseHintWithoutEntityContext());
}
}
Loading

0 comments on commit 77b6665

Please sign in to comment.