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

Aplicações multi-tenacidade com Django

Aplicações multi-tenacidade com Django

Multi-tenacidade (multitenancy) é característica de um sistema atender vários clientes a partir de uma única instância de software. Esta é uma característica bastante fácil de se verificar em sistemas SaaS mas que pode ser encontrada em diversos níveis e formas em arquiteturas de software.
O caso de uso mais comum de uso de muni-tenacidade é para isolamento de dados entre clientes. Uma forma de fazer isso é através do uso de múltiplos bancos de dados. Outra forma bastante comum é utilizar-se de apenas um banco de dados e modelar relacionamentos nas tabelas de forma que seja possível realizar consultas isoladas. A terceira forma mais comum é, novamente, ter apenas um banco de dados mas desta vez ter 'schemas' separados para cada cliente.
Nesta palestra falarei sobre cada um destas abordagens. Para cada uma delas você entenderá como funciona a arquitetura, como desenvolve-la utilizando Django, verá exemplos de como realizar consultas e conhecerá as ferramentas que podem ajudar no processo de desenvolvimento. Ao fim você será capaz de escolher a abordagem que melhor se adequa a sua próxima aplicação.

Filipe Ximenes

October 08, 2017
Tweet

More Decks by Filipe Ximenes

Other Decks in Programming

