Support for Multitenancy

1,063 views
Skip to first unread message

tra...@isalussolutions.com

unread,
Mar 21, 2016, 12:40:47 PM3/21/16
to HAPI FHIR
Hi, we would like to enable support for multi tenancy using the HAPI JPA persistence stack.  Has there been any work on that sort of design?  

For our particular case, we would like to have many developers using a single sandbox, but be isolated from each other.

Thanks!

James Agnew

unread,
Mar 27, 2016, 10:03:09 AM3/27/16
to Travis Cummings, HAPI FHIR
Hi Travis,

So far no, this isn't something we've really looked into doing.

I'm not sure what the best approach would be- I can see the utility in supporting this, but it would require some rework for sure. Probably the best thing would be a new column in the ResourceTable entity indicating which tenant ID a given resource belonged to.. but this would be a fairly far-reaching change since lots of things touch it.

Is this related to the HSPC SMART sandbox I'm assuming? I'm doing some other work around creating a standard authorization framework for HAPI right now that might play well there too... Will send you a separate email.

Cheers,
James

--
You received this message because you are subscribed to the Google Groups "HAPI FHIR" group.
To unsubscribe from this group and stop receiving emails from it, send an email to hapi-fhir+...@googlegroups.com.
To post to this group, send email to hapi...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/hapi-fhir/5799c0a8-8197-4f4c-8b03-f31dc3534dac%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Travis Cummings

unread,
Mar 28, 2016, 12:39:23 PM3/28/16
to HAPI FHIR
Our current thinking is to use a schema per user architecture.  We would expose URL's for logically different HAPI FHIR servers at paths like:

.../api/user1/data
.../api/user2/data

Then use an interceptor or aspect to set the tenant to be the user in the request path.  Then use JPA and Hibernate support for multi tenancy.

The main disadvantages I can see are that there could be dozens or hundreds of schemas.  I believe the datasource would be shared among the per-tenant persistence managers so it would scale out nicely.  Is there a scalability concern within each persistence manager from HAPI?  Is there tons of caching, etc. that would not scale out?

We'll give it a try and let you know how it goes.

Thanks!
Travis

James Agnew

unread,
Apr 10, 2016, 9:52:41 PM4/10/16
to Travis Cummings, HAPI FHIR
Hi Travis,

Somehow this thread got buried in my inbox and I missed it!

I think in general this approach would work well. The JPA module does not do any caching at all as of 1.5, so you shouldn't run into scalability concerns. Prior to 1.5, search results (in other words, pages for multi-page search results) were cached in memory, but this has been addressed now so that removes any concern on that front from the JPA side.

The one thing that would probably be very complicated for what you're describing is the lucene indexing. Lucene is only used for fulltext searches (_text and _content params) so if you don't need that feature you could disable hibernate search entirely and that would take care of that.

Cheers,
James


--
You received this message because you are subscribed to the Google Groups "HAPI FHIR" group.
To unsubscribe from this group and stop receiving emails from it, send an email to hapi-fhir+...@googlegroups.com.
To post to this group, send email to hapi...@googlegroups.com.

Alexander Kley

unread,
Apr 13, 2016, 5:56:09 AM4/13/16
to HAPI FHIR, tra...@isalussolutions.com
Hi James, hi Travis

@James
How can I disable hibernate search entirely?

Removing special jpa properties from FhirServerConfig/FhirServerConfigDstu3 does not work:
extraProperties.put("hibernate.search.default.directory_provider", "filesystem");
extraProperties.put("hibernate.search.default.indexBase", "target/lucenefiles");
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");

Kind regards,
Alex

fhi...@gmail.com

unread,
Aug 18, 2017, 10:11:44 AM8/18/17
to HAPI FHIR, tra...@isalussolutions.com
Hi everyone,

