-
-
Notifications
You must be signed in to change notification settings - Fork 265
multi tenancy aware sequences #2305
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ThomasFOC
wants to merge
9
commits into
ebean-orm:master
Choose a base branch
from
FOCONIS:feature/multi-tenancy-aware-sequences
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
89d93df
failing test for sequences in multi tenant
ThomasFOC 6754c5e
Using DataSourceSupplier in BeanDescriptorManager#createSequenceIdGen…
ThomasFOC 4112981
Merge remote-tracking branch 'origin/master' into feature/multi-tenan…
ThomasFOC 5899b5e
failing test for tenantmode-schema-configuration
ThomasFOC 1b18ca9
Merge remote-tracking branch 'origin/master' into feature/multi-tenan…
ThomasFOC 275e6e1
compilation errors fixed
ThomasFOC 1cc2196
fixed test setup and debugging information
ThomasFOC 8c8ef88
Merge remote-tracking branch 'origin/master' into feature/multi-tenan…
ThomasFOC eb20b64
apply changes to test due restructuring of tests
ThomasFOC File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
196 changes: 196 additions & 0 deletions
196
ebean-test/src/test/java/org/tests/idkeys/TestSequenceMultiTenant.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,196 @@ | ||
| package org.tests.idkeys; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.assertArrayEquals; | ||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||
|
|
||
| import java.sql.Connection; | ||
| import java.sql.ResultSet; | ||
| import java.sql.SQLException; | ||
| import java.util.Map; | ||
| import java.util.concurrent.ConcurrentHashMap; | ||
|
|
||
| import javax.sql.DataSource; | ||
|
|
||
| import org.junit.jupiter.api.Test; | ||
| import org.multitenant.partition.UserContext; | ||
| import org.tests.idkeys.db.GenKeySeqA; | ||
|
|
||
| import io.ebean.Database; | ||
| import io.ebean.DatabaseFactory; | ||
| import io.ebean.config.DatabaseConfig; | ||
| import io.ebean.config.TenantDataSourceProvider; | ||
| import io.ebean.config.TenantMode; | ||
| import io.ebean.config.TenantSchemaProvider; | ||
| import io.ebean.config.dbplatform.h2.H2Platform; | ||
|
|
||
| /** | ||
| * Tests sequences for multiple tenants. | ||
| */ | ||
| class TestSequenceMultiTenant { | ||
|
|
||
| /** | ||
| * Tests sequences using multi tenancy per database | ||
| */ | ||
| @Test | ||
| void test_multi_tenant_db_sequences() { | ||
|
|
||
| Database db = setupDb(); | ||
|
|
||
| UserContext.set("4711", "1"); | ||
| assertEquals(1L, db.nextId(GenKeySeqA.class)); | ||
| assertEquals(2L, db.nextId(GenKeySeqA.class)); | ||
|
|
||
| UserContext.set("5711", "2"); | ||
| assertEquals(1L, db.nextId(GenKeySeqA.class)); | ||
|
|
||
| UserContext.set("4711", "1"); | ||
| assertEquals(3L, db.nextId(GenKeySeqA.class)); | ||
|
|
||
| } | ||
|
|
||
| private static Database setupDb() { | ||
|
|
||
| DatabaseConfig config = new DatabaseConfig(); | ||
|
|
||
| config.setName("h2multitenantseq"); | ||
| config.loadFromProperties(); | ||
| config.setDdlGenerate(true); | ||
| config.setDdlRun(true); | ||
| config.setDdlExtra(false); | ||
| config.setRegister(false); | ||
| config.setDefaultServer(false); | ||
| config.setCurrentTenantProvider(() -> UserContext.get().getTenantId()); | ||
| config.setTenantMode(TenantMode.DB); | ||
| config.setDatabasePlatform(new H2Platform()); | ||
| config.setTenantDataSourceProvider(new TenantDataSourceProvider() { | ||
|
|
||
| Map<Object, DataSource> map = new ConcurrentHashMap<>(); | ||
|
|
||
| @Override | ||
| public DataSource dataSource(Object tenantId) { | ||
| if (tenantId == null) { | ||
| tenantId = "1"; | ||
| } | ||
| return map.computeIfAbsent(tenantId, this::createDataSource); | ||
| } | ||
|
|
||
| private DataSource createDataSource(Object tenantId) { | ||
|
|
||
| DatabaseConfig config = new DatabaseConfig(); | ||
|
|
||
| config.setName("h2multitenantseq"); | ||
| config.loadFromProperties(); | ||
| config.setDdlRun(true); | ||
| config.setDdlExtra(false); | ||
| config.setRegister(false); | ||
| config.setDefaultServer(false); | ||
| config.getDataSourceConfig().setUrl("jdbc:h2:mem:h2multitenantseq-" + tenantId); | ||
|
|
||
| return DatabaseFactory.create(config).pluginApi().dataSource(); | ||
| } | ||
| }); | ||
|
|
||
| config.getClasses().add(GenKeySeqA.class); | ||
|
|
||
| return DatabaseFactory.create(config); | ||
| } | ||
|
|
||
| /** | ||
| * Tests sequences using multi tenancy per schema | ||
| */ | ||
| @Test | ||
| void test_multi_tenant_schema_sequences() throws SQLException { | ||
| createDDl("PUBLIC"); | ||
| createDDl("TENANT_SCHEMA_1"); | ||
| createDDl("TENANT_SCHEMA_2"); | ||
|
|
||
| Database db = setupSchema(); | ||
|
|
||
| Connection connection = db.dataSource().getConnection(); | ||
|
|
||
| // debugging schemas | ||
| // see org.h2.jdbc.JdbcDatabaseMetaData.getSchemas() | ||
| ResultSet rs = connection.getMetaData().getSchemas(); | ||
| String[] schemaData = new String[4]; | ||
| int schemaCnt = 0; | ||
| while(rs.next()) { | ||
| schemaData[schemaCnt++] = String.format("%s, %s, %s", rs.getString(1), rs.getString(2), rs.getBoolean(3)); | ||
| } | ||
| assertArrayEquals(schemaData, new String[]{"INFORMATION_SCHEMA, H2MULTITENANTSEQ, false", | ||
| "PUBLIC, H2MULTITENANTSEQ, true", | ||
| "TENANT_SCHEMA_1, H2MULTITENANTSEQ, false", | ||
| "TENANT_SCHEMA_2, H2MULTITENANTSEQ, false"}); | ||
|
|
||
| // debugging sequences | ||
| rs = connection.prepareStatement("SELECT * FROM INFORMATION_SCHEMA.SEQUENCES;").executeQuery(); | ||
| String[] sequenceData = new String[4]; | ||
| int sequenceCnt = 0; | ||
| while (rs.next()) { | ||
| // SEQUENCE_CATALOG,SEQUENCE_SCHEMA,SEQUENCE_NAME,CURRENT_VALUE,INCREMENT,IS_GENERATED,REMARKS,CACHE,MIN_VALUE,MAX_VALUE,IS_CYCLE,ID | ||
| String format = String.format("%s, %s, %s", rs.getString(1), rs.getString(2), rs.getString(3)); | ||
| System.out.println(format); | ||
| sequenceData[sequenceCnt++] = format; | ||
| } | ||
|
|
||
| UserContext.set("4711", "1"); | ||
| assertEquals(1L, db.nextId(GenKeySeqA.class)); | ||
| assertEquals(2L, db.nextId(GenKeySeqA.class)); | ||
|
|
||
| UserContext.set("5711", "2"); | ||
| assertEquals(1L, db.nextId(GenKeySeqA.class)); | ||
|
|
||
| UserContext.set("4711", "1"); | ||
| assertEquals(3L, db.nextId(GenKeySeqA.class)); | ||
| } | ||
|
|
||
| private Database setupSchema() { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the support of @rPraml we were able to write a failing test for tenantmode schema. THe main question seems to be, were the sequence schould live. |
||
|
|
||
| DatabaseConfig config = new DatabaseConfig(); | ||
|
|
||
| config.setName("h2multitenantseq"); | ||
| config.loadFromProperties(); | ||
| config.setDdlGenerate(true); | ||
| config.setDdlRun(false); | ||
| config.setDdlExtra(false); | ||
| config.setRegister(false); | ||
| config.setDefaultServer(false); | ||
| config.setCurrentTenantProvider(() -> UserContext.get().getTenantId()); | ||
| config.setTenantMode(TenantMode.SCHEMA); | ||
| config.setDatabasePlatform(new H2Platform()); | ||
| config.getDataSourceConfig().setUrl("jdbc:h2:mem:h2multitenantseq;DB_CLOSE_ON_EXIT=FALSE;"); | ||
| config.setTenantSchemaProvider(new TenantSchemaProvider() { | ||
|
|
||
| @Override | ||
| public String schema(Object tenantId) { | ||
| return tenantId == null | ||
| ? "PUBLIC" | ||
| : "TENANT_SCHEMA_" + tenantId; | ||
| } | ||
| }); | ||
|
|
||
| config.getClasses().add(GenKeySeqA.class); | ||
|
|
||
| return DatabaseFactory.create(config); | ||
| } | ||
|
|
||
| private void createDDl(String schema) { | ||
| DatabaseConfig config = new DatabaseConfig(); | ||
|
|
||
| config.setName("h2multitenantseq"); | ||
| config.loadFromProperties(); | ||
| config.setDdlGenerate(true); | ||
| config.setDdlRun(true); | ||
| config.setDdlExtra(false); | ||
| config.setRegister(false); | ||
| config.setDefaultServer(false); | ||
| config.getDataSourceConfig().setUrl( | ||
| "jdbc:h2:mem:h2multitenantseq;DB_CLOSE_ON_EXIT=FALSE;INIT=CREATE SCHEMA IF NOT EXISTS " | ||
| + schema + "\\;SET SCHEMA " + schema); | ||
| config.setDbSchema(schema); // see io.ebeaninternal.dbmigration.DdlGenerator.createSchemaIfRequired(Connection) | ||
|
|
||
| config.getClasses().add(GenKeySeqA.class); | ||
|
|
||
| DatabaseFactory.create(config); | ||
| } | ||
|
|
||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FI: This works good for a DS-supplier that supplies different dataSources for different tenants (As it is done in TenantMode.DB/DB_WITH_MASTER - which is our use case @FOCONIS) - for other tenant modes like SCHEMA or CATALOG it may cause other problems. Maybe in a schema/catalog environment this should somehow configurable, where the Sequence lives.