<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>multitenancy | 🃏 slides.nauda.dev 🃏</title><link>https://slides.nauda.dev/tag/multitenancy/</link><atom:link href="https://slides.nauda.dev/tag/multitenancy/index.xml" rel="self" type="application/rss+xml"/><description>multitenancy</description><generator>Wowchemy (https://wowchemy.com)</generator><language>en-us</language><lastBuildDate>Fri, 01 Mar 2024 08:35:31 +0700</lastBuildDate><image><url>https://slides.nauda.dev/media/icon_hube4dce13482f81b54a523f0927f755ce_23287_512x512_fill_lanczos_center_3.png</url><title>multitenancy</title><link>https://slides.nauda.dev/tag/multitenancy/</link></image><item><title>Software multitenancy</title><link>https://slides.nauda.dev/slides/multitenancy/</link><pubDate>Fri, 01 Mar 2024 08:35:31 +0700</pubDate><guid>https://slides.nauda.dev/slides/multitenancy/</guid><description>
&lt;section data-noprocess data-shortcode-slide
data-background-image="featured.jpg"
data-background-opacity="0.2"
data-background-position="5% 95%"
>
&lt;h1 id="software-and-application-multitenancy">Software and Application multitenancy&lt;/h1>
&lt;p>Trung Pham Duy - March 2024&lt;/p>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
data-background-image="featured.jpg"
data-background-opacity="0.2"
data-background-position="35%"
>
&lt;h2 id="agenda">Agenda&lt;/h2>
&lt;ul>
&lt;li>SaaS&lt;/li>
&lt;li>Multitenancy
&lt;ul>
&lt;li>Benefits&lt;/li>
&lt;li>Disadvantages&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Database approaches&lt;/li>
&lt;li>Application PoV&lt;/li>
&lt;/ul>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
data-background-image="saas.png"
data-background-opacity="0.2"
>
&lt;h2 id="span-styletext-transform-nonesaasspan">&lt;span style="text-transform: none;">SaaS&lt;/span>&lt;/h2>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
data-background-image="top-25-saas.jpg"
data-background-opacity="0.2"
>
&lt;h3 id="what-is-span-styletext-transform-nonesaasspan">What is &lt;span style="text-transform: none;">SaaS&lt;/span>&lt;/h3>
&lt;ul>
&lt;li>Software as a service (SaaS /sæs/) is a software licensing and delivery model in which software is &lt;em>licensed on a subscription&lt;/em> basis and is &lt;em>centrally hosted&lt;/em>.&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;a href="https://slides.nauda.dev/slides/saas" target="_blank">
&lt;p>Checkout the presentation about &lt;code>Saas&lt;/code>&lt;/p>
&lt;/a>
&lt;/blockquote>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
data-background-image="tenants_building.png"
data-background-opacity="0.25"
>
&lt;h2 id="multitenancy">Multitenancy&lt;/h2>
&lt;hr>
&lt;h3 id="what-is-multitenancy">What is multitenancy&lt;/h3>
&lt;ul>
&lt;li>Multi-tenant architecture, also known as multitenancy, is a design approach that enables &lt;em>multiple user groups&lt;/em> — referred to as &lt;em>tenants&lt;/em> — access to &lt;em>one instance of an application or system&lt;/em>.&lt;/li>
&lt;li>Much like a building, where various tenants have their own &lt;em>private and secure apartments&lt;/em>, &lt;em>but share the same infrastructure&lt;/em>.&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>A common approach to build SaaS solutions&lt;/p>
&lt;/blockquote>
&lt;aside class="notes">
This type of architecture enables companies to save money on infrastructure costs by using the same hardware resources for several instances of the same application for multiple tenants. It is especially beneficial for SaaS companies because it allows them to scale their applications and remain cost-effective.
&lt;/aside>
&lt;hr>
&lt;h3 id="what-is-a-tenants">What is a tenants&lt;/h3>
&lt;p>&lt;code>Tenants&lt;/code> can refers to:&lt;/p>
&lt;ul>
&lt;li>Users and user groups inside your company (departments, teams, individual employees)&lt;/li>
&lt;li>Users and user groups outside your company (vendors, business partners)&lt;/li>
&lt;li>Customers (subscribers or clients) who purchase your product&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>&lt;code>Tenant&lt;/code> is a &lt;a href="https://dictionary.cambridge.org/vi/dictionary/english/catch-all" target="_blank" rel="noopener">catch-all&lt;/a> term, and &lt;strong>teams define what it means to them&lt;/strong>!&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h3 id="how-does-multi-tenant-architecture-work">How Does Multi-Tenant Architecture Work?&lt;/h3>
&lt;ul>
&lt;li>Multi-tenant architecture creates distinct, isolated environments within a single physical infrastructure, such as a virtual machine, server, or cloud platform.&lt;/li>
&lt;li>Because every multi-tenant environment is separated from one another, they can be customized to meet the needs of each individual tenant without impacting other environments.&lt;/li>
&lt;/ul>
&lt;aside class="notes">
&lt;ul>
&lt;li>Tenants can personalize features such as user interface design and data security settings for their environment.&lt;/li>
&lt;li>Additionally, different sets of rules can be applied to each domain in terms of access control, resource allocation, and feature availability.&lt;/li>
&lt;/ul>
&lt;/aside>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
data-background-image="singletenant-multitenant.png"
data-background-opacity="0.3"
data-background-position="50% -60%"
data-background-size="70%"
>
&lt;h3 id="single-tenant-vs-multiple-tenant">Single-tenant vs Multiple-tenant&lt;/h3>
&lt;div style="font-size:60%">
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Single-tenant architecture&lt;/th>
&lt;th>Multi-tenant architecture&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Each tenant has a dedicated instance of the application and its resources&lt;/td>
&lt;td>Multiple tenants share a single instance of the application and its resources&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Scaling requires provisioning and managing individual instances for each tenant&lt;/td>
&lt;td>Easier scalability as multiple tenants can be accommodated within a single instance&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>More secure due to isolated environments&lt;/td>
&lt;td>Security measures involve ensuring data isolation and preventing unauthorized access&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Less cost-efficient: hardware and software for each user is paid separately&lt;/td>
&lt;td>More cost-efficient: thanks to shared infrastructure and platform&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Independent maintenance and upgrades for each tenant&amp;rsquo;s instance&lt;/td>
&lt;td>Centralized maintenance, updates, and upgrades that apply to all tenants&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Slower deployment: each tenant requires individual setup and configuration&lt;/td>
&lt;td>Faster deployment: new tenants can be added within the existing setup&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;/div>
&lt;hr>
&lt;h2 id="benefits-of-multi-tenant-architecture">Benefits of Multi-tenant Architecture&lt;/h2>
&lt;hr>
&lt;h3 id="-scalability">✔️ Scalability&lt;/h3>
&lt;ul>
&lt;li>With one application being able to serve multiple tenants, companies can easily scale up or down quickly and efficiently without managing multiple dedicated systems.
&lt;ul>
&lt;li>This scalability makes it much easier to accommodate seasonal spikes in demand or adjust usage based on customer needs.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="-cost-savings">✔️ Cost Savings&lt;/h3>
&lt;ul>
&lt;li>Multitenancy allows companies to reduce costs by leveraging shared resources instead of having separate systems dedicated to one tenant at a time.
&lt;ul>
&lt;li>This means businesses needn’t invest in additional servers or hardware to meet customer demands, helping to save money in the long run.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="-increased-efficiency">✔️ Increased Efficiency&lt;/h3>
&lt;ul>
&lt;li>Multi-tenant architecture increases efficiency by allowing multiple tenants to share resources such as compute power and storage space without sacrificing performance.
&lt;ul>
&lt;li>This makes it easier for businesses to manage large workloads without having to invest in additional hardware or software licenses.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="-easy-maintenancemanagement">✔️ Easy Maintenance/Management&lt;/h3>
&lt;ul>
&lt;li>Multi-tenant architecture is much easier to maintain and manage than single-tenant architecture because all tenants share the same underlying codebase and platform.
&lt;ul>
&lt;li>This means businesses don’t have to worry about managing multiple versions of their application across different platforms — they can focus on managing one version across all tenants.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="disadvantages-of-multi-tenant-architecture">Disadvantages of Multi-tenant Architecture&lt;/h2>
&lt;hr>
&lt;h3 id="-security-issues-if-incorrectly-implemented">❌ Security Issues If Incorrectly Implemented&lt;/h3>
&lt;ul>
&lt;li>Poorly implemented multitenancy can lead to issues such as unauthorized access and data misuse.
&lt;ul>
&lt;li>Require careful design and management to ensure tenant isolation and secure data storage.&lt;/li>
&lt;li>The most important thing is to authorize the access of end users.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="-competing-for-limited-resources">❌ Competing For Limited Resources&lt;/h3>
&lt;p>With multiple customers using the same system resources and computing power, companies might start suffering from “noisy neighbor” syndrome, where they can’t access the resources they need, and operations slow down.&lt;/p>
&lt;hr>
&lt;h3 id="-migration-difficulty">❌ Migration Difficulty&lt;/h3>
&lt;ul>
&lt;li>While multi-tenant architectures are easy to adapt, they can be hard to leave.
&lt;ul>
&lt;li>Migrating data from a multi-tenant environment to any other type of environment can be a challenge because personal data is scattered all over the shared cloud, wherever there’s room for it.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="-global-problems-and-downtime">❌ Global Problems And Downtime&lt;/h3>
&lt;p>By outsourcing data and operations to an external cloud managed by a third-party service provider, companies risk losing access to critical data and information in the case of a technical error. Also, cloud environments are susceptible to downtime; although, it’s minimal with the top providers.&lt;/p>
&lt;hr>
&lt;h2 id="database-approaches-on-multitenancy">Database approaches on Multitenancy&lt;/h2>
&lt;hr>
&lt;h3 id="database-approaches">Database approaches&lt;/h3>
&lt;p>Data can be physically or virtually isolated.&lt;/p>
&lt;ol>
&lt;li>Single database, shared schema (DB_1SC)&lt;/li>
&lt;li>Single database, separate schema (DB_nSCs)&lt;/li>
&lt;li>One database per tenant (nDBs)&lt;/li>
&lt;li>Multiple databases, multiple tenants per database, shared schema (nSV_DB_1SC)&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h3 id="1-single-database-shared-schema-db_1sc">1/ Single database, shared schema (DB_1SC)&lt;/h3>
&lt;table style="font-size:60%">
&lt;tbody>
&lt;tr>
&lt;td>
&lt;ul>
&lt;li>One database to hold the data for all tenants&lt;/li>
&lt;li>Every tenant&amp;rsquo;s data is stored in the same set of tables&lt;/li>
&lt;li>Tables that contain tenant-specific data include a column to identify which tenant each row belongs to (known as discriminator)&lt;/li>
&lt;/ul>
&lt;/td>
&lt;td style="width:60%"">
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/slides/multitenancy/db-approaches/SingleDatabase_SharedSchema_hue428f693552a6c5da45139c01a9ed724_19856_8e943a1edf58c026fa57067ad96821a2.webp 400w,
/slides/multitenancy/db-approaches/SingleDatabase_SharedSchema_hue428f693552a6c5da45139c01a9ed724_19856_2646c0e05ea1048207d92b0d82291013.webp 760w,
/slides/multitenancy/db-approaches/SingleDatabase_SharedSchema_hue428f693552a6c5da45139c01a9ed724_19856_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://slides.nauda.dev/slides/multitenancy/db-approaches/SingleDatabase_SharedSchema_hue428f693552a6c5da45139c01a9ed724_19856_8e943a1edf58c026fa57067ad96821a2.webp"
width="526"
height="287"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h3 id="db_1sc---security">DB_1SC - Security&lt;/h3>
&lt;div style="font-size:60%;text-align:left">
&lt;p>❌ Risk of exposing one tenant’s data to another tenant or updating the wrong tenant’s data (e.g., if a developer misses a WHERE clause to filter on the tenant id)&lt;/p>
&lt;p>❌ No tenant isolation&lt;/p>
&lt;/div>
&lt;hr>
&lt;h3 id="db_1sc---maintainability">DB_1SC - Maintainability&lt;/h3>
&lt;div style="font-size:60%;text-align:left">
&lt;p>✔️ One database schema to maintain and a simple schema update rollout process—it only needs to be applied once&lt;/p>
&lt;p>✔️ Manage the High Availability/Disaster Recovery/maintenance operation/monitoring strategy for just one database&lt;/p>
&lt;p>✔️ Limited development/application code complexity—single schema, single database to connect to&lt;/p>
&lt;p>✔️ Adding new tenants is easy—no processes needed around database/schema provisioning or connection determination&lt;/p>
&lt;p>❌ Any query or data modification must includes a predicate to restrict the operation to a specific tenant id&lt;/p>
&lt;p>❌ Must remember to update the &lt;a href="https://www.postgresql.org/docs/current/ddl-rowsecurity.html" target="_blank" rel="noopener">RLS policy&lt;/a> as new tables are added over time&lt;/p>
&lt;p>❌ Can’t easily restore a single tenant’s data&lt;/p>
&lt;/div>
&lt;hr>
&lt;h3 id="db_1sc---scalability">DB_1SC - Scalability&lt;/h3>
&lt;div style="font-size:60%;text-align:left">
&lt;p>❌ Limited to scaling-up hardware, rather than scaling out&lt;/p>
&lt;p>❌ Risk of “noisy neighbors”—tenants can impact the performance of the system for all others due to a lack of isolation and all competing for the same resources&lt;/p>
&lt;p>❌ One-size-fits-all performance tuning and stability—tenants’ data volumes and usage can vary dramatically, impacting things such as execution plans making it more difficult to optimize performance across every tenant&lt;/p>
&lt;p>❌ As the number of tenants and data per tenant grows, maintenance activities take longer, potentially impacting all tenants.&lt;/p>
&lt;/div>
&lt;hr>
&lt;h3 id="2-single-database-separate-schema-span-styletext-transform-nonedb_nscsspan">2/ Single database, separate schema (&lt;span style="text-transform: none;">DB_nSCs&lt;/span>)&lt;/h3>
&lt;div style="font-size:60%">
&lt;ul>
&lt;li>One database to hold the data for all tenants&lt;/li>
&lt;li>Separate tables for each tenant, each set under a tenant-specific schema&lt;/li>
&lt;/ul>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/slides/multitenancy/db-approaches/SingleDatabase_MultiSchemas_hu72e9f46fdb58b66412c035c9a5b697d5_37855_03d5f314b455fea583e0ce4edf20b6bc.webp 400w,
/slides/multitenancy/db-approaches/SingleDatabase_MultiSchemas_hu72e9f46fdb58b66412c035c9a5b697d5_37855_ee9f7ec30d483efa7784810671d06e1f.webp 760w,
/slides/multitenancy/db-approaches/SingleDatabase_MultiSchemas_hu72e9f46fdb58b66412c035c9a5b697d5_37855_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://slides.nauda.dev/slides/multitenancy/db-approaches/SingleDatabase_MultiSchemas_hu72e9f46fdb58b66412c035c9a5b697d5_37855_03d5f314b455fea583e0ce4edf20b6bc.webp"
width="760"
height="266"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;/div>
&lt;hr>
&lt;h3 id="span-styletext-transform-nonedb_nscsspan---security">&lt;span style="text-transform: none;">DB_nSCs&lt;/span> - Security&lt;/h3>
&lt;div style="font-size:60%;text-align:left">
&lt;p>✔️ Tenant data has some more isolation (but still within the same database)&lt;/p>
&lt;p>✔️ No RLS needed; reduced risk of missing a WHERE clause to limit to specific tenant’s data&lt;/p>
&lt;p>❌ Still a risk of querying the incorrect schema (e.g., specifying the schema for an object when it should have instead come from the user account’s default schema—usual best practice is include schema prefixes, which can feel unnatural)&lt;/p>
&lt;p>❌ Still limited data isolation&lt;/p>
&lt;/div>
&lt;hr>
&lt;h3 id="span-styletext-transform-nonedb_nscsspan---maintainability">&lt;span style="text-transform: none;">DB_nSCs&lt;/span> - Maintainability&lt;/h3>
&lt;div style="font-size:60%;text-align:left">
&lt;p>✔️ 1 database to manage High Availability/Disaster Recovery/maintenance operation/monitoring strategy for&lt;/p>
&lt;p>✔️ Extra scope and control over some tenant-specific maintenance activities&lt;/p>
&lt;p>❌ Schema updates more involved, needing to be rolled out to n tenants&lt;/p>
&lt;p>❌ Added query complexity: use proper schema&lt;/p>
&lt;p>❌ Can’t easily restore a single tenant’s data (although it’s a slightly better process than approach 1 due to isolation of tenant data)&lt;/p>
&lt;p>❌ Adding new tenants is more involved as new schemas/user accounts need to be created&lt;/p>
&lt;p>❌ As the number of tenants grows, there will be a lot of database objects being created to manage and maintain&lt;/p>
&lt;/div>
&lt;hr>
&lt;h3 id="span-styletext-transform-nonedb_nscsspan---scalability">&lt;span style="text-transform: none;">DB_nSCs&lt;/span> - Scalability&lt;/h3>
&lt;div style="font-size:60%;text-align:left">
&lt;p>✔️ Data is partitioned into smaller tables, with smaller indexes&lt;/p>
&lt;p>✔️ Optimizations could be made at an individual tenant’s schema level&lt;/p>
&lt;p>❌ Limited to scaling-up (vertical scaling), rather than scaling out (horizontal scaling)&lt;/p>
&lt;p>❌ Risk of “noisy neighbors”—tenants can impact the performance of the system for all others due to limited level of isolation and all competing for the same resources&lt;/p>
&lt;/div>
&lt;hr>
&lt;h3 id="3-one-database-per-tenant-span-styletext-transform-nonendbsspan">3/ One Database Per Tenant (&lt;span style="text-transform: none;">nDBs&lt;/span>)&lt;/h3>
&lt;div style="font-size:60%">
&lt;p>Each tenant has their own database&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/slides/multitenancy/db-approaches/One-Database-Per-Tenant_hu793ec06b13344f9c562b800e41b33b95_23227_1759ee8966acb05455adc18a22e8d953.webp 400w,
/slides/multitenancy/db-approaches/One-Database-Per-Tenant_hu793ec06b13344f9c562b800e41b33b95_23227_2732f96d37a63cf4657943fe73daa1b8.webp 760w,
/slides/multitenancy/db-approaches/One-Database-Per-Tenant_hu793ec06b13344f9c562b800e41b33b95_23227_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://slides.nauda.dev/slides/multitenancy/db-approaches/One-Database-Per-Tenant_hu793ec06b13344f9c562b800e41b33b95_23227_1759ee8966acb05455adc18a22e8d953.webp"
width="565"
height="235"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;/div>
&lt;hr>
&lt;h3 id="span-styletext-transform-nonendbsspan---security">&lt;span style="text-transform: none;">nDBs&lt;/span> - Security&lt;/h3>
&lt;div style="font-size:60%;text-align:left">
&lt;p>✔️ Highest level of tenant isolation, supporting options for shared server and/or isolated servers&lt;/p>
&lt;p>❌ Potentially more servers to patch and keep secure&lt;/p>
&lt;/div>
&lt;hr>
&lt;h3 id="span-styletext-transform-nonendbsspan---maintainability">&lt;span style="text-transform: none;">nDBs&lt;/span> - Maintainability&lt;/h3>
&lt;div style="font-size:60%;text-align:left">
&lt;p>✔️ Maintenance jobs can be managed and customized per tenant&lt;/p>
&lt;p>✔️ Can easily restore/relocate/clear down a tenant’s data&lt;/p>
&lt;p>✔️ No added query complexity&lt;/p>
&lt;p>❌ Adding new tenants is more involved, as new schemas need to be created&lt;/p>
&lt;p>❌ As the number of tenants grows, there will be more databases being created to manage and maintain&lt;/p>
&lt;p>❌ Some added complexity to maintain a registry of tenant-db mappings/application code to determine which connection to use&lt;/p>
&lt;/div>
&lt;hr>
&lt;h3 id="span-styletext-transform-nonendbsspan---scalability">&lt;span style="text-transform: none;">nDBs&lt;/span> - Scalability&lt;/h3>
&lt;div style="font-size:60%;text-align:left">
&lt;p>✔️ Scale-out and scale-up are both options—tenants can be spread over multiple servers&lt;/p>
&lt;p>✔️ Choose to balance between cost (higher tenant density/fewer servers) and performance (lower tenant density/more servers)&lt;/p>
&lt;p>✔️ Control over “noisy neighbor” risks&lt;/p>
&lt;/div>
&lt;hr>
&lt;h3 id="4-multiple-databases-multiple-tenants-per-database-shared-schema-span-styletext-transform-nonensv_db_1scspan">4/ Multiple Databases, Multiple Tenants Per Database, Shared Schema (&lt;span style="text-transform: none;">nSV_DB_1SC&lt;/span>)&lt;/h3>
&lt;div style="font-size:60%">
&lt;ul>
&lt;li>Hybrid of approach #1 and approach #3&lt;/li>
&lt;li>A pool of databases exist&lt;/li>
&lt;li>Tenants share a database and schema with other tenants, but are spread over multiple databases&lt;/li>
&lt;/ul>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/slides/multitenancy/db-approaches/MultipleDatabases_SharedSchema_hu5fe81c2a3ee5d15dbba29d70c4544119_20794_3d22248289d4b898c5724644e7c1faf1.webp 400w,
/slides/multitenancy/db-approaches/MultipleDatabases_SharedSchema_hu5fe81c2a3ee5d15dbba29d70c4544119_20794_10403e0fb05591643e9475c7e23d75f4.webp 760w,
/slides/multitenancy/db-approaches/MultipleDatabases_SharedSchema_hu5fe81c2a3ee5d15dbba29d70c4544119_20794_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://slides.nauda.dev/slides/multitenancy/db-approaches/MultipleDatabases_SharedSchema_hu5fe81c2a3ee5d15dbba29d70c4544119_20794_3d22248289d4b898c5724644e7c1faf1.webp"
width="619"
height="263"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;/div>
&lt;hr>
&lt;h3 id="span-styletext-transform-nonensv_db_1scspan---security">&lt;span style="text-transform: none;">nSV_DB_1SC&lt;/span> - Security&lt;/h3>
&lt;div style="font-size:60%;text-align:left">
&lt;p>✔️ Some tenant isolation possible in general over approach #1&lt;/p>
&lt;p>❌ Tenants still share a database and schema with others (same RLS mitigation applies as approach #1)&lt;/p>
&lt;/div>
&lt;hr>
&lt;h3 id="span-styletext-transform-nonensv_db_1scspan---maintainability">&lt;span style="text-transform: none;">nSV_DB_1SC&lt;/span> - Maintainability&lt;/h3>
&lt;div style="font-size:60%;text-align:left">
&lt;p>✔️ Choose to balance between overhead of more databases to maintain (lower tenant density) versus fewer (higher tenant density)&lt;/p>
&lt;p>✔️ Possible to relocate a tenant’s data (although harder than approach #3)&lt;/p>
&lt;p>❌ More maintenance overhead than approach #1&lt;/p>
&lt;/div>
&lt;hr>
&lt;h3 id="span-styletext-transform-nonensv_db_1scspan---scalability">&lt;span style="text-transform: none;">nSV_DB_1SC&lt;/span> - Scalability&lt;/h3>
&lt;div style="font-size:60%;text-align:left">
&lt;p>✔️ Scale-out and scale-up are both options—tenants can be spread over multiple servers&lt;/p>
&lt;p>✔️ Choose to balance between cost (higher tenant density/fewer servers) and performance (lower tenant density/more servers)&lt;/p>
&lt;/div>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
data-background-image="featured.jpg"
data-background-opacity="0.2"
data-background-position="45% 15%"
>
&lt;h2 id="span-styletext-transform-noneapplication-povspan">&lt;span style="text-transform: none;">Application PoV&lt;/span>&lt;/h2>
&lt;hr>
&lt;h3 id="at-first-glance">At first glance&lt;/h3>
&lt;ul>
&lt;li>While you can bring multitenancy into existing applications, it makes it easier to build solutions when &lt;em>multitenancy is considered foundational&lt;/em>.&lt;/li>
&lt;li>Multitenancy typically touches every aspect of an application from &lt;em>authentication&lt;/em> and &lt;em>authorization&lt;/em>, &lt;em>business logic&lt;/em>, &lt;em>database schema&lt;/em>, and even elements users won’t see like &lt;em>the hosting environment&lt;/em>.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="two-pillar-parties">Two pillar parties&lt;/h3>
&lt;table>
&lt;tbody>
&lt;tr>
&lt;td>
&lt;p>&lt;strong>Parties&lt;/strong>&lt;/p>
&lt;/td>
&lt;td>
&lt;div style="border:2px solid lightgray;border-radius:25px;padding: .25em .5em;">
&lt;ul>
&lt;li>&lt;code>Tenants&lt;/code>: Our client, using the service&lt;/li>
&lt;li>&lt;code>Host&lt;/code>: Service provider - us!&lt;/li>
&lt;/ul>
&lt;div>
&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h3 id="an-ideal-app---1-unaware-of-multi-tenancy">An ideal app - 1/ Unaware of multi-tenancy&lt;/h3>
&lt;p>✔️ Work seamlessly as much as possible!&lt;/p>
&lt;ul>
&lt;li>You shouldn’t pass &lt;code>tenantId&lt;/code> to all your controllers, application services, repositories or domain services…&lt;/li>
&lt;li>Do all tenancy related stuff in a low level layer and keep your business code clean as much as possible.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="an-ideal-app---2-deployable-to-on-premise-as-well">An ideal app - 2/ Deployable to on-premise as well&lt;/h3>
&lt;p>✔️ When a customer/tenant wants to setup your solution to his own servers, you should be doing that without any code changes.&lt;/p>
&lt;hr>
&lt;h3 id="challenges">Challenges&lt;/h3>
&lt;ol>
&lt;li>Maintaining application state&lt;/li>
&lt;li>Identifying the Active Tenant&lt;/li>
&lt;li>Data isolation&lt;/li>
&lt;li>Set &lt;code>tenantId&lt;/code> for New Entities&lt;/li>
&lt;li>DB Connection String Selection&lt;/li>
&lt;li>Database Migration&lt;/li>
&lt;/ol>
&lt;blockquote>
&lt;p>Hereafter, sample codes are in .NET Core&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;style>
.ae60898 {
color: rgba(240,240,240,.2);
}
.ae60898 h3 {
display: none;
}
.ae60898.___1 li:nth-child(1),
.ae60898.___2 li:nth-child(2),
.ae60898.___3 li:nth-child(3),
.ae60898.___4 li:nth-child(4),
.ae60898.___5 li:nth-child(5),
.ae60898.___6 li:nth-child(6) {
color: #a3f7a3;
}
&lt;/style>
&lt;div class="ae60898 ___1">
&lt;h3 id="curtain---challenges">Curtain - Challenges&lt;/h3>
&lt;ol>
&lt;li>Maintaining application state&lt;/li>
&lt;li>Identifying the Active Tenant&lt;/li>
&lt;li>Data isolation&lt;/li>
&lt;li>Set tenantId for New Entities&lt;/li>
&lt;li>DB Connection String Selection&lt;/li>
&lt;li>Database Migration&lt;/li>
&lt;/ol>
&lt;div>
&lt;hr>
&lt;h4 id="1-maintaining-application-state">1/ Maintaining application state&lt;/h4>
&lt;blockquote>
&lt;p>Application code &amp;amp; services &lt;ins>should be stateless&lt;/ins>!&lt;/p>
&lt;/blockquote>
&lt;p>Where should we save the state?🤔&lt;/p>
&lt;ol>
&lt;li>HTTP Request (cookie, header, query string, payload)&lt;/li>
&lt;li>Authentication ticket&lt;/li>
&lt;li>Database&lt;/li>
&lt;li>Distributed cache (Redis, Memcached, &amp;hellip;)&lt;/li>
&lt;/ol>
&lt;hr>
&lt;div class="ae60898 ___2">
&lt;h3 id="curtain---challenges-1">Curtain - Challenges&lt;/h3>
&lt;ol>
&lt;li>Maintaining application state&lt;/li>
&lt;li>Identifying the Active Tenant&lt;/li>
&lt;li>Data isolation&lt;/li>
&lt;li>Set tenantId for New Entities&lt;/li>
&lt;li>DB Connection String Selection&lt;/li>
&lt;li>Database Migration&lt;/li>
&lt;/ol>
&lt;div>
&lt;hr>
&lt;h4 id="2-identifying-the-active-tenant">2/ Identifying the Active Tenant&lt;/h4>
&lt;p>How to determine the current tenant?🤔&lt;/p>
&lt;ol>
&lt;li>&lt;code>&lt;ins>CurrentUser&lt;/ins>TenantResolveContributor&lt;/code>&lt;/li>
&lt;li>&lt;code>&lt;ins>QueryString&lt;/ins>TenantResolveContributor&lt;/code>&lt;/li>
&lt;li>&lt;code>&lt;ins>Route&lt;/ins>TenantResolveContributor&lt;/code>&lt;/li>
&lt;li>&lt;code>&lt;ins>Header&lt;/ins>TenantResolveContributor&lt;/code>&lt;/li>
&lt;li>&lt;code>&lt;ins>Cookie&lt;/ins>TenantResolveContributor&lt;/code>&lt;/li>
&lt;li>&lt;code>&lt;ins>Domain&lt;/ins>TenantResolver&lt;/code>&lt;/li>
&lt;/ol>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
data-background-image="NET_Core_Logo.svg.png"
data-background-opacity="0.2"
data-background-position="5% 88%"
data-background-size="15%"
>
&lt;h4 id="2-identifying-the-active-tenant---current-user-claims">2/ Identifying the Active Tenant - Current User (claims)&lt;/h4>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/slides/multitenancy/app/active-tenant/1-current-user_hu833c63cfe4a5a845921dd4b66db24bb0_156532_bfc0dcc04bacdd08f5f35e03ca50b9b0.webp 400w,
/slides/multitenancy/app/active-tenant/1-current-user_hu833c63cfe4a5a845921dd4b66db24bb0_156532_66df229879694624ac1a60360c8173b4.webp 760w,
/slides/multitenancy/app/active-tenant/1-current-user_hu833c63cfe4a5a845921dd4b66db24bb0_156532_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://slides.nauda.dev/slides/multitenancy/app/active-tenant/1-current-user_hu833c63cfe4a5a845921dd4b66db24bb0_156532_bfc0dcc04bacdd08f5f35e03ca50b9b0.webp"
width="760"
height="260"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
data-background-image="NET_Core_Logo.svg.png"
data-background-opacity="0.2"
data-background-position="5% 88%"
data-background-size="15%"
>
&lt;h4 id="2-identifying-the-active-tenant---query-string">2/ Identifying the Active Tenant - Query string&lt;/h4>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/slides/multitenancy/app/active-tenant/2-qs_huf9413e58e06529044ab44934e8ab1e5b_180200_35fa7e6a47659b0ce012283107228170.webp 400w,
/slides/multitenancy/app/active-tenant/2-qs_huf9413e58e06529044ab44934e8ab1e5b_180200_106308ea38187948606ee166132b4f7b.webp 760w,
/slides/multitenancy/app/active-tenant/2-qs_huf9413e58e06529044ab44934e8ab1e5b_180200_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://slides.nauda.dev/slides/multitenancy/app/active-tenant/2-qs_huf9413e58e06529044ab44934e8ab1e5b_180200_35fa7e6a47659b0ce012283107228170.webp"
width="760"
height="243"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
data-background-image="NET_Core_Logo.svg.png"
data-background-opacity="0.2"
data-background-position="5% 88%"
data-background-size="15%"
>
&lt;h4 id="2-identifying-the-active-tenant---route">2/ Identifying the Active Tenant - Route&lt;/h4>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/slides/multitenancy/app/active-tenant/3-route_hu441f524001b1fd18dd2789e5cb0ecf91_214321_f8455ea68e1616f35cfe09ac5cf0394e.webp 400w,
/slides/multitenancy/app/active-tenant/3-route_hu441f524001b1fd18dd2789e5cb0ecf91_214321_194f1e0ef671e6bbf7f9d52c30806afe.webp 760w,
/slides/multitenancy/app/active-tenant/3-route_hu441f524001b1fd18dd2789e5cb0ecf91_214321_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://slides.nauda.dev/slides/multitenancy/app/active-tenant/3-route_hu441f524001b1fd18dd2789e5cb0ecf91_214321_f8455ea68e1616f35cfe09ac5cf0394e.webp"
width="760"
height="262"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
data-background-image="NET_Core_Logo.svg.png"
data-background-opacity="0.2"
data-background-position="5% 88%"
data-background-size="15%"
>
&lt;h4 id="2-identifying-the-active-tenant---header">2/ Identifying the Active Tenant - Header&lt;/h4>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/slides/multitenancy/app/active-tenant/4-header_hu699ffecbe37012d16f8bb11dc6527bfb_393995_8f3cb07a59f0eb0796567efb72e567a8.webp 400w,
/slides/multitenancy/app/active-tenant/4-header_hu699ffecbe37012d16f8bb11dc6527bfb_393995_5a05200d64edc74e004894a7c9c46ca1.webp 760w,
/slides/multitenancy/app/active-tenant/4-header_hu699ffecbe37012d16f8bb11dc6527bfb_393995_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://slides.nauda.dev/slides/multitenancy/app/active-tenant/4-header_hu699ffecbe37012d16f8bb11dc6527bfb_393995_8f3cb07a59f0eb0796567efb72e567a8.webp"
width="760"
height="319"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
data-background-image="NET_Core_Logo.svg.png"
data-background-opacity="0.2"
data-background-position="5% 88%"
data-background-size="15%"
>
&lt;h4 id="2-identifying-the-active-tenant---cookies">2/ Identifying the Active Tenant - Cookies&lt;/h4>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/slides/multitenancy/app/active-tenant/5-cookie_hu028fb22bd7157a0b22e6daa122dacb24_339340_863d10c3c5e13e4aa26706cd90a7dbd7.webp 400w,
/slides/multitenancy/app/active-tenant/5-cookie_hu028fb22bd7157a0b22e6daa122dacb24_339340_409d93fe7954e4e75a7c7862b2ada283.webp 760w,
/slides/multitenancy/app/active-tenant/5-cookie_hu028fb22bd7157a0b22e6daa122dacb24_339340_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://slides.nauda.dev/slides/multitenancy/app/active-tenant/5-cookie_hu028fb22bd7157a0b22e6daa122dacb24_339340_863d10c3c5e13e4aa26706cd90a7dbd7.webp"
width="760"
height="307"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;div class="ae60898 ___3">
&lt;h3 id="curtain---challenges-2">Curtain - Challenges&lt;/h3>
&lt;ol>
&lt;li>Maintaining application state&lt;/li>
&lt;li>Identifying the Active Tenant&lt;/li>
&lt;li>Data isolation&lt;/li>
&lt;li>Set tenantId for New Entities&lt;/li>
&lt;li>DB Connection String Selection&lt;/li>
&lt;li>Database Migration&lt;/li>
&lt;/ol>
&lt;div>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
data-background-image="NET_Core_Logo.svg.png"
data-background-opacity="0.2"
data-background-position="5% 88%"
data-background-size="15%"
>
&lt;h4 id="3-data-isolation---dont">3/ Data isolation - Don&amp;rsquo;t!&lt;/h4>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/slides/multitenancy/app/data-isolation/traditional_hub44dca0e73321ddfbb527ef0c27bf79f_154879_a03f54f2f46b958b4f903fcf1567ea0e.webp 400w,
/slides/multitenancy/app/data-isolation/traditional_hub44dca0e73321ddfbb527ef0c27bf79f_154879_d079991dc3ad0a0117ad6a156f5d4238.webp 760w,
/slides/multitenancy/app/data-isolation/traditional_hub44dca0e73321ddfbb527ef0c27bf79f_154879_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://slides.nauda.dev/slides/multitenancy/app/data-isolation/traditional_hub44dca0e73321ddfbb527ef0c27bf79f_154879_a03f54f2f46b958b4f903fcf1567ea0e.webp"
width="760"
height="326"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
data-background-image="NET_Core_Logo.svg.png"
data-background-opacity="0.2"
data-background-position="5% 88%"
data-background-size="15%"
>
&lt;h4 id="3-data-isolation---use-framework-feature">3/ Data isolation - Use Framework feature&lt;/h4>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/slides/multitenancy/app/data-isolation/ef-core-global-query-filters_hu2c49808c8827816cf832a7a6c347ecd6_244979_5e8466e636509bdfd6d31ee2e609eec2.webp 400w,
/slides/multitenancy/app/data-isolation/ef-core-global-query-filters_hu2c49808c8827816cf832a7a6c347ecd6_244979_90b61220a6256b2c536263dc8af6c39e.webp 760w,
/slides/multitenancy/app/data-isolation/ef-core-global-query-filters_hu2c49808c8827816cf832a7a6c347ecd6_244979_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://slides.nauda.dev/slides/multitenancy/app/data-isolation/ef-core-global-query-filters_hu2c49808c8827816cf832a7a6c347ecd6_244979_5e8466e636509bdfd6d31ee2e609eec2.webp"
width="760"
height="281"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/slides/multitenancy/app/data-isolation/multitenant-entity_hu7067132a710b9b0147967d88d3e52b3f_192981_86d5272575b6284f10c4b677e8c4e2a2.webp 400w,
/slides/multitenancy/app/data-isolation/multitenant-entity_hu7067132a710b9b0147967d88d3e52b3f_192981_125f79fe36c02a9f18928b3ef1917b8c.webp 760w,
/slides/multitenancy/app/data-isolation/multitenant-entity_hu7067132a710b9b0147967d88d3e52b3f_192981_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://slides.nauda.dev/slides/multitenancy/app/data-isolation/multitenant-entity_hu7067132a710b9b0147967d88d3e52b3f_192981_86d5272575b6284f10c4b677e8c4e2a2.webp"
width="760"
height="199"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;div class="ae60898 ___4">
&lt;h3 id="curtain---challenges-3">Curtain - Challenges&lt;/h3>
&lt;ol>
&lt;li>Maintaining application state&lt;/li>
&lt;li>Identifying the Active Tenant&lt;/li>
&lt;li>Data isolation&lt;/li>
&lt;li>Set tenantId for New Entities&lt;/li>
&lt;li>DB Connection String Selection&lt;/li>
&lt;li>Database Migration&lt;/li>
&lt;/ol>
&lt;div>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
data-background-image="NET_Core_Logo.svg.png"
data-background-opacity="0.2"
data-background-position="5% 88%"
data-background-size="15%"
>
&lt;h4 id="4-set-tenantid-for-new-entities">4/ Set &lt;code>tenantId&lt;/code> for New Entities&lt;/h4>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/slides/multitenancy/app/set-tenantid_hu6a0f819d2e789aecfca7bbc8a1b24bef_178413_a23fbaab124c0dd8df24c7088e490bf6.webp 400w,
/slides/multitenancy/app/set-tenantid_hu6a0f819d2e789aecfca7bbc8a1b24bef_178413_fcde4a1a16ba2f573aa8d1225c5234f2.webp 760w,
/slides/multitenancy/app/set-tenantid_hu6a0f819d2e789aecfca7bbc8a1b24bef_178413_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://slides.nauda.dev/slides/multitenancy/app/set-tenantid_hu6a0f819d2e789aecfca7bbc8a1b24bef_178413_a23fbaab124c0dd8df24c7088e490bf6.webp"
width="760"
height="348"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;div class="ae60898 ___5">
&lt;h3 id="curtain---challenges-4">Curtain - Challenges&lt;/h3>
&lt;ol>
&lt;li>Maintaining application state&lt;/li>
&lt;li>Identifying the Active Tenant&lt;/li>
&lt;li>Data isolation&lt;/li>
&lt;li>Set tenantId for New Entities&lt;/li>
&lt;li>DB Connection String Selection&lt;/li>
&lt;li>Database Migration&lt;/li>
&lt;/ol>
&lt;div>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
data-background-image="NET_Core_Logo.svg.png"
data-background-opacity="0.2"
data-background-position="5% 88%"
data-background-size="15%"
>
&lt;h4 id="5-db-connection-string-selection">5/ DB Connection String Selection&lt;/h4>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/slides/multitenancy/app/dbstring-select_hu569d24618cdeb7fa48daabfb572b455b_427130_61ed8476839a1c9c651f9afec1d797ec.webp 400w,
/slides/multitenancy/app/dbstring-select_hu569d24618cdeb7fa48daabfb572b455b_427130_3db8db5136d9cf4b41a28ae7e64d7b54.webp 760w,
/slides/multitenancy/app/dbstring-select_hu569d24618cdeb7fa48daabfb572b455b_427130_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://slides.nauda.dev/slides/multitenancy/app/dbstring-select_hu569d24618cdeb7fa48daabfb572b455b_427130_61ed8476839a1c9c651f9afec1d797ec.webp"
width="760"
height="312"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;div class="ae60898 ___6">
&lt;h3 id="curtain---challenges-5">Curtain - Challenges&lt;/h3>
&lt;ol>
&lt;li>Maintaining application state&lt;/li>
&lt;li>Identifying the Active Tenant&lt;/li>
&lt;li>Data isolation&lt;/li>
&lt;li>Set tenantId for New Entities&lt;/li>
&lt;li>DB Connection String Selection&lt;/li>
&lt;li>Database Migration&lt;/li>
&lt;/ol>
&lt;div>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
data-background-image="NET_Core_Logo.svg.png"
data-background-opacity="0.2"
data-background-position="5% 88%"
data-background-size="15%"
>
&lt;h4 id="6-db-migrations---make-db-migration-with-a-custom-tool">6/ DB Migrations - Make DB migration with a custom tool&lt;/h4>
&lt;div style="text-align:left;font-size:75%;">
&lt;p>😊 Easy to implement. All tenants are in the same version&lt;/p>
&lt;p>😡 May get too long time for big number of tenants and data&lt;/p>
&lt;p>😡 All tenants wait for all upgrade progress&lt;/p>
&lt;/div>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
data-background-image="NET_Core_Logo.svg.png"
data-background-opacity="0.2"
data-background-position="5% 88%"
data-background-size="15%"
>
&lt;h4 id="6-db-migrations---run-migration-on-first-db-access">6/ DB Migrations - Run migration on first DB access&lt;/h4>
&lt;div style="text-align:left;font-size:75%;">
&lt;p>😊 Upgrading is distributed to time. A tenant does not wait for another&lt;/p>
&lt;p>😡 First user may wait too much and see timeout exception.&lt;/p>
&lt;p>😡 Hard to implement (concurrency problems)!&lt;/p>
&lt;/div>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
data-background-image="NET_Core_Logo.svg.png"
data-background-opacity="0.2"
data-background-position="5% 88%"
data-background-size="15%"
>
&lt;h4 id="6-db-migrations---make-two-types-application-servers">6/ DB Migrations - Make two types application servers&lt;/h4>
&lt;div style="text-align:left;font-size:75%;list-style:none;">
&lt;p>Ideal as upgraded tenants use the new application, other tenants use the old application.&lt;/p>
&lt;p>😊 Minimum wait time for a tenant&lt;/p>
&lt;p>😊 Upgrading can be scheduled for tenants&lt;/p>
&lt;p>😊 Run A/B tests and see bugs before anyone else&lt;/p>
&lt;p>😡 Requires multiple app servers&lt;/p>
&lt;p>😡 Hard to maintain and monitor&lt;/p>
&lt;/div>
&lt;hr>
&lt;h2 id="references">References&lt;/h2>
&lt;ol>
&lt;li>&lt;a href="https://opensource-db.com/multi-tenancy-on-postgres/" target="_blank" rel="noopener">⭐ Multitenancy on PostgreSQL&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=3uWeyEbV4c4" target="_blank" rel="noopener">⭐ Building Multi-Tenant ASP.NET Core Applications and ABP Framework | .NET Conf 2023&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://learn.microsoft.com/en-us/azure/architecture/guide/multitenant/overview" target="_blank" rel="noopener">Azure Architecture - Multi-tenant overview&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.gooddata.com/blog/multi-tenant-architecture/" target="_blank" rel="noopener">[GoodData] Multi-tenant Architecture&lt;/a>&lt;/li>
&lt;/ol></description></item></channel></rss>