Is this feature still being considered? I could not find an issue for it on github. This would be awesome because as the hapi fhir jpa server uses a single table to hold all the resources one could say that query performance on that table can go low if there are many records in it. Using a multi-tenancy approach could solve this issue. JPA has support for multi-tenancy, i don't think the discriminator column approach would be well suited because it wouldn't alleviate the performance burden as much as a multi-schema or multi-database approach.
I found this article to be very instructive and it even has an example implementation that i think could be implemented in hapi-fhir https://www.ricston.com/blog/multitenancy-jpa-spring-hibernate-part-1/.

As for lucene indexing, i have no clue. 

@James would you like me to open an issue and try to implement this on a forked branch? I don't have the time now, but can do it in 1 to 2 weeks from now.

fhi...@gmail.com

unread,
Aug 18, 2017, 10:14:06 AM8/18/17
to HAPI FHIR, tra...@isalussolutions.com
Travis, have you come to some implementation around your multi-schema suggestion?

James Agnew

unread,
Aug 18, 2017, 10:30:44 AM8/18/17
to Alexander Kley, HAPI FHIR, Travis Cummings
Hi Alexander,

I believe it should be possible to disable it by adding the following properties, per this stackoverflow.

hibernate.search.autoregister_listeners = false
hibernate.search.indexing_strategy = manual

Cheers,
James

To unsubscribe from this group and stop receiving emails from it, send an email to hapi-fhir+unsubscribe@googlegroups.com.

To post to this group, send email to hapi...@googlegroups.com.

James Agnew

unread,
Aug 18, 2017, 10:36:43 AM8/18/17
to fhi...@gmail.com, HAPI FHIR, Travis Cummings
Hi there,

(Oops, gmail confused me and I replied to an old message first.... need to finish my coffee!)

We still definitely plan on implementing multitenancy, probably within the next few months, but it's funding dependent. If anyone wants to take a crack at playing with Hibernate's native multitenancy support before then I'm definitely willing to try and offer any guidance I can...

Cheers,
James

To unsubscribe from this group and stop receiving emails from it, send an email to hapi-fhir+unsubscribe@googlegroups.com.

To post to this group, send email to hapi...@googlegroups.com.

Malcolm McRoberts

unread,
Aug 23, 2017, 10:47:15 AM8/23/17
to HAPI FHIR, tra...@isalussolutions.com
I'm also interested to hear how the multi-schema approach worked for you.  Is it possible to give an admin user the ability to search across all the schemas?  Right now I'm looking at interceptors that would use relationships with organizations that map to tenants. 

Shlomy Reinstein

unread,
Nov 20, 2017, 10:20:30 AM11/20/17
to HAPI FHIR
Hi,

Any news on this? Has any work been done to support multitenancy in HAPI?

Thanks,
Shlomy

mi...@interopion.com

unread,
Nov 21, 2017, 5:26:00 PM11/21/17
to HAPI FHIR
We've had some luck implementing multi-tenancy on the HAPI JPA Server backing the HSPC Sandbox.  

We are currently using a thread-local variable to decide on a per-request basis which schema to access.  This has some major deficiencies:
  • Since HAPI 2.5 or so the search is implemented on a different thread. This broke our implementation so we are currently overriding this functionality.
  • Only the default tenant is indexed for search (lucene)
  • Only the default tenant has its stale search results cleaned up
The source code is here if people want to check it out: https://bitbucket.org/hspconsortium/reference-api.  I could probably pull out the multi-tenancy stuff into a more simple sample project.  

What would people think about adding a maven module "hapi-fhir-jpaserver-example-multitenant" or perhaps "hapi-fhir-jpaserver-examples/hapi-fhir-jpaserver-example-multitenant" and we could start working through the issues?

- Mike

Shlomy Reinstein

unread,
Nov 26, 2017, 7:25:30 AM11/26/17
to HAPI FHIR
Hi,
Thanks a lot for your reply!
Did you consider adding a column to the FHIR DB tables, and then adding it to each request to the DB? Is that difficult to do? This would eliminate the need in multiple schemas (especially when the set of tables and their structure is the same for all tenants).
Thanks,
Shlomy

בתאריך יום רביעי, 22 בנובמבר 2017 בשעה 00:26:00 UTC+2, מאת mi...@interopion.com:

