
Em 2017, vencemos a competição pelo desenvolvimento do núcleo de transações para os negócios de investimento do Alfa-Bank e começamos a trabalhar imediatamente. (Vladimir Drynkin, líder da equipe de desenvolvimento do núcleo de transações de negócios de investimento do Alfa-Bank,
falou sobre o núcleo de negócios de investimentos no HighLoad ++ 2018.) Esse sistema deveria agregar dados de transações em diferentes formatos de várias fontes, unificar os dados, salvá-los e salvá-los. fornecer acesso a ele.
No processo de desenvolvimento, o sistema evoluiu e ampliou suas funções. Em algum momento, percebemos que criamos algo muito mais do que apenas software de aplicativo projetado para um escopo bem definido de tarefas: criamos um sistema para criar aplicativos distribuídos com armazenamento persistente. Nossa experiência serviu de base para o novo produto,
Tarantool Data Grid (TDG).
Quero falar sobre a arquitetura TDG e as soluções que desenvolvemos durante o desenvolvimento. Apresentarei as funções básicas e mostrarei como nosso produto pode se tornar a base para a construção de soluções chave na mão.
Em termos de arquitetura, dividimos o sistema em
papéis separados. Cada um deles é responsável por uma gama específica de tarefas. Uma instância em execução de um aplicativo implementa um ou mais tipos de função. Pode haver várias funções do mesmo tipo em um cluster:
Conector
O conector é responsável pela comunicação com o mundo exterior; Ele foi projetado para aceitar a solicitação, analisá-la e, se for bem-sucedido, envia os dados para processamento ao processador de entrada. Os seguintes formatos são suportados: HTTP, SOAP, Kafka, FIX. A arquitetura nos permite adicionar suporte para novos formatos (o suporte do IBM MQ está disponível em breve). Se a análise da solicitação falhar, o conector retornará um erro. Caso contrário, ele responderá que a solicitação foi processada com êxito, mesmo se um erro ocorreu durante o processamento adicional. Isso é feito de propósito para trabalhar com os sistemas que não sabem como repetir solicitações ou vice-versa, de forma muito agressiva. Para garantir que nenhum dado seja perdido, a fila de reparo é usada: o objeto entra na fila e é removido dela somente após o processamento bem-sucedido. O administrador recebe notificações sobre os objetos restantes na fila de reparo e pode tentar novamente o processamento após tratar um erro de software ou falha de hardware.
Processador de entrada
O Processador de entrada categoriza os dados recebidos por características e chama os manipuladores correspondentes. Manipuladores são códigos Lua que são executados em uma sandbox, portanto não podem afetar a operação do sistema. Nesse estágio, os dados podem ser transformados conforme necessário e, se necessário, qualquer número de tarefas pode ser executado para implementar a lógica necessária. Por exemplo, ao adicionar um novo usuário no MDM (Master Data Management construído com base no Tarantool Data Grid), um registro de ouro seria criado como uma tarefa separada para que o processamento da solicitação não diminua. O sandbox suporta solicitações de leitura, alteração e adição de dados. Também permite que você chame alguma função para todas as funções do tipo de armazenamento e agregue o resultado (mapear / reduzir).
Os manipuladores podem ser descritos em arquivos:
sum.lua local x, y = unpack(...) return x + y
Então declarado na configuração:
functions: sum: { __file: sum.lua }
Por que Lua? Lua é uma linguagem direta. Com base em nossa experiência, as pessoas começam a escrever um código que resolveria o problema apenas algumas horas depois de ver o idioma pela primeira vez. E estes não são apenas desenvolvedores profissionais, mas, por exemplo, analistas. Além disso, graças ao compilador JIT, Lua é sppedy.
Armazenamento
O armazenamento armazena dados persistentes. Antes de salvar, os dados são validados para conformidade com o esquema de dados. Para descrever o esquema, usamos o formato estendido do
Apache Avro . Exemplo:
{ "name": "User", "type": "record", "logicalType": "Aggregate", "fields": [ { "name": "id", "type": "string" }, { "name": "first_name", "type": "string" }, { "name": "last_name", "type": "string" } ], "indexes": ["id"] }
Com base nessa descrição, o DDL (Data Definition Language) para o Tarantool DBMS e o esquema
GraphQL para acesso a dados são gerados automaticamente.
A replicação de dados assíncrona é suportada (também planejamos adicionar replicação síncrona).
Processador de saída
Às vezes, é necessário notificar os consumidores externos sobre os novos dados. É por isso que temos a função Processador de saída. Após salvar os dados, eles poderiam ser transferidos para o manipulador apropriado (por exemplo, para transformá-los conforme exigido pelo consumidor) e depois transferidos para o conector para envio. A fila de reparo também é usada aqui: se ninguém aceitar o objeto, o administrador poderá tentar novamente mais tarde.
Dimensionamento
As funções Conector, Processador de entrada e Processador de saída são sem estado, o que nos permite escalar o sistema horizontalmente, apenas adicionando novas instâncias de aplicativos com a função ativada necessária. Para dimensionamento de armazenamento horizontal, um cluster é organizado usando a
abordagem de buckets virtuais. Após adicionar um novo servidor, alguns buckets dos servidores antigos são movidos para um novo servidor em segundo plano. Esse processo é transparente para os usuários e não afeta a operação de todo o sistema.
Propriedades de dados
Os objetos podem ser enormes e conter outros objetos. Garantimos a adição e atualização de dados de maneira atômica e o salvamento do objeto com todas as dependências em um único depósito virtual. Isso é feito para evitar a chamada "mancha" do objeto em vários servidores físicos.
O controle de versão também é suportado: cada atualização do objeto cria uma nova versão, e sempre podemos reduzir o tempo para ver como tudo estava no momento. Para dados que não precisam de um histórico longo, podemos limitar o número de versões ou até armazenar apenas a última, ou seja, podemos desativar a versão para um tipo de dados específico. Também podemos definir os limites históricos: por exemplo, exclua todos os objetos de um tipo específico com mais de um ano. O arquivamento também é suportado: podemos fazer upload de objetos acima de uma certa idade para liberar espaço no cluster.
Tarefas
Recursos interessantes a serem observados incluem a capacidade de executar tarefas no prazo, a pedido do usuário ou automaticamente a partir da sandbox:

