Skip to content
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

Multitenancy schema discriminator with OneToMany relationships - Wrong tenant reference leading to QueryException #1161

Closed
nexteam-silviot opened this issue May 26, 2021 · 0 comments · Fixed by #1188

Comments

@nexteam-silviot
Copy link

Hello!
We're facing the issue reported here in 2016, working with Multitenancy and schema discriminator.
When executing JPQL queries randomly it gets executed on the wrong tenant.

The patch attached in the Bug report above applies even on newest version (2.7.8) so I did apply the patch, recompiled the library and fixed the issue but I'm kindly request to please review the patch and apply it also on the official repo.

The issue seems due to a bug in the clone() function of OneToManyMapping class which is not cloning certain internal objects. When multiple threads modifies those objects (one of them is the tenant), it gets modified on the cloned ones too. Thus queries and persist() / merge() actions fail on a wrong tenant

I'm attaching here an example to help with debugging!

testmultitenancy.src.zip
mtfix.init.sql.zip

Prepare for the test

1) Create test schemas and tables with 2 tenants executing attached mtfix.init.sql file

2) Have 2 entities, Child and Parent using schema discriminator and having a OneToMany relationship
Find attached in src.zip Parent.java and Child.java files

@Entity
@Table(name = "children")
@Multitenant(MultitenantType.TABLE_PER_TENANT)
@TenantTableDiscriminator(type = TenantTableDiscriminatorType.SCHEMA)
public class Child implements Serializable {


@Entity
@Table
@Cacheable(false)
@Multitenant(MultitenantType.TABLE_PER_TENANT)
@TenantTableDiscriminator(type = TenantTableDiscriminatorType.SCHEMA)
public class Parent implements Serializable {

3) Import ParentDAO, TestFacade and TestResource classes from src.zip which implements the logic of the test

Steps to reproduce

TestFacade simply loads the Parent entity

System.out.println(Thread.currentThread().getName() + " => Parent loaded (id: " + id + ", tenant: " + tenant + ")");
Parent found = parentDao.load(tenant, id);
if (found == null) {
    throw new Exception("No entity loaded for id " + id);
}

Call TestFacade::execute() using multithreading with the two tenants, you will see that the function fails randomly with a QueryException.

ExecutorService es = Executors.newFixedThreadPool(5);
for (int i = 1; i <= 30; i++) {
    es.execute(testFacade.execute("tenant_1", 1));
    es.execute(testFacade.execute("tenant_2", 2));
}
es.shutdown();
es.awaitTermination(10, TimeUnit.MINUTES);

Example log

The query was supposed to execute on tenant_1 but the many to one relationship has a wrong tenant reference (tenant_2)

...
http-nio-8085-exec-119 => Parent loaded (id: 1, tenant: tenant_1)
...
26-May-2021 12:46:58.560 GRAVE [http-nio-8085-exec-119] org.apache.openejb.core.transaction.EjbTransactionUtil.handleSystemException EjbTransactionUtil.handleSystemException: 
Exception Description: The field [tenant_2.PARENT.id] in this expression has an invalid table in this context.
Query: ReadObjectQuery(name="readParent" referenceClass=Parent )
 Local Exception Stack: 
Exception [EclipseLink-6069] (Eclipse Persistence Services - 2.7.8.v20201217-ecdf3c32c4): org.eclipse.persistence.exceptions.QueryException
Exception Description: The field [tenant_2.PARENT.id] in this expression has an invalid table in this context.
Query: ReadObjectQuery(name="readParent" referenceClass=Parent )
	at org.eclipse.persistence.exceptions.QueryException.invalidTableForFieldInExpression(QueryException.java:757)
	at org.eclipse.persistence.internal.expressions.FieldExpression.validateNode(FieldExpression.java:327)
	at org.eclipse.persistence.expressions.Expression.normalize(Expression.java:3293)
	at org.eclipse.persistence.internal.expressions.DataExpression.normalize(DataExpression.java:371)
	at org.eclipse.persistence.internal.expressions.FieldExpression.normalize(FieldExpression.java:227)
	at org.eclipse.persistence.internal.expressions.CompoundExpression.normalize(CompoundExpression.java:226)
	at org.eclipse.persistence.internal.expressions.RelationExpression.normalize(RelationExpression.java:576)
	at org.eclipse.persistence.internal.expressions.SQLSelectStatement.normalize(SQLSelectStatement.java:1524)
	at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.buildNormalSelectStatement(ExpressionQueryMechanism.java:566)
	at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.prepareSelectOneRow(ExpressionQueryMechanism.java:1742)
	at org.eclipse.persistence.queries.ReadObjectQuery.prepare(ReadObjectQuery.java:793)
	at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:673)
	at org.eclipse.persistence.queries.ObjectLevelReadQuery.checkPrepare(ObjectLevelReadQuery.java:968)
	at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:622)
	at org.eclipse.persistence.internal.jpa.EntityManagerImpl.findInternal(EntityManagerImpl.java:937)
	at org.eclipse.persistence.internal.jpa.EntityManagerImpl.find(EntityManagerImpl.java:830)
	at org.eclipse.persistence.internal.jpa.EntityManagerImpl.find(EntityManagerImpl.java:696)
	at it.testmultitenancy.daos.ParentDAO.load(ParentDAO.java:30)