Travis Cummings

unread,
Nov 28, 2017, 6:35:16 PM11/28/17
to Shlomy Reinstein, HAPI FHIR
Hi Shlomy,

We did consider the 3 common multi-tenant designs: separate database, separate schema, shared schema.  We choose separate schema because it was a nice blend of account isolation and resource sharing.

We didn't pursue the shared schema design is because a HIPAA concern arises when credentials associated with one account can be used to access data for another account.  The implementation Mike mentioned doesn't actually have separate credentials per account (and datasource), but it certainly could if that were a system requirement.

A second argument against shared schema design is that it would require altering the HAPI table structure.  If this behavior became part of HAPI core, this issue could be alleviated.

An argument against separate database design was that it didn't scale well beyond a few tenants.

But HAPI as a open-source library could certainly support these different architectures, allowing consumers to choose for their use case.

Travis

--
You received this message because you are subscribed to a topic in the Google Groups "HAPI FHIR" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/hapi-fhir/F37kfAHYXnc/unsubscribe.
To unsubscribe from this group and all its topics, send an email to hapi-fhir+...@googlegroups.com.

To post to this group, send email to hapi...@googlegroups.com.

marcod...@gmail.com

unread,
Mar 18, 2019, 5:22:18 AM3/18/19
to HAPI FHIR
Hy Mike,

we are currently evaluating a project where we want to use separate scheme multi-tenancy. I made a quick look at your source code repo, but for a newbie this is quite overwhelming.

So I would very much like to see a more simple sample project :)

Are there any general updates on this, as this thread seems quite old?

thanks

Alberto Freitas

unread,
Jan 24, 2020, 4:20:48 AM1/24/20
to HAPI FHIR
Hello Hapi guys, any news about multi-tenancy support, example with separate schema strategy or any recommended example?

Thanks,
Alberto

Ken Stevens

unread,
Jan 26, 2020, 6:49:57 PM1/26/20
to HAPI FHIR
We have sketched out a design for this and hope to implement it this Spring in time for the May 2020 release.

umberto.c...@gmail.com

unread,
Apr 22, 2020, 4:10:59 AM4/22/20
to HAPI FHIR
Hi,

Just adding that I tried aa very similar approach, using an InheritableThreadLocal and the tenant id is correctly passed to the child threads, but the issue remains as the SearchCoordinator uses a cached thread pool, and each time a cached thread is resumed, the old tenant is used. It may be solved using a tenant-aware executor. Looking forward to the multi-tenancy support in the next release.

Regards, Umberto

chr...@iscribes.co

unread,
May 6, 2020, 2:46:31 PM5/6/20
to HAPI FHIR
Hi - we are also looking forward to testing/using the multi-tenancy feature once it is available.  
Is the work on multti-tenancy tracked anywhere on github?

Thanks!

James Agnew

unread,
May 6, 2020, 2:54:25 PM5/6/20
to chr...@iscribes.co, HAPI FHIR
Multitenancy has now actually been completed. It will be available in HAPI FHIR 5.0.0, which is released next week.

The documentation is actually already live on the website if you're curious. :)

--
You received this message because you are subscribed to the Google Groups "HAPI FHIR" group.
To unsubscribe from this group and stop receiving emails from it, send an email to hapi-fhir+...@googlegroups.com.

chr...@iscribes.co

unread,
May 6, 2020, 2:58:42 PM5/6/20
to HAPI FHIR
For those that find this post later, here is the link to the documentation that James mentioned: https://hapifhir.io/hapi-fhir/docs/server_jpa_partitioning/partitioning.html
To unsubscribe from this group and stop receiving emails from it, send an email to hapi...@googlegroups.com.

srinija...@gmail.com

unread,
Aug 3, 2020, 12:42:58 PM8/3/20
to HAPI FHIR
I am trying to enable the partitioning feature on the Hapi-Fhir-japastarter server, getting the following error.

Screen Shot 2020-08-02 at 11.38.59 PM.png


Steps I followed, enabled the partitioning in the hapi.properties
and ran the jpa-starter with mvn jetty:run
console error we got is as follows

