Don't have everything ready for spring-data but had something like that in mind:
public class RoutedEMFConf { @Bean @Primary LocalContainerEntityManagerFactoryBean mainEntityManagerFactory(final Tenant tenant, final ApplicationContext context) { final var emfs = findDelegates(context); // can be other beans with qualifiers final var routedEmf = EntityManagerFactory.class.cast(Proxy.newProxyInstance( RoutedEMFConf.class.getClassLoader(), // opt: use SessionFactoryImplementor.class if you need hibernate internals new Class<?>[]{EntityManagerFactory.class, Marking.class}, (proxy, method, args) -> { switch (method.getName()) { case "equals": return args[0] instanceof Marking; // assume there is a single one per app, otherwise complete the impl case "hashCode": return 1; default: try { final var id = tenant.get(); return method.invoke(requireNonNull(emfs.get(id), () -> "No emf for '" + id + "'"), args); } catch (final InvocationTargetException ite) { throw ite.getTargetException(); } } } )); return new LocalContainerEntityManagerFactoryBean() { @Override protected EntityManagerFactory createNativeEntityManagerFactory() throws PersistenceException { return routedEmf; } }; } private Map<String, EntityManagerFactory> findDelegates(final ListableBeanFactory lbf) { return Stream.of(lbf.getBeanNamesForType(EntityManagerFactory.class)) .filter(it -> !"mainEntityManagerFactory".equals(it)) .collect(toMap(identity(), k -> lbf.getBean(k, EntityManagerFactory.class))); } public interface Marking {} // modelize the tenant lookup but can be a class, interface is not always needed public interface Tenant extends Supplier<String> { } } Side note: the delegate must have a valid name (likely make it a spring extension registering beans from your conf or "properties" models). The missing part is mainly the Tenant impl but guess you already have something for that ;) - I assume some security context and meta for login. Romain Manni-Bucau @rmannibucau <https://twitter.com/rmannibucau> | Blog <https://rmannibucau.metawerx.net/> | Old Blog <http://rmannibucau.wordpress.com> | Github <https://github.com/rmannibucau> | LinkedIn <https://www.linkedin.com/in/rmannibucau> | Book <https://www.packtpub.com/application-development/java-ee-8-high-performance> Le mar. 9 janv. 2024 à 11:28, Francesco Chicchiriccò <ilgro...@apache.org> a écrit : > Thank Romain, I share your considerations and concerns below, and also > agree that EMF routing is the way to go. > > I probably need to tune my current exploration to let evolve what we > currently have in Syncope towards proper EMF routing. > > Do you have any sample I could follow about that? > > Regards. > > On 09/01/24 10:51, Romain Manni-Bucau wrote: > > Hi Francesco, > > > > While you have an EMF router you don't have pitfall 4, it only happens if > > your routing is done at datasource level but it also means you have way > > more side effects and you start to loose the hability to tune per tenant > (a > > common pattern is to tune the cache per tenant "size"/usage, there all > > would be shared, not isolated so no real way to handle anything there). > > > > Note: having routed caches can make it work somehow but will need a lot > of > > reimplementation of the cache whereas it is free when using a routed emf. > > It can be faked with PartitionedDataCache overriding the key name > > (appending the tenant) but in terms of supervision I fear it will be way > > harder and I'm not sure it would be very consummable for people (you end > up > > making the leak risk higher for users by design and you don't get any > > benefit from that - you don't reduce the overhead, you don't reduce the > > pool size etc which are at another level). > > > > In terms of spring-data integration there is also no link, just @Bean EMF > > routedEmf() and you'll get it working transparently while a tx - cache > > scope of spring - is for a single tenant. > > > > Hope I'm not missing something "key" ;). > > > > Romain Manni-Bucau > > @rmannibucau <https://twitter.com/rmannibucau> | Blog > > <https://rmannibucau.metawerx.net/> | Old Blog > > <http://rmannibucau.wordpress.com> | Github < > https://github.com/rmannibucau> | > > LinkedIn <https://www.linkedin.com/in/rmannibucau> | Book > > < > https://www.packtpub.com/application-development/java-ee-8-high-performance > > > > > > > > Le mar. 9 janv. 2024 à 10:32, Francesco Chicchiriccò < > ilgro...@apache.org> > > a écrit : > > > >> Hi Romain, > >> see my replies embedded below. > >> > >> Regards. > >> > >> On 08/01/24 17:43, Romain Manni-Bucau wrote: > >>> Hi Francesco, > >>> > >>> Normally if you have one EMF per tenant there is no leak between them > >> since the cache instance is stored in the EMF - used that approach in > TomEE. > >> > >> As I am saying below, this is what we have already in Syncope. > >> > >> My company is also supporting customers heavily using this particular > >> feature: it works, I have no issues with that. > >> Someone is also building a SaaS solution on top of that, so runtime > tenant > >> addition and removal is also fine. > >> > >> I am exploring this different approach because it would allow to > introduce > >> Spring Data JPA, which could have some benefits - see > >> https://issues.apache.org/jira/browse/SYNCOPE-1799 > >> > >>> You can check it in > >> org.apache.openjpa.datacache.DataCacheManagerImpl#initialize of each emf > >> which should be different. > >> > >> Thanks for the pointer. > >> > >>> So overall if there is a leak it is likely that it leaks accross > >> transactions or some spring cache level. > >> > >> I think that things are more subtle: consider the following use case. > >> > >> We have MyEntity with String @Id. > >> > >> Suppose we have two tenants: A and B. > >> > >> 1. Tenant A will make a REST call which creates a MyEntity instance with > >> key "key1" under the db for A. > >> > >> 2. Tenant A will make another REST call which looks for the newly > created > >> MyEntity instance via: > >> > >> entityManager.find(MyEntity.class, "key1"); > >> > >> 3. Tenant B makes the same call as (1) with the same key "key1": all is > >> fine, a new row is created under the db for B. > >> > >> 4. Tenant B makes the same call as (2) with the same key "key1": if not > >> already evicted, entityManager will return the MyEntity instance for > Tenant > >> A from the cache. > >> > >> I need to avoid the pitfalls from (4). > >> > >>> Side note: the datasource routing pattern is useless if you have an > >> entity manager routing pattern and only use JPA to do database work, > both > >> will more easily conflict than help. > >> > >> The idea is not to have an entity manager routing pattern, rather to > have > >> a cache routing patter on the single entity manager factory; or just to > >> configure some predefined partitions. > >> > >>> If you still want to plug the datacase (query cache) configuration in > >> the jpa properties can take a custom fully qualified name too. > >>> Le lun. 8 janv. 2024 à 17:14, Francesco Chicchiriccò < > >> ilgro...@apache.org> > >>> a écrit : > >>> > >>>> Hi there, > >>>> at Syncope we have been implementing multi-tenancy by relying on > >> something > >>>> like: > >>>> > >>>> * 1 data source per tenant > >>>> * 1 entity manager factory per tenant > >>>> * 1 transaction manager per tenant > >>>> * etc > >>>> > >>>> So far so good. > >>>> > >>>> Now I am experimenting a different approach similar to [1], e.g. > >>>> > >>>> * 1 low-level data source per tenant > >>>> * 1 data source extending Spring's AbstractRoutingDataSource using the > >>>> value of a ThreadLocal variable as lookup key > >>>> * 1 single entity manager factory configured with the routing data > >> source > >>>> * 1 single transaction manager > >>>> * etc > >>>> > >>>> It mostly works but I am having caching issues with concurrent > >> operations > >>>> working on different tenants, so I was wondering: how can I extend the > >>>> various OpenJPA (query, data, L1, L2, every one) caches to hold back > >>>> different actual instances per tenant and to use the appropriate one > >>>> depending on the same ThreadLocal value I have already used above for > >> data > >>>> sources? > >>>> > >>>> Thanks in advance. > >>>> Regards. > >>>> > >>>> [1] https://github.com/Cepr0/sb-multitenant-db-demo > > > -- > Francesco Chicchiriccò > > Tirasa - Open Source Excellence > http://www.tirasa.net/ > > Member at The Apache Software Foundation > Syncope, Cocoon, Olingo, CXF, OpenJPA, PonyMail > http://home.apache.org/~ilgrosso/ > >