A Appodeal é uma empresa de aproximadamente 100 pessoas que trabalha em Moscou, São Francisco, Barnaul, Lutsk, Kirov, Barcelona e, desde junho de 2018, também em Minsk.
Geramos receita com aplicativos móveis exibindo anúncios para os usuários. Começamos com a mediação de publicidade, mas a pilha de tecnologia está em constante crescimento; portanto, outros produtos da indústria de tecnologia de anúncios também foram adicionados à mediação.

Para aqueles que não estão familiarizados com a Ad Tech, essa é a área de trabalho das empresas de tecnologia que trabalham no campo da publicidade. Quando você diz a alguém que trabalha no campo da publicidade móvel, as pessoas geralmente reagem com ceticismo - aparentemente, o anúncio irritante "Azino Three Axes" vem à mente. De fato, isso é apenas a ponta do iceberg, e toda essa publicidade "selvagem" não tem nada a ver com o negócio real de publicidade.
E o segmento para celular em que estamos envolvidos há muito tempo superou o segmento de publicidade na web:

Por que integrar anúncios em aplicativos?
Obviamente, muitos recursos são gastos na criação de aplicativos - e os criadores / proprietários desejam o tempo e o esforço gastos para pagar. Os proprietários de aplicativos móveis que postam seus aplicativos na App Store / Google Play são chamados de editores ou editores. Os editores aplicam diferentes modelos de monetização, desde compras no aplicativo até monetização de publicidade. Mas, de todos esses métodos, apenas o último permite que o usuário não pague pelo uso do aplicativo - e isso oferece a maior cobertura do público.
Sim, se houver muita publicidade, isso irritará a todos e afetará negativamente a retenção de usuários. Do qual, é claro, ninguém precisa. Portanto, eles sempre tentam integrar a publicidade com sabedoria, a fim de ganhar o máximo de dinheiro em seu aplicativo e, ao mesmo tempo, não gastar um centavo dos usuários.
Como isso funciona?
Assim que o editor decide gerar receita com publicidade, ele chega à empresa que pode facilitar essa tarefa o mais possível. Como isso acontece com o Appodeal? Depois de se registrar no site, integramos sua aplicação ao nosso serviço. Isso é feito através do SDK do cliente, que conecta o aplicativo à parte do servidor e se comunica com a parte do servidor por meio da API.
Se você minimizar os detalhes, o objetivo da interação será reduzido para dois estágios:
a. Determine qual anúncio será exibido agora;
b. Envie informações sobre qual anúncio foi exibido e quais não e exiba-o nas estatísticas.
Atualmente, o Appodeal atende vários milhares de aplicativos ativos que fornecem aproximadamente 400 a 450 milhões de impressões de anúncios por dia, recebidos em resposta a cerca de 1 bilhão de solicitações para redes de anúncios (que são as fornecedoras de publicidade). Para fazer isso funcionar, nossos servidores atendem a cerca de 125 mil solicitações por segundo, ou seja, aproximadamente 10,8 bilhões de consultas por dia.

Em que tudo isso é construído?
Utilizamos várias tecnologias para fornecer velocidade, confiabilidade e ao mesmo tempo flexibilidade de desenvolvimento e suporte. No momento, estamos escrevendo código nos seguintes idiomas:
- / Ruby / Ruby on Rails + React.JS (front-end) /: Ainda há uma grande parte da API e toda a Web Part que os usuários e nossos funcionários veem
- / GoLang /: processando grandes quantidades de dados estatísticos e não apenas
- / Scala /: solicitações de processamento em tempo real para trabalhar com trocas de troca de tráfego usando o protocolo RTB (leia mais sobre isso no final do artigo)
- / Elixir / Phoenix /: Pelo contrário, a parte experimental. Construindo alguns microsserviços para lidar com algumas estatísticas e APIs.

Por que é originalmente Ruby e Ruby on Rails?
O Appodeal concorre em seu segmento com players muito grandes, então você precisa se adaptar rapidamente às mudanças do mercado. Muitas vezes, isso parece uma mudança nas rodas de um carro a uma velocidade de 100 km / h. O Ruby on Rails nos permitiu resistir à corrida e conquistar uma posição no mercado o suficiente para ser líder em seu segmento. As principais vantagens do Rails em nossa opinião:
- Um grande número de desenvolvedores qualificados
- Ótima comunidade. Um grande número de soluções e bibliotecas prontas
- A velocidade de introduzir novos recursos e alterar / excluir os antigos

