GoogleCloudPlatform / google-cloud-spanner-hibernate

The official Google Cloud Spanner Dialect for Hibernate ORM
GNU Lesser General Public License v2.1
51 stars 40 forks source link

SpannerSchemaManagementTool singleton prevents having multiple persistence units #406

Open yrodiere opened 2 years ago

yrodiere commented 2 years ago

See here:

https://github.com/GoogleCloudPlatform/google-cloud-spanner-hibernate/blob/2c97da01f00eaa0865c9205a8f8788fa896aee60/google-cloud-spanner-hibernate-dialect/src/main/java/com/google/cloud/spanner/hibernate/SpannerServiceContributor.java#L42-L45

SpannerSchemaManagementTool is effectively a singleton, but it gets injected with information specific to the persistence unit. If it's reused across multiple persistence unit, it will only keep information about the last persistence unit to start, and schema management for the previous persistence units will not work correctly.

You can find more information on the kind of problems this leads to here: https://github.com/quarkusio/quarkus/issues/24742

In order to solve this problem, SpannerServiceContributor should create one instance of SpannerSchemaManagementTool each time the service is initiated. That's what org.hibernate.tool.schema.internal.SchemaManagementToolInitiator, the default implementation, does.

zhumin8 commented 2 years ago

On a first glance, this basic sample works fine with multiple entities. Is this SpannerServiceContributor.java specifically used with quarkus?

@olavloite do you have any context on this by any chance?

yrodiere commented 2 years ago

On a first glance, this basic sample works fine with multiple entities. Is this SpannerServiceContributor.java specifically used with quarkus?

This sample is not relevant, because the problem is not about having multiple entities.

The problem is about having multiple persistence units, i.e. multiple instances of Hibernate ORM in the same application. In that case, you have multiple service registries and multiple entity manager factories. If your SpannerSchemaManagementTool is a singleton, it will get injected (through this method) multiple times with the service registries from each persistence unit, and only the last injection will win. You will end up with multiple persistence units using a single SpannerSchemaManagementTool that internally relies on metadata from the last persistence unit. This cannot work.

If you need a reproducer beyond the one from https://github.com/quarkusio/quarkus/issues/24742, you only need to set up an application with two persistence units, each with different entities, and each pointing to a different Google Spanner instance, with database schema generation enabled in Hibernate ORM (that should be the default). ~You'll see that SpannerSchemaManagementTool will create the same tables in both Google Spanner instances.~ Actually, the problem is rather that it will create all tables in one Google Spanner instance, instead of some in the first instance and some in the second instance. Which is arguably even worse.

Is this SpannerServiceContributor.java specifically used with quarkus?

SpannerServiceContributor.java comes from google-cloud-spanner-hibernate, not from Quarkus. It is used as soon as google-cloud-spanner-hibernate is in the classpath, as far as I can tell.

zhumin8 commented 2 years ago

Thank you for the detailed explanation. We will look into this.

meltsufin commented 2 years ago

@yrodiere we welcome contributions for this from the community.

yrodiere commented 2 years ago

@meltsufin Then let's hope the community will provide a contribution. I'm not personally a Spanner user, so fixing bugs in this project unfortunately cannot be a priority for me, but I'll see if I have time to have a look in the future.

mpeddada1 commented 2 years ago

Thanks for providing the initial context for this issue! We will keep this issue open as we await contributions from the community.