...

Thank you for your attention!

@nexteam-silviot nexteam-silviot changed the title Multitenancy schema discriminator does queries on wrong tenant Multitenancy schema discriminator with ManyToOne relationships - Wrong tenant reference leading to QueryException May 26, 2021
@nexteam-silviot nexteam-silviot changed the title Multitenancy schema discriminator with ManyToOne relationships - Wrong tenant reference leading to QueryException Multitenancy schema discriminator with OneToMany relationships - Wrong tenant reference leading to QueryException May 26, 2021
aserkes added a commit to aserkes/eclipselink that referenced this issue Jun 23, 2021
…lone()

Signed-off-by: aserkes <andrii.serkes@oracle.com>
aserkes added a commit to aserkes/eclipselink that referenced this issue Jun 23, 2021
…lone(). Fix ConcurrentModificationException when EntityManager.find() is called in multithread environment.

Signed-off-by: aserkes <andrii.serkes@oracle.com>
aserkes added a commit to aserkes/eclipselink that referenced this issue Jun 25, 2021
… method

Signed-off-by: aserkes <andrii.serkes@oracle.com>
aserkes added a commit to aserkes/eclipselink that referenced this issue Jun 25, 2021
…lone() in ClassDescriptor.

Signed-off-by: aserkes <andrii.serkes@oracle.com>
lukasj pushed a commit that referenced this issue Jun 28, 2021
…ConcurrentModificationException. (#1188)

* Fix #1161: Clone appropriate fields in OneToManyMapping.clone(). Fix ConcurrentModificationException when EntityManager.find() is called in multithread environment.

Signed-off-by: aserkes <andrii.serkes@oracle.com>
aserkes added a commit to aserkes/eclipselink that referenced this issue Jul 9, 2021
…lone(). Fix ConcurrentModificationException.

Signed-off-by: aserkes <andrii.serkes@oracle.com>
aserkes added a commit to aserkes/eclipselink that referenced this issue Jul 9, 2021
Signed-off-by: aserkes <andrii.serkes@oracle.com>
aserkes added a commit to aserkes/eclipselink that referenced this issue Jul 12, 2021
…lone(). Fix ConcurrentModificationException. (backport for 3.0)

Signed-off-by: aserkes <andrii.serkes@oracle.com>
lukasj pushed a commit that referenced this issue Aug 6, 2021
…ConcurrentModificationException. (backport for 3.0)

Signed-off-by: aserkes <andrii.serkes@oracle.com>
lukasj pushed a commit that referenced this issue Aug 6, 2021
…ConcurrentModificationException. (backport for 2.7) (#1208)

Signed-off-by: aserkes <andrii.serkes@oracle.com>
cocorossello pushed a commit to cocorossello/eclipselink that referenced this issue Jan 20, 2022
…apping.clone(). Fix ConcurrentModificationException. (backport for 2.7) (eclipse-ee4j#1208)"

This reverts commit 474f855.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
1 participant