Aqui podemos ver outro papel chamado Runner. Este papel não tem estado; se necessário, mais instâncias de aplicativos com essa função podem ser adicionadas ao cluster. O corredor é responsável por concluir as tarefas. Como já mencionei, novas tarefas podem ser criadas a partir da sandbox; eles ingressam na fila no armazenamento e, em seguida, são executados no corredor. Esse tipo de tarefa é chamado de trabalho. Também temos um tipo de tarefa chamado Tarefa, ou seja, uma tarefa definida pelo usuário que seria executada no prazo (usando a sintaxe cron) ou sob demanda. Para executar e rastrear essas tarefas, temos um conveniente gerenciador de tarefas. A função do planejador deve estar ativada para usar esta função. Esse papel tem um estado, portanto não dimensiona o que não é necessário de qualquer maneira. No entanto, como qualquer outra função, ela pode ter uma réplica que começa a funcionar se o mestre falhar repentinamente.
Logger
Outra função é chamada Logger. Ele coleta logs de todos os membros do cluster e fornece uma interface para carregar e visualizá-los através da interface da web.
Serviços
Vale ressaltar que o sistema facilita a criação de serviços. No arquivo de configuração, você pode especificar quais solicitações devem ser enviadas ao manipulador escrito pelo usuário em execução na sandbox. Esse manipulador pode, por exemplo, executar algum tipo de solicitação analítica e retornar o resultado.
O serviço é descrito no arquivo de configuração:
services: sum: doc: "adds two numbers" function: sum return_type: int args: x: int y: int
A API do GraphQL é gerada automaticamente e o serviço está disponível para chamadas:
query { sum(x: 1, y: 2) }
Isso chama o manipulador de
sum
que retorna o resultado:
3
Perfis e métricas de solicitação
Implementamos o suporte ao protocolo OpenTracing para proporcionar uma melhor compreensão dos mecanismos do sistema e solicitar criação de perfil. Sob demanda, o sistema pode enviar informações sobre como a solicitação foi executada para as ferramentas que suportam este protocolo (por exemplo, Zipkin):
Desnecessário dizer que o sistema fornece métricas internas que podem ser coletadas usando o Prometheus e visualizadas usando o Grafana.
Implantação
O Tarantool Data Grid pode ser implantado a partir de pacotes ou arquivos RPM usando o utilitário interno ou Ansible. O Kubernetes também é suportado (
Tarantool Kubernetes Operator ).
Um aplicativo que implementa a lógica de negócios (configuração, manipuladores) é carregado no cluster Tarantool Data Grid implantado no arquivo morto por meio da interface do usuário ou como um script usando a API fornecida.
Aplicativos de amostra
Quais aplicativos você pode criar com o Tarantool Data Grid? De fato, a maioria das tarefas de negócios está de alguma forma relacionada ao processamento, armazenamento e acesso ao fluxo de dados. Portanto, se você tiver grandes fluxos de dados que exijam armazenamento e acessibilidade seguros, nosso produto poderá economizar muito tempo em desenvolvimento e ajudá-lo a se concentrar na lógica de negócios.
Por exemplo, você gostaria de reunir informações sobre o mercado imobiliário para se manter atualizado sobre as melhores ofertas no futuro. Nesse caso, destacamos as seguintes tarefas:
- Os robôs que coletam informações de fontes abertas seriam suas fontes de dados. Você pode resolver esse problema usando soluções prontas ou escrevendo código em qualquer idioma.
- Em seguida, o Tarantool Data Grid aceita e salva os dados. Se o formato dos dados de várias fontes for diferente, você poderá escrever um código em Lua que converterá tudo em um único formato. No estágio de pré-processamento, você também pode, por exemplo, filtrar ofertas recorrentes ou atualizar ainda mais as informações do banco de dados sobre agentes que operam no mercado.
- Agora você já possui uma solução escalável no cluster que pode ser preenchida com dados e usada para criar amostras de dados. Em seguida, você pode implementar novas funções, por exemplo, escrever um serviço que criaria uma solicitação de dados e retornaria a oferta mais vantajosa por dia. Seria necessário apenas várias linhas no arquivo de configuração e algum código Lua.
O que vem depois?
Para nós, uma prioridade é aumentar a conveniência do desenvolvimento com o
Tarantool Data Grid . (Por exemplo, este é um IDE com suporte para manipuladores de perfil e depuração que funcionam na sandbox.)
Também prestamos muita atenção aos problemas de segurança. No momento, nosso produto está sendo certificado pela FSTEC da Rússia (Serviço Federal de Tecnologia e Controle de Exportação) para reconhecer o alto nível de segurança e atender aos requisitos de certificação de produtos de software usados em sistemas de informações de dados pessoais e sistemas federais de informações.