2020-07-30 09:02:24.577 [http-nio-8080-exec-1] INFO  ca.uhn.fhir.context.FhirContext [FhirContext.java:180] Creating new FHIR context for FHIR version [R4]

2020-07-30 09:02:24.982 [http-nio-8080-exec-2] WARN  c.u.f.r.s.i.ExceptionHandlingInterceptor [ExceptionHandlingInterceptor.java:147] Failure during REST processing: ca.uhn.fhir.rest.server.exceptions.InvalidRequestException: Invalid request: The FHIR endpoint on this server does not know how to handle GET operation[metadata] with parameters [[]]

2020-07-30 09:02:25.007 [http-nio-8080-exec-2] INFO  fhirtest.access [LoggingInterceptor.java:160] ERROR - GET http://localhost:8080/hapi-fhir-jpaserver/fhir/metadata

2020-07-30 09:02:25.122 [http-nio-8080-exec-2] INFO  c.u.f.c.s.DefaultProfileValidationSupport [DefaultProfileValidationSupport.java:301] Loading structure definitions from classpath: /org/hl7/fhir/r4/model/profile/profiles-resources.xml

2020-07-30 09:02:25.974 [http-nio-8080-exec-2] INFO  c.u.f.c.s.DefaultProfileValidationSupport [DefaultProfileValidationSupport.java:301] Loading structure definitions from classpath: /org/hl7/fhir/r4/model/profile/profiles-types.xml

2020-07-30 09:02:26.014 [http-nio-8080-exec-2] INFO  c.u.f.c.s.DefaultProfileValidationSupport [DefaultProfileValidationSupport.java:301] Loading structure definitions from classpath: /org/hl7/fhir/r4/model/profile/profiles-others.xml

2020-07-30 09:02:26.214 [http-nio-8080-exec-2] INFO  c.u.f.c.s.DefaultProfileValidationSupport [DefaultProfileValidationSupport.java:301] Loading structure definitions from classpath: /org/hl7/fhir/r4/model/extension/extension-definitions.xml

2020-07-30 09:02:26.936 [http-nio-8080-exec-1] WARN  ca.uhn.fhir.to.BaseController [BaseController.java:443] Failed to load conformance statement, error was: ca.uhn.fhir.rest.server.exceptions.InvalidRequestException: HTTP 400 : Invalid request: The FHIR endpoint on this server does not know how to handle GET operation[metadata] with parameters [[]]

2020-07-30 09:02:26.943 [http-nio-8080-exec-3] WARN  c.u.f.r.s.i.ExceptionHandlingInterceptor [ExceptionHandlingInterceptor.java:147] Failure during REST processing: ca.uhn.fhir.rest.server.exceptions.InvalidRequestException: Invalid request: The FHIR endpoint on this server does not know how to handle GET operation[metadata] with parameters [[]]

2020-07-30 09:02:26.944 [http-nio-8080-exec-3] INFO  fhirtest.access [LoggingInterceptor.java:160] ERROR - GET http://localhost:8080/hapi-fhir-jpaserver/fhir/metadata

2020-07-30 09:02:26.984 [http-nio-8080-exec-1] WARN  ca.uhn.fhir.to.BaseController [BaseController.java:288] Failed to invoke server

ca.uhn.fhir.rest.server.exceptions.InvalidRequestException: HTTP 400 : Invalid request: The FHIR endpoint on this server does not know how to handle GET operation[metadata] with parameters [[]]

        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)

        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)

        at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)

        at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)

        at ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException.newInstance(BaseServerResponseException.java:302)

        at ca.uhn.fhir.rest.client.impl.BaseClient.invokeClient(BaseClient.java:351)

        at ca.uhn.fhir.rest.client.impl.GenericClient$BaseClientExecutable.invoke(GenericClient.java:540)

        at ca.uhn.fhir.rest.client.impl.GenericClient$FetchConformanceInternal.execute(GenericClient.java:827)

        at ca.uhn.fhir.to.Controller.actionConformance(Controller.java:96)

        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

        at java.base/java.lang.reflect.Method.invoke(Method.java:566)

        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)

        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)

        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)

        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888)

        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)

        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)

        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)

        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)

        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)

        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)

        at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)

        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)

        at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)

        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)

        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)

        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)

        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)

        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)

        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:200)

        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)

        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)

        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)

        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)

        at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:678)

        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)

        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)

        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)

        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)

        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:836)

        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1839)

        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)

        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)

        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)

        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)

        at java.base/java.lang.Thread.run(Thread.java:834)

2020-07-30 09:02:27.020 [http-nio-8080-exec-1] INFO  ca.uhn.fhir.to.Controller [Controller.java:104] [server=home] - Displayed conformance profile


Could you help me solve this error.

 

On Wednesday, May 6, 2020 at 2:54:25 PM UTC-4, James Agnew wrote:
To unsubscribe from this group and stop receiving emails from it, send an email to hapi...@googlegroups.com.

Kristof Taveirne

unread,
Aug 25, 2020, 12:44:20 PM8/25/20
to HAPI FHIR
Hi,

I'm experiencing the same after I added multitenancy to my FHIR server.

Invalid request: The FHIR endpoint on this server does not know how to handle GET operation[metadata] with parameters [[_format]]

Did you find a solution for this?
The issue is the metadata endpoint is on /fhir/metadata, but I'm using /fhir/<tenantId>/<Resource> strategy.

The error gets thrown after the ITenantIdentificationStrategy but before the AuthorizationInterceptor where the "Allow metadata" rule is added.

Did you manage to resolve this issue? 

Kind regards,
Kristof.

Op maandag 3 augustus 2020 om 18:42:58 UTC+2 schreef srinija...@gmail.com:

James Agnew

unread,
Aug 26, 2020, 9:10:55 AM8/26/20
to Kristof Taveirne, HAPI FHIR
Does this work if you request /fhir/<tenantId>/metadata ?

When you are using multitenancy with URL based tenant selection, your base URL needs to include the tenant ID always. FHIR doesn't have any concept of tenancy, so what you're actually creating is a single server that is serving up a bunch of logically separate FHIR endpoints.

Cheers,
James

To unsubscribe from this group and stop receiving emails from it, send an email to hapi-fhir+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/hapi-fhir/762913c5-5119-47f7-93db-78e5af288c52n%40googlegroups.com.

Kristof Taveirne

unread,
Aug 27, 2020, 7:28:17 AM8/27/20
to James Agnew, HAPI FHIR
Hi James,

Yes!

That does actually work!
The issue now is that everything with a tenantid is protected, so /metadata now works as long as I provide a security token, but I'll find a way around that.

Thanks!  

Greetings,
Kristof.
Message has been deleted
Message has been deleted

Alberto Freitas

unread,
Sep 1, 2020, 6:20:45 AM9/1/20
to HAPI FHIR

Hi

I tried the new Partitioning feature and it is working very well. Excellent job Hapi FHIR team¡¡¡

I am really excited about implementing a multi-tenant architecture, but in my case, I need to implement a Multi-tenant Hapi FHIR with database-per-tenant (each database physically isolated).

I implemented a Springboot version, based on https://github.com/jamesagnew/hapi-fhir/tree/master/hapi-fhir-spring-boot, with a configuration class to use the AbstractRoutingDataSource class (provided by Spring Framework) and a TenantContext class that includes a ThreadLocal static field to store the current tenantId. That implementation is working with POST operations but it is not working with GET operations, because SearchCoordinatorSvcImpl is using ThreadPoolExecutor as ExecutorService.

ThreadPoolExecutor is not a tenant-aware ExecutorService , because the threads in the pool are different from the original thread that stores the current tenantId.

What do you suggest that could be a possible solution or implementation to satisfy the requirement of having a multi-tenant Hapi FHIR JPA Server with a physical database-per-tenant?

Thank you very much¡¡¡

Greetings,

Alberto


Reply all
Reply to author
Forward
0 new messages