Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Scaling Multi-tenant Applications Using the Dja...

Citus Data
November 11, 2018

Scaling Multi-tenant Applications Using the Django ORM & Postgres | PyCon Canada 2018 | Sai Srirampur

In the real-world there are 10000s of B2B companies. Their app-stack fits the multi-tenant model - each tenant(customer) deals with it’s own data. It is super critical to build scalable applications which gives the company leeway to grow as more customers get on-boarded. Let’s learn how to do that!

Citus Data

November 11, 2018
Tweet

More Decks by Citus Data

Other Decks in Technology

Transcript

  1. Sai Srirampur | PyConCA 2018 Sai Srirampur | PyConCA 2018

    Scaling Multi-Tenant Applications Using the Django ORM & Postgres Sai Srirampur PyCon Canada | Toronto | Nov 2018
  2. Sai Srirampur | PyConCA 2018 • Sai Srirampur a.k.a Sai

    • Engineer at Citus Data • Joined Citus to make it so developers never have to worry about scaling their database • Creator django multi-tenant • Follow me @saisrirampur @citusdata
  3. Sai Srirampur | PyConCA 2018 Sai Srirampur | PyConCA 2018

    PYTHON & DJANGO POSTGRES SCALING MULTI-TENANT APPLICATIONS
  4. Sai Srirampur | PyConCA 2018 Sai Srirampur | PyConCA 2018

    Why Postgres? TLDR; Open source Constraints Extensions PostGIS / Geospatial HLL, TopN, Citus Foreign data wrappers Rich SQL CTEs Window functions Full text search Datatypes JSONB
  5. Sai Srirampur | PyConCA 2018 Today… Scaling Multi-Tenant Apps Using

    the Django ORM & Postgres 3 architectures to build Multi-tenant apps django_multitenant Scaling multi-tenant apps with distributed postgres
  6. Sai Srirampur | PyConCA 2018 • Multiple customers (“tenants”) •

    Each with own data • SaaS • Shopify, Salesforce Multi-Tenant Apps Sai Srirampur | PyConCA 2018
  7. Sai Srirampur | PyConCA 2018 Sai Srirampur | PyConCA 2018

    Why talk about scalable multi-tenant applications? tens 100’s 1000’s
  8. Sai Srirampur | PyConCA 2018 Sai Srirampur | PyConCA 2018

    Architectures To Build Multi-Tenant Applications 3
  9. Sai Srirampur | PyConCA 2018 What defines a Database? •

    Organized collection of interrelated data • Don’t share resources. • Username and password • Connections • Memory
  10. Sai Srirampur | PyConCA 2018 Sai Srirampur | PyConCA 2018

    CREATE DATABASE tenant_100; ./manage.py migrate --database=tenant_100; One database per tenant / Onboarding database_routing: db_for_read, db_for_write etc.
  11. Sai Srirampur | PyConCA 2018 • Start quickly • Isolate

    customer (tenant) data • Compliance is a bit easier • Time for DBA/developer to manage • Maintain consistency (ex: create index across all databases) • Longer running migrations • Performance degrades as # customers (tenants) goes up PROS CONS [1] One Database Per Tenant Sai Srirampur | PyConCA 2018
  12. Sai Srirampur | PyConCA 2018 What defines a database Schema?

    • Logical namespaces to hold a set of tables • Share resources: • Username and password • Connections • Memory
  13. Sai Srirampur | PyConCA 2018 One schema per tenant Tenant

    5 Tenant 1251 Tenant 1252 Database [2]
  14. Sai Srirampur | PyConCA 2018 Sai Srirampur | PyConCA 2018

    CREATE schema tenant_100; ./manage.py migrate --schema=tenant_100; app_code for schema routing based on tenant One schema per tenant / Onboarding
  15. Sai Srirampur | PyConCA 2018 • Better resource utilization vs.

    one database per tenant • Start quickly • Logical isolation • Hard to manage (ex: add column across all schemas) • Longer running migrations • Performance degrades as # customers (tenants) goes up PROS CONS [2] One Schema Per Tenant Sai Srirampur | PyConCA 2018
  16. Sai Srirampur | PyConCA 2018 • Easy maintenance • Faster

    running migrations • Best resource utilization • Faster performance • Scales to 1k-100k tenants • Application code to guarantee isolation • Make sure ORM calls are always scoped to a single tenant PROS CONS [3] Shared Table Architecture :-) Sai Srirampur | PyConCA 2018
  17. Sai Srirampur | PyConCA 2018 Comparing the 3 architectures for

    scaling multi-tenant Django applications ONE DATABASE PER TENANT ONE SCHEMA PER TENANT SHARED TABLE ARCHITECTURE Database Database [1] [2] [3] start quickly & isolation guarantees, bad resource utilization & not scalable better resource util, logical isolation not scalable Scales to over 100K tenants! explicitly handle isolation
  18. Sai Srirampur | PyConCA 2018 Sai Srirampur | PyConCA 2018

    django_multitenant Automates all ORM calls to be scoped to a single tenant
  19. Sai Srirampur | PyConCA 2018 Sai Srirampur | PyConCA 2018

    With django_multitenant Purchase.objects.filter(id=1) <=> "SELECT* from purchase where id=1 and store_id=<current_tenant>"
  20. Sai Srirampur | PyConCA 2018 django_multitenant usage — 3 steps

    1. Inherit all models with TenantModel 2. Change ForeignKey to TenantForeignKey 3. Define tenant scoping: set_current_tenant(t)
  21. Sai Srirampur | PyConCA 2018 TenantForeignKey mimics composite foreign key

    behavior • Adds tenant_id filter to referenced model. Handles: • Reference lookups — ex: (Purchase.product.name) • select_related() / prefetch_related() • Explicit Joins (product__name)
  22. Sai Srirampur | PyConCA 2018 set_current_tenant(t) • Specifies which tenant

    the APIs should be scoped to • Set at authentication logic via middleware • Set explicitly at top of function (ex. view, external tasks/jobs)
  23. Sai Srirampur | PyConCA 2018 Models without django_multitenant class Purchase(models.Model):

    store = models.ForeignKey(Store) product_purchased = models.ForeignKey(Product) ordered_at = models.DateTimeField(default=timezone.now) billing_address = models.TextField() Sai Srirampur | PyConCA 2018
  24. Sai Srirampur | PyConCA 2018 Post django_multitenant class Purchase(TenantModel): store

    = models.ForeignKey(Store) product_purchased = TenantForeignKey(Product) ordered_at = models.DateTimeField(default=timezone.now) billing_address = models.TextField() Sai Srirampur | PyConCA 2018
  25. Sai Srirampur | PyConCA 2018 Setting the tenant at authentication

    class SetCurrentTenantFromUser(object): def process_request(self, request): if not hasattr(self, 'authenticator'): from rest_framework_jwt.authentication import JSONWebTokenAuthentication self.authenticator = JSONWebTokenAuthentication() try: user, _ = self.authenticator.authenticate(request) except: return try: #Assuming your app has a function to get the tenant associated for a user current_tenant = get_tenant_for_user(user) except: # TODO: handle failure return set_current_tenant(current_tenant) Sai Srirampur | PyConCA 2018
  26. Sai Srirampur | PyConCA 2018 Benefits of django_multitenant Drop-in implementation

    of shared tables architecture Guarantees isolation Ready to scale with distributed Postgres (Citus)