Transcript

  1. Aplicações Multi 'Tenant'
    em Django
    @xima

    View Slide

  2. @xima

    View Slide

  3. vinta.com.br/playbook

    View Slide

  4. FLOSS
    Django React boilerplate
    https://github.com/vintasoftware/django-react-boilerplate
    Django Role Permissions
    https://github.com/vintasoftware/django-role-permissions
    Tapioca
    https://github.com/vintasoftware/tapioca-wrapper

    View Slide

  5. O que é multi tenacidade?

    View Slide

  6. "Inquilino"
    "Locatário"

    View Slide

  7. View Slide

  8. "... se refere a arquitetura de
    software onde uma única
    instância de software 'roda' em
    um servidor e serve múltiplos
    'inquilinos'."
    - [tradução livre] Wikipedia

    View Slide

  9. Single Tenant
    1 client =
    1 server +
    1 db

    View Slide

  10. Multi Tenant
    n clients =
    1 server +
    ? db

    View Slide

  11. O que queremos?
    ● Reduzir custo de infraestrutura;
    ● Simplificar a manutenção da infraestrutura;
    ● Simplificar a manutenção da base de código;

    View Slide

  12. Contexto

    View Slide

  13. Corporate
    Fidget Spinner
    Tracking™

    View Slide

  14. View Slide

  15. View Slide

  16. View Slide

  17. "Como você vai proteger os
    nossos dados?"

    View Slide

  18. Single Shared Schema
    [ou como os gigantes fazem]

    View Slide

  19. View Slide

  20. "Talk is cheap..."

    View Slide

  21. Routing - ibm.spinnertracking.com
    def tenant_middleware(get_response):
    def middleware(request):
    host = request.get_host().split(':')[0]
    subdomain = host.split('.')[0]
    try:
    customer = Customer.objects.get(name=subdomain)
    except Customer.DoesNotExist:
    customer = None
    request.customer = customer
    response = get_response(request)
    return response
    return middleware

    View Slide

  22. Querying
    avg_duration = (
    Spin.objects
    .filter(user_spinner__user__customer=request.customer)
    .aggregate(avg=Avg('duration')))['avg']

    View Slide

  23. View Slide

  24. Simpler querying
    avg_duration = (
    Spin.objects
    .filter(customer=request.customer)
    .aggregate(avg=Avg('duration')))['avg']

    View Slide

  25. Case study: Salesforce
    ● Proporção 1:5000;
    ● Checagem de sanidade;
    ● Checagem de software;
    ● Transparência para os desenvolvedores;
    ● https://www.youtube.com/watch?v=jeysYua6ENs

    View Slide

  26. Drawbacks
    ● Difícil de garantir isolamento;
    ● Pode adicionar complexidade ao código;
    ● Problemas de compatibilidade com bibliotecas de terceiros;

    View Slide

  27. Multiple databases

    View Slide

  28. View Slide

  29. Routing
    DATABASES = {
    'default': {
    'ENGINE': ...,
    'NAME': ...,
    },
    'ibm': {
    'ENGINE': ...,
    'NAME': ...,
    }
    }

    View Slide

  30. The `.using()` approach
    spinners = (
    Spinner.objects
    .using(request.customer.name)
    .annotate(
    avg_duration=Avg('owned_spinners__spins__duration'))
    .order_by('-avg_duration'))

    View Slide

  31. The threadlocal middleware approach
    def multidb_middleware(get_response):
    def middleware(request):
    subdomain = get_subdomain(request)
    customer = get_customer(subdomain)
    request.customer = customer
    @thread_local(using_db=customer.name)
    def execute_request(request):
    return get_response(request)
    response = execute_request(request)
    return response
    return middleware

    View Slide

  32. The router
    class TenantRouter(object):
    def db_for_read(self, model, **hints):
    return get_thread_local('using_db', 'default')
    def db_for_write(self, model, **hints):
    return get_thread_local('using_db', 'default')
    # …
    # settings.py
    DATABASE_ROUTERS = ['multitenancy.routers.TenantRouter']

    View Slide

  33. Querying
    spinners = (
    Spinner.objects
    .using(request.customer.name)
    .annotate(
    avg_duration=Avg('owned_spinners__spins__duration'))
    .order_by('-avg_duration'))

    View Slide

  34. Multitenacidade de banco
    vs.
    Multitenacidade de Aplicação

    View Slide

  35. Single Database
    Multiple Schemas

    View Slide

  36. View Slide

  37. O que são 'schemas'?
    SELECT id, name FROM user
    WHERE user.name LIKE 'F%';

    View Slide

  38. O que são 'schemas'?
    CREATE SCHEMA ibm;
    SELECT id, name FROM ibm.user
    WHERE ibm.user.name LIKE 'F%';

    View Slide

  39. O `search_path`
    SET search_path TO ibm;
    SELECT id, name FROM user
    WHERE user.name LIKE 'F%';

    View Slide

  40. Django-tenant-schemas

    View Slide

  41. Routing - middleware
    # ...
    connection.set_schema_to_public()
    hostname = self.hostname_from_request(request)
    TenantModel = get_tenant_model()
    try:
    tenant = self.get_tenant(TenantModel, hostname, request)
    assert isinstance(tenant, TenantModel)
    except TenantModel.DoesNotExist:
    # ...
    request.tenant = tenant
    connection.set_tenant(request.tenant)
    # ...

    View Slide

  42. Routing - settings
    MIDDLEWARE_CLASSES = [
    'tenant_schemas.middleware.TenantMiddleware',
    # …
    ]
    DATABASES = {
    'default': {
    'ENGINE': 'tenant_schemas.postgresql_backend',
    'NAME': 'mydb',
    }
    }

    View Slide

  43. Routing - db backend
    # ...
    try:
    cursor_for_search_path.execute(
    'SET search_path = {0}'.format(','.join(search_paths)))
    except (django.db.utils.DatabaseError, psycopg2.InternalError):
    self.search_path_set = False
    else:
    self.search_path_set = True
    if name:
    cursor_for_search_path.close()
    # ...

    View Slide

  44. Linha de comando
    ./manage.py tenant_command shell
    ./manage.py createsuperuser
    ./manage.py migrate_schemas

    View Slide

  45. Querying
    spinners = (
    Spinner.objects
    .annotate(
    avg_duration=Avg('owned_spinners__spins__duration'))
    .order_by('-avg_duration'))

    View Slide

  46. SELECT id, duration FROM ibm.spinner_spin
    WHERE duration > 120
    UNION
    SELECT id, duration FROM vinta.spinner_spin
    WHERE duration > 120;
    Querying across schemas

    View Slide

  47. SELECT uuid, duration FROM ibm.spinner_spin
    WHERE duration > 120
    UNION
    SELECT uuid, duration FROM vinta.spinner_spin
    WHERE duration > 120;
    Querying across schemas

    View Slide

  48. Pontos Positivos
    ● Abstração das queries;
    ● Criação automática dos 'schemas';
    ● Facilita migrações;
    ● Infraestrutura simplificada;

    View Slide

  49. Pontos Negativos
    ● Escalabilidade(?)
    ● Tests um pouco mais lentos;
    ● Queries transversais complicadas;

    View Slide

  50. multitenancidade
    não é discreta, é um
    expectro contínuo

    View Slide

  51. github.com/filipeximenes/multitenancy
    blog post -> http://bit.ly/django-tenants

    View Slide

  52. Obrigado!
    Newsletter:
    vinta.com.br/blog/
    twitter.com/@xima
    github.com/filipeximenes
    [email protected]

    View Slide

  53. Sites framework
    https://docs.djangoproject.com/en/1.11/ref/contrib/sites/

    View Slide

  54. Feature Toggles
    ● Customizações por tenant;
    ● Pausar bugs;
    ● Integração contínua;
    ● Testes A/B;

    View Slide