Dos menos óbvios:
- O desempenho geral é ruim. Isso também afeta a falta de JIT (no momento), a falta de capacidade de paralelizar o código (se você não levar em conta o JRuby). Até certo ponto, isso permanece suportável porque o gargalo geralmente é o banco de dados e o cache. O que vemos na foto da NewRelic:

- Os monólitos de trilho não são muito bons nos microsserviços - eles são afetados por um alto grau de conectividade entre a lógica de negócios e a lógica de acesso a dados (ActiveRecord).
Como os dados são armazenados?
Temos muitos dados. Muito. Estamos falando de bilhões / dezenas / centenas de bilhões de registros. Como os dados são completamente diferentes, os armazenamos de maneiras diferentes. Nunca deve ser limitado em arquitetura a qualquer solução, que é supostamente universal. A prática mostra que, em primeiro lugar, em Highload, praticamente não existem soluções universais. Universalidade significa indicadores médios (ou significativamente inferiores à média) para acesso / velocidade de leitura / tamanho do armazenamento de dados como uma taxa por essa versatilidade. Em segundo lugar, você precisa tentar algo novo o tempo todo, experimentar e procurar soluções não triviais para as tarefas. Total:
- / PostgreSQL /: Nós amamos o Postgre. Consideramos a melhor solução de armazenamento OLTP no momento. Os dados sobre usuários, aplicativos, campanhas publicitárias e assim por diante são armazenados lá. Usamos replicação de réplica primária. Fazemos backups apenas nas férias de Natal, porque isso é para os fracos (uma piada).
- / VerticaDB /: banco de dados orientado a colunas. Usamos para armazenar bilhões de registros estatísticos. Em suma, o Vertika foi considerado por algum tempo a melhor solução OLAP para armazenar análises. A principal desvantagem é o enorme preço (individual) da licença.
- / ClickHouse /: também um banco de dados orientado a colunas. Gradualmente, mude para ele com o VerticaDB. Consideramos a melhor solução OLAP no momento. Não vale um centavo. Funciona de maneira muito rápida e confiável. O principal ponto negativo é que os dados não podem ser excluídos e atualizados (falaremos sobre isso em um artigo separado, se alguém estiver interessado).

