Quebrando as regras: usando SQLite para aplicativos da Web de demonstração

Publicados: 2022-03-10
Resumo rápido ↬ Então, você criou o próximo aplicativo da Web matador, mas agora é apresentado a você a pergunta que todo novo produto deve considerar: “Como posso mostrar a todos o quão bom meu aplicativo é?”

A maioria dos usuários em potencial desejará experimentar o software ou serviço antes de comprometer tempo e dinheiro. Alguns produtos funcionam muito bem apenas oferecendo aos usuários uma avaliação gratuita, enquanto outros aplicativos são mais experientes com dados de amostra já instalados. Muitas vezes, é aqui que a conta demo antiga entra em jogo.

No entanto, quem já implementou uma conta demo pode atestar os problemas associados. Você sabe como as coisas funcionam na Internet: qualquer pessoa pode inserir dados (se faz sentido ou não para o produto) e há uma boa chance de que o conteúdo adicionado por usuários anônimos ou bots possa ser ofensivo para outras pessoas. Claro, você sempre pode redefinir o banco de dados, mas com que frequência e quando? E, em última análise, isso realmente resolve o problema? Minha solução para usar SQLite .

Por que não usar o SQLite para a versão de produção?

É sabido que o SQLite não lida com vários threads, pois todo o banco de dados é bloqueado durante um comando de gravação, que é uma das razões pelas quais você não deve usá-lo em um ambiente de produção normal. No entanto, na minha solução, um arquivo SQLite separado é usado para cada usuário que demonstra o software. Isso significa que a limitação de gravação é limitada apenas a esse usuário, mas vários usuários simultâneos (cada um com seu próprio arquivo de banco de dados) não terão essa limitação. Isso permite uma experiência controlada para o usuário testar o software e permite que eles visualizem exatamente o que você deseja que eles vejam.

Mais depois do salto! Continue lendo abaixo ↓

Este tutorial é baseado em uma solução do mundo real que tenho executado com sucesso para um aplicativo Web de demonstração SaaS desde 2015. O tutorial foi escrito para Ruby on Rails (minha estrutura de escolha) versão 3 e superior, mas os conceitos básicos devem ser capaz de ser adaptado a qualquer outra linguagem ou framework. De fato, como Ruby on Rails segue o paradigma de software "convenção sobre configuração" pode até ser mais fácil de implementar em outros frameworks, especialmente linguagens simples (como PHP puro) ou frameworks que não fazem muito em termos de gerenciamento de conexões de banco de dados .

Dito isto, esta técnica é particularmente adequada para Ruby on Rails. Por quê? Porque, na maioria das vezes, é um "banco de dados agnóstico". O que significa que você deve ser capaz de escrever seu código Ruby e alternar entre bancos de dados sem problemas.

Uma amostra de uma versão finalizada desse processo pode ser baixada do GitHub.

A primeira etapa: ambiente de implantação

Chegaremos à implantação mais tarde, mas Ruby on Rails é, por padrão, dividido em ambientes de desenvolvimento, teste e produção. Vamos adicionar a esta lista um novo ambiente de demonstração para nosso aplicativo que será quase idêntico ao ambiente de produção, mas nos permitirá usar diferentes configurações de banco de dados.

No Rails, crie um novo ambiente duplicando o config/environments/production.rb e renomeie-o como demo.rb . Como o ambiente de demonstração será usado em uma configuração semelhante à produção, talvez você não precise alterar muitas opções de configuração para esse novo ambiente, embora eu sugira alterar config.assets.compile de false para true , o que facilitará o teste local sem ter que pré-compilar.

Se você estiver executando o Rails 4 ou superior, também precisará atualizar o config/secrets.yml para adicionar um secret_key_base para o ambiente de demonstração. Certifique-se de tornar essa chave secreta diferente da produção para garantir que as sessões sejam exclusivas entre cada ambiente, protegendo ainda mais seu aplicativo.

Em seguida, você precisa definir a configuração do banco de dados em config/database.yml . Embora o ambiente de demonstração use principalmente o banco de dados duplicado que abordaremos na próxima seção, devemos definir o arquivo de banco de dados padrão e as configurações a serem usadas para nossa demonstração. Adicione o seguinte a config/database.yml :

 demo: adapter: sqlite3 pool: 5 timeout: 5000 database: db/demo.sqlite3

