Best strategy for a single, shared tenant-aware database and centrally-controlled data access? #9944
-
Related #7455 TLDR / SummaryI am taking an old homebrew proprietary multitenant CMS with a single database per environment and porting it to OC CMS. In the original, tenants' data is stored, tracked, managed, and controlled from a central admin UI. Each tenant sees users/content/etc. as though they were their own data. In reality, creating something stores it centrally and maps it as coming from the tenant (the central system has final authority). Each tenant/user combo then queries the central system that responds by dictating what data should be exposed/presented to the tenant/user. What is the safest and most maintainable way of doing this in OC CMS? I'm thinking it would involve wrapping the existing ProblemsAs much as possible, I want to minimize customization of the OrchardCMS/OrchardCore codebase's default behavior (to avoid needing to maintain alternate code). Thus far, I've been following the approach recommended in #7455 where each tenant has its own YesSql collection, and data is remotely accessed across tenants using ShellHost/ShellScope. This has presented a number of problems though (listed below). The common thread is that this approach results in additional code complexity, long-term maintenance, inefficient scaling, and a bad developer experience. We are now looking into single-database alternatives that preserve the flexible configuration allowed for each OC tenant (features, theme, sub-routed admin, etc.). Tenant-Scoped DataUsersI use AzureAD for auth, but each user has to link it to a local user in each tenant. When moving from one tenant to another, users have to log in each time rather than already being authenticated. I'd like the user to already be authenticated everywhere at once and let authorization handle tenant-scoping of the UI (e.g. what users are displayed as part of a tenant, etc.). ContentIf a tenant marks that content is allowed to be shared/visible (read-only), I'd either have to constantly sync data between tenants' tables with BackgroundTasks/IContentManager.ImportAsync or use faux reference items that need their own custom logic and can become invalid "null" holes. Both strategies introduce unnecessary complexity and points of failure. OtherFor everything else, we will likely run into similar problems as Content, but without the built-in utility to migrate/import across YesSql containers (like WorkflowTypes). Would need to devise custom solutions for each type to individually choose data should be exposed and to where. Omni-Tenant OperationsI'm worried about performance and complexity at scale when dealing with omni-tenant operations. If 1) I have hundreds of tenants, 2) I need to do many regular/recurring operations with all of them, and 3) each operation requires multiple SQL queries, then I will need to repeat all of these queries/operations in a loop through all tenants and aggregate/resolve the data. Even if I manually compose a single SQL query instead, I'd likely be joining together hundreds or thousands of tables. It would be much simpler to just grab all the data from a single source directly and do what's needed without any additional work. An alternative we thought of is to use a "shared" tenant, push copies of data there, and then use that for aggregate tasks, but it has issues of its own (can't "write" to the central data for en masse changes, adds unnecessary points of failure with desyncs, more code complexity, and lots of custom code to maintain, etc.) DB ComplexityEven with the few experimental tenants that have been made so far, looking at the database shows that it's become a gigantic mess of tables. Things are hard to find, data is spread out everywhere, etc. The old CMS database is much simpler, streamlined, and easier to maintain. Possible SolutionWhat if I replace the service registration for ConceptThe new session internally creates an instance of Would also need to ensure that tenants don't activate features without first running their DataMigrations on the Default tenant, but that doesn't seem dangerous or too difficult. RisksThe most dangerous/hacky aspect of this solution is the fact that OC modules have the potential to directly execute SQL statements against their respective databases (which could then violate the central session's control over things). The only example of this I can find though in the OC source code is the Still, that doesn't mean that other OC features in the future won't rely on direct SQL statements. Questions
|
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 9 replies
-
I personally think that each tenants should use their own table even if it's becoming "harder to manage" over time. An other approach you could consider is to use the Content API of each of these tenants and make cross API tenant calls. If you are using OpenId I believe it should work. It would then remove the need to move/sync data across different tenants. Also of course using a Redis cache might be usefull in a SaaS context. |
Beta Was this translation helpful? Give feedback.
-
OrchardCore multi-tenant is designed to be an isolated container per each tenant. The one shared services are those that were added to host. I would suggest to design your orchard solution that go with the orchard core concept and not against it. You could create a module that could perform operations for all tenants that can be enabled/disabled in host tenant. You can also use |
Beta Was this translation helpful? Give feedback.
I personally think that each tenants should use their own table even if it's becoming "harder to manage" over time.
It's always harder to isolate a specific tenant data when you are using a single table for multiple tenants.
I think you can use the OpenId module to authenticate every tenants on a single main tenant. Though, the same role permissions will be applied on every tenants. As for Lucene we don't recommend using it in a SaaS scenario, you should look at ElasticSearch instead (scalable), we have a PR which is a work in progress that already works but needs some adjustments here and there.
An other approach you could consider is to use the Content API of each of these tenants and m…