Nada! Como é impossível excluir e modificar dados ?!
- / Aerospike /: O armazenamento de valor-chave NoSQL mais rápido em nossa opinião. Há uma série de desvantagens, mas em geral estamos satisfeitos. Existe até um gráfico de comparação para o Aerospike em seu site de desempenho com outras soluções: [Quando usar o banco de dados Aerospike NoSQL vs. Redis] (https://www.aerospike.com/when-to-use-aerospike-vs-redis/)
- / Redis /: Sobre “Rabanete”, eu acho, não faz sentido contar separadamente. Paradoxalmente, sua principal vantagem é a facilidade de uso e a segmentação única, que evitam as condições de corrida, por exemplo, ao trabalhar com contadores comuns.
- / Druid /: Usamos para grandes conjuntos de dados em trabalho com trocas de RTB. De fato, na maioria das vezes, ele joga no mesmo campo do ClickHouse, mas, historicamente, ainda não conseguimos mudar para nenhum instrumento.

Esse conjunto pode parecer sobrecarregado, mas, em primeiro lugar, o Appodeal é um grande conglomerado de várias equipes de desenvolvimento e vários projetos em uma. E segundo, essas são as duras realidades da tecnologia de anúncios - não somos os únicos a usar uma pilha de vários andares dentro de uma empresa.
Como você segue isso?
Como os fluxos de dados são grandes, eles precisam estar na fila para processá-los. Como fila, usamos Kafka. Esta é uma ótima solução confiável, escrita em Scala, que nunca nos falhou ainda.
O único requisito para o usuário nesse caso é que ele tenha tempo para percorrer a fila sempre crescente mais rapidamente do que cresce. Uma regra simples e óbvia. Portanto, para esses fins, usamos principalmente o GoLang. No entanto, isso não nega o fato de que a RAM neste servidor deve ser abundante.
Para monitorar toda essa economia, é necessário monitorar e delegar literalmente tudo em uma fileira. Para isso, usamos:
- / NewRelic /: Uma solução testada pelo tempo que se integra perfeitamente aos micro serviços Ruby on Rails e GoLang. O único menos do NewRelic é o seu preço. Portanto, o NewRelic não está em toda parte conosco. Na maioria das vezes, tentamos substituí-lo por nossas próprias métricas coletadas à mão - as colocamos na Grafana.
- / Statsd + Grafana /: uma coisa boa para coletar suas métricas. Com o único menos que você precisa configurar tudo sozinho e "repetir" a funcionalidade NewRelic imediatamente.
- / ElasticSearch + Fluentd + Kibana /: nos logs, colocamos tudo em uma linha. De consultas lentas do PostgreSQL a algumas mensagens do sistema Rails. Na verdade, uma solução como o Kibana, baseada no ElasticSearch, permite coletar convenientemente todos os logs em um único local e, em seguida, procurar as mensagens necessárias neles.
- / Airbrake /: obrigatório nesse processo de coleta de erros junto com o stacktrace'ami da mensagem. No momento, estamos saindo com o Airbrake para uma das soluções gratuitas. Por uma razão, novamente, preços.

Você precisa entender que o monitoramento adequadamente construído são seus olhos e ouvidos. Cegamente impossível trabalhar. Você precisa ver o que está acontecendo em seus servidores em um determinado momento, para que a estabilidade e a confiabilidade do seu produto dependam amplamente da competência com que você constrói um sistema para coletar e exibir métricas.
A propósito, falando em confiabilidade, contemos vários servidores intermediários para pré-lançamento e verificação de lançamentos, que mantemos estáveis sob carga, duplicando parte do tráfego real lá. Toda semana sincronizamos bancos de dados entre produção e preparação. Isso nos dá uma espécie de "espelho" que permite testar coisas que não podem ser verificadas localmente, além de identificar problemas no nível do teste de carga.
É realmente tão complicado?
Acontece assim. Como Elon Musk escreveu em seu livro: "As melhores mentes da minha geração estão ocupadas fazendo as pessoas clicarem em anúncios", disse-me Jeff Hammerbacher, engenheiro do Facebook. "Horror ..." Uma pequena lista do que o Appodeal faz:
- Estamos integrados a duas dezenas de redes e agências de publicidade. No modo automático, registramos aplicativos nessas redes e configuramos vários parâmetros para que essas redes funcionem com desempenho máximo. Nem toda rede possui APIs correspondentes, em algum lugar você precisa fazer isso com robôs.
- Cada rede paga aos usuários receita por impressões, que devem ser recebidas, discriminadas por vários parâmetros e processadas. Isso é feito sem parar. Em algum lugar, novamente, por robôs.
- Para proporcionar ao usuário uma renda máxima, nós “fazemos” as redes competirem entre si, construindo a chamada “cascata” a partir de ofertas publicitárias. A cascata é construída com base em vários indicadores, por exemplo, eCPM (preço médio por 1.000 impressões), que previmos de várias maneiras. Quanto maior a oferta de publicidade na cascata, mais predizeremos o preço. Essa cascata é oferecida no dispositivo sempre que necessário. Como você deve ter adivinhado, um anúncio no qual ninguém clica e que apenas irrita todo mundo não interessa a ninguém. A exceção é apenas a chamada. Anúncios de banner com "marca" da Coca-Cola, Pepsi e outros gigantes corporativos que costumam falar sobre si mesmos sempre e em qualquer lugar.
- Parte dessa interação é construída no chamado protocolo RTB (Real-Time Bidding):

Nesse caso, os chamados licitantes são negociados on-line em um leilão pelo direito de exibir seus anúncios no dispositivo selecionado. Um ponto muito interessante, digno de um artigo separado. Muitas trocas, como o Google AdExchange, definem uma estrutura rígida para o tempo de resposta do servidor (por exemplo, 50 ms), o que coloca um problema de desempenho inicial. Em caso de desobediência - uma multa de milhares de dólares. É exatamente isso que o kernel escrito em Scala faz em conjunto com o Druid.
Cada caçador quer saber onde fica o faisão e nossos clientes (como nós) querem saber para quem o anúncio foi exibido, quando e por que. Portanto, temos que enfileirar (Kafka) toda a pilha de dados que temos, processar gradualmente e adicionar ao banco de dados OLAP (ClickHouse). Muitas pessoas pensam que o PostgreSQL lidará com essa tarefa não pior do que qualquer solução "hipster", mas não é assim. O PostgreSQL é bom, mas a solução clássica para construir índices para a velocidade de acesso a dados para de funcionar quando o número de campos para filtragem e classificação exceder 10, e a quantidade de dados armazenados se aproximar de 1 bilhão de registros. Você simplesmente não tem memória suficiente para armazenar todos esses índices ou terá problemas para atualizar esses índices. De qualquer forma, você não poderá obter o mesmo desempenho que as soluções orientadas a colunas para consultas analíticas.
Conclusão
Neste artigo, tentei descrever pelo menos brevemente o que fazemos, como armazenamos e processamos dados. Conte-nos nos comentários que pilha você usa, faça perguntas e solicite novos artigos - teremos o maior prazer em compartilhar nossa experiência.