No Rails, você também pode verificar seu Gemfile para ter certeza de que o SQLite3 está disponível no novo ambiente de demonstração. Você pode definir isso de várias maneiras, mas pode ficar assim:

 group :development, :test, :demo do gem 'sqlite3' end

Uma vez que o banco de dados está configurado, você precisa rake db:migrate RAILS_ENV=demo e então semear os dados no banco de dados como quiser (seja de um arquivo seed, inserindo manualmente novos dados ou até mesmo duplicando o arquivo development.sqlite3 ). Neste ponto, você deve verificar se tudo está funcionando executando rails server -e demo na linha de comando. Enquanto estiver executando o servidor no novo ambiente de demonstração, você pode certificar-se de que seus dados de teste estão como você deseja, mas você sempre pode voltar e editar esse conteúdo mais tarde. Ao adicionar seu conteúdo ao banco de dados de demonstração, recomendo criar um conjunto limpo de dados para que o arquivo seja o menor possível. No entanto, se você precisar migrar dados de outro banco de dados, recomendo o YamlDb, que cria um formato independente de banco de dados para despejar e restaurar dados.

Se sua aplicação Rails estiver rodando conforme o esperado, você pode passar para a próxima etapa.

A segunda etapa: usando o banco de dados de demonstração

A parte essencial deste tutorial é permitir que cada sessão use um arquivo de banco de dados SQLite diferente. Normalmente, seu aplicativo se conectará ao mesmo banco de dados para todos os usuários, de modo que será necessário código adicional para essa tarefa.

Para começar a permitir que Ruby on Rails alterne bancos de dados, primeiro precisamos adicionar os quatro métodos privados a seguir em application_controller.rb . Você também precisará definir um filtro anterior para o método set_demo_database para que a lógica que referencia o banco de dados de demonstração correto seja chamada em cada carregamento de página.

 # app/controllers/application_controller.rb # use `before_filter` for Rails 3 before_action :set_demo_database, if: -> { Rails.env == 'demo' } private # sets the database for the demo environment def set_demo_database if session[:demo_db] # Use database set by demos_controller db_name = session[:demo_db] else # Use default 'demo' database db_name = default_demo_database end ActiveRecord::Base.establish_connection(demo_connection(db_name)) end # Returns the current database configuration hash def default_connection_config @default_config ||= ActiveRecord::Base.connection.instance_variable_get("@config").dup end # Returns the connection hash but with database name changed # The argument should be a path def demo_connection(db_path) default_connection_config.dup.update(database: db_path) end # Returns the default demo database path defined in config/database.yml def default_demo_database return YAML.load_file("#{Rails.root.to_s}/config/database.yml")['demo']['database'] end

Como cada sessão do servidor terá um banco de dados diferente, você armazenará o nome do arquivo do banco de dados em uma variável de sessão. Como você pode ver, estamos usando session[:demo_db] para rastrear o banco de dados específico do usuário. O método set_demo_database está controlando qual banco de dados usar estabelecendo a conexão com o banco de dados definido na variável de sessão. O método default_demo_database simplesmente carrega o caminho do banco de dados conforme definido no arquivo de configuração database.yml .

Se você estiver usando uma linguagem simples, neste ponto você provavelmente pode apenas atualizar seu script de conexão de banco de dados para apontar para o novo banco de dados e então passar para a próxima seção. No Rails, as coisas exigem mais alguns passos porque segue o paradigma de software "convenção sobre configuração".

A terceira etapa: duplicando o arquivo SQLite

Agora que o aplicativo está configurado para usar o novo banco de dados, precisamos de um gatilho para a nova sessão de demonstração. Para simplificar, comece usando apenas um botão básico "Iniciar demonstração". Você também pode torná-lo um formulário onde você coleta um nome e endereço de e-mail (para um acompanhamento da equipe de vendas, etc.) ou qualquer outra coisa.

Seguindo as convenções do Rails, crie um novo controlador 'Demo':

 rails generate controller demos new

Em seguida, você deve atualizar as rotas para apontar para suas novas ações do controlador, envolvendo-as em uma condicional para evitar que sejam chamadas no ambiente de produção. Você pode nomear as rotas como quiser ou nomeá-las usando as convenções padrão do Rails:

 if Rails.env == 'demo' get 'demos/new', as: 'new_demo' post 'demos' => 'demos#create', as: 'demos' end

Em seguida, vamos adicionar um formulário bem básico ao views/demos/new.html.erb . Você pode querer adicionar campos de formulário adicionais para capturar:

 <h1>Start a Demo</h1> <%= form_tag demos_path, method: :post do %> <%= submit_tag 'Start Demo' %> <% end %>

A mágica acontece na ação de create . Quando o usuário se submete a esta rota, a ação copiará o arquivo demo.sqlite3 com um novo nome de arquivo exclusivo, definirá variáveis ​​de sessão, fará o login do usuário (se aplicável) e redirecionará o usuário para a página apropriada (chamaremos isso de 'painel de controle').

 class DemosController < ApplicationController def new # Optional: setting session[:demo_db] to nil will reset the demo session[:demo_db] = nil end def create # make db/demos dir if doesn't exist unless File.directory?('db/demos/') FileUtils.mkdir('db/demos/') end # copy master 'demo' database master_db = default_demo_database demo_db = "db/demos/demo-#{Time.now.to_i}.sqlite3" FileUtils::cp master_db, demo_db # set session for new db session[:demo_db] = demo_db # Optional: login code (if applicable) # add your own login code or method here login(User.first) # Redirect to wherever you want to send the user next redirect_to dashboard_path end end

Agora você deve ser capaz de testar o código de demonstração localmente, iniciando novamente o servidor usando rails server -e demo .

Se você já tinha o servidor em execução, será necessário reiniciá-lo para quaisquer alterações feitas, pois ele está configurado para armazenar em cache o código como o servidor de produção.

Quando todo o código funcionar como esperado, confirme suas alterações no controle de versão e certifique-se de confirmar o arquivo demo.sqlite3 , mas não os arquivos no diretório db/demos . Se você estiver usando o git, basta adicionar o seguinte ao seu arquivo .gitignore :

Se você deseja coletar informações adicionais do usuário de demonstração (como nome e/ou e-mail), provavelmente desejará enviar essas informações por meio de uma API para seu aplicativo principal ou algum outro pipeline de vendas, pois seu banco de dados de demonstração não será confiável (reinicia toda vez que você reimplantar).

 !/db/demo.sqlite3 db/demos/*

Etapa final: implantar seu servidor de demonstração

Agora que você tem sua configuração de demonstração funcionando localmente, obviamente desejará implantá-la para que todos possam usá-la. Embora cada aplicativo seja diferente, recomendo que o aplicativo de demonstração resida em um servidor separado e, portanto, em um domínio como seu aplicativo de produção (como demo.myapp.com). Isso garantirá que você mantenha os dois ambientes isolados. Além disso, como o arquivo SQLite é armazenado no servidor, serviços como o Heroku não funcionarão, pois não fornecem acesso ao sistema de arquivos. No entanto, você ainda pode usar praticamente qualquer provedor de VPS (como AWS EC2, Microsoft Azure, etc). Se você gosta da conveniência automatizada, existem outras opções de Plataformas como Serviço que permitem trabalhar com VPS.

Independentemente do seu processo de implantação, você também pode precisar verificar se o aplicativo tem as permissões de leitura/gravação apropriadas para seu diretório onde você armazena os arquivos SQLite de demonstração. Isso pode ser tratado manualmente ou com um gancho de implantação.

SQLite não funcionará para mim. E quanto a outros sistemas de banco de dados?

Não há dois aplicativos criados iguais e nem seus requisitos de banco de dados. Ao utilizar o SQLite, você tem a vantagem de poder duplicar rapidamente o banco de dados, além de poder armazenar o arquivo no controle de versão. Embora eu acredite que o SQLite funcione para a maioria das situações (especialmente com Rails), existem situações em que o SQLite pode não ser adequado para as necessidades do seu aplicativo. Felizmente, ainda é possível usar os mesmos conceitos acima com outros sistemas de banco de dados. O processo de duplicação de um banco de dados será um pouco diferente para cada sistema, mas vou descrever uma solução para MySQL e um processo semelhante existe com PostgreSQL e outros.

A maioria dos métodos abordados acima funciona sem quaisquer modificações adicionais. No entanto, em vez de armazenar um arquivo SQLite em seu controle de versão, você deve usar mysqldump (ou pg_dump para PostgreSQL) para exportar um arquivo SQL de qualquer banco de dados que tenha o conteúdo que você gostaria de usar para sua experiência de demonstração. Este arquivo também deve ser armazenado em seu controle de versão.

As únicas alterações no código anterior serão encontradas na ação demos#create . Em vez de copiar o arquivo SQLite3, a ação do controlador criará um novo banco de dados, carregará o arquivo sql nesse banco de dados e concederá permissões para o usuário do banco de dados, se necessário. A terceira etapa de concessão de acesso só é necessária se o usuário administrador do banco de dados for diferente do usuário que o aplicativo usa para se conectar. O código a seguir faz uso de comandos padrão do MySQL para lidar com essas etapas:

 def create # database names template_demo_db = default_demo_database new_demo_db = "demo_database_#{Time.now.to_i}" # Create database using admin credentials # In this example the database is on the same server so passing a host argument is not require `mysqladmin -u#{ ENV['DB_ADMIN'] } -p#{ ENV['DB_ADMIN_PASSWORD'] } create #{new_demo_db}` # Load template sql into new database # Update the path if it differs from where you saved the demo_template.sql file `mysql -u#{ ENV['DB_ADMIN'] } -p#{ ENV['DB_ADMIN_PASSWORD'] } #{new_demo_db} < db/demo_template.sql` # Grant access to App user (if applicable) `mysql -u#{ ENV['DB_ADMIN'] } -p#{ ENV['DB_ADMIN_PASSWORD'] } -e "GRANT ALL on #{new_demo_db}.* TO '#{ ENV['DB_USERNAME'] }'@'%';"` # set session for new db session[:demo_db] = new_demo_db # Optional: login code (if applicable) # add your own login code or method here login(User.first) redirect_to dashboard_path end

Ruby, como muitas outras linguagens incluindo PHP, permite que você use backticks para executar um comando shell (ou seja, `ls -a` ) de dentro do seu código. No entanto, você deve usar isso com cuidado e garantir que nenhum parâmetro ou variável voltado para o usuário possa ser inserido no comando para proteger seu servidor de código injetado maliciosamente. Neste exemplo, estamos interagindo explicitamente com as ferramentas de linha de comando do MySQL, que é a única maneira de criar um novo banco de dados. É da mesma forma que o framework Ruby on Rails cria um novo banco de dados. Certifique-se de substituir ENV['DB_ADMIN'] e ENV['DB_ADMIN_PASSWORD'] por sua própria variável de ambiente ou qualquer outra forma de definir o nome de usuário do banco de dados. Você precisará fazer o mesmo para o ENV['DB_USERNAME'] se seu usuário administrador for diferente do usuário do seu aplicativo.

Isso é tudo o que é preciso para mudar para o MySQL! A vantagem mais óbvia dessa solução é que você não precisa se preocupar com possíveis problemas que possam aparecer a partir da sintaxe diferente entre os sistemas de banco de dados.

Eventualmente, uma decisão final é tomada com base na qualidade e serviço esperados, em vez de conveniência e velocidade, e não é necessariamente influenciada apenas pelo preço.

Pensamentos finais

Este é apenas um ponto de partida para o que você pode fazer com seu novo servidor de demonstração. Por exemplo, seu site de marketing pode ter um link para "Experimentar o recurso XYZ". Se você não precisar de um nome ou e-mail, poderá vincular o método demos#create a um link como /demos/?feature=xyz e a ação simplesmente redirecionará para o recurso e/ou página desejados, em vez do painel em o exemplo acima.

Além disso, se você usar o SQLite para os ambientes de desenvolvimento e demonstração, sempre ter esse banco de dados de amostra no controle de versão daria a todos os seus desenvolvedores acesso a um banco de dados limpo para uso em desenvolvimento local, ambientes de teste ou testes de garantia de qualidade. As possibilidades são infinitas.

Você pode baixar uma demonstração completa do GitHub.