Logs Estruturados

Logs Estruturados

Então, meses sem escrever nada mas hoje estou aproveitando o domingo para escrever algumas coisas sobre o que tenho vivido no último ano. Olhando projeções para o próximo ano, vejo alguns pontos relevantes relacionados ao que posso contribuir com o dia-a-dia de vocês.

Você sabe o que são logs estruturados?

Logs estruturados, diferente dos logs de aplicação, comuns, contém uma série informações e metadados adicionais, usados para agrupamento e consulta. Logs estruturados devem conter informações relevantes ao negócio e ao desenvolvedor.

Um log comum de exceções, por exemplo, envia dados da exceção, como mensagem, stack trace, inner exceptions. Um log estruturado de exceções, poderia conter informações que diagnosticam o servidor no qual o log foi gerado, qual a operação estava sendo processada, qual o ID ou dados mais completos dos seus objetos de negócio durante a operação, além da comum severidade. E não para por aí, ainda seria possível informar qual o cliente da requisição, no caso de WebApps, qual o usuário logado, entre outras diversas informações de aplicação que poderiam ser passadas para o log, ajudando no troubleshooting e análise de problemas ou mesmo de fluxos de negócio. (mais…)

[deprecated]Roadmap da Reestruturação do Oragon Architecture

[deprecated]Roadmap da Reestruturação do Oragon Architecture

[deprecated]

Olá, como vai você?

Espero que esteja tudo bem! Nesse post vou falar das mudanças previstas para o Oragon Architecture nas próximas semanas. No post Oragon Architecture – A evolução e os novos desafios eu falo da evolução ao longo dos anos e do que pretendemos fazer focando no big picture do projeto. Agora é hora de especificar os detalhes que envolvem a construção do projeto. Enfim, transformar uma ideia em resultado, para isso é necessário planejar. Abaixo vamos abordar a estratégia de migração do Oragon Architecture. Espero que goste!

(mais…)

[deprecated]Oragon Architecture – Por que? Pra que?

[deprecated]Oragon Architecture – Por que? Pra que?

[deprecated]

Olá, tudo bom?

Vou falar um pouco do meu projeto pessoal o Oragon Architecture. Se você me acompanha, sabe que falo bastante dele, e vou aproveitar para responder algumas perguntas que já me fizeram nos últimos dias.

 

Já fui questionado algumas vezes porque criar um framework de aplicação, uma arquitetura relativamente gorda, baseada em geração de código com base em banco e muita configuração com Spring.Net.

Bom, primeiro, o Oragon Architecture me viabilizou criar projetos que jamais seriam possíveis sem ele, no prazo que foram realizados. Esse é o principal motivador! Com ele alguns projetos de anos, conseguem ser realizados em meses, pelo simples fato de eliminar necessidade de codificação repetitiva. Quando você se preocupa apenas com o negócio deixando de lado aspectos não funcionais, tudo fica mais fluente no dia-a-dia de desenvolvimento. Gestão de Exceptions, Tratamento de erro, gestão de conexões, leak de conexões, sessões não finalizadas, tudo isso deixa de fazer parte do seu código na medida que você passa a usar os aspectos de AOP do Oragon Architecture.

O acesso a dados é bem facilitado, usando NHibernate e FluentNHibernate. Há quem questione o modelo. Bom, tive poucas chances na vida de trabalhar com bases de dados criadas do Zero, ou chances de realizar code-first. Muitas das vezes as empresas possuem equipes de DBA`s ou AD`s que são responsáveis pela modelagem do banco. Essa foi a melhor forma de me adaptar a estes cenários. Do ponto de vista do código gerado, há uma boa riqueza de detalhes na geração de código, como a criação de padrões que podem gerar facilmente nomes fluentes para bancos cheios de regras. Como uma coluna USUA_CD_USUARIO, virar CodigoUsuario, ou apenas Codigo, da mesma forma que USUA_DT_CRIACAO pode virar DataCriacao, com o case correto também. Pluralização em PT-BR e EN-US também são features interessantes da geração de código. Isso permite gerar código de uma forma bem inteligente, muito próximo do que nós mesmo modelaríamos manualmente, contemplando relações 1xN, Nx1, NxN e 1×1.

Quanto ao Spring.Net, bom, o primeiro fator que motiva na utilização dele é o fato de injetarmos modelos de dependências completas e a riqueza de features, como AOP e seus diversos helpers para outros muitos frameworks, como Quartz, RabbitMQ. Uso o Spring.Net desde 2006, uma pesquisa no google pode mostrar isso, nesse período ainda não existia WCF, e o Spring.Net já fazia abstração completa de protocolo para WebServices, Enterprise Services (COM+), Remoting e InProcess.

Aliás, essas abstrações de protocolo`me ajudaram a ver um mundo diferente, e desde então tudo que faço abstrai sempre o protocolo de comunicação. No Oragon Architecture, temos um conjunto de abstrações para plugar serviços em filas RabbitMQ, seguindo os mesmos princípios. Você implementa suas regras de negócio, o resto fica com a arquitetura. Como será a hospedagem, envelope de mensagem In/Out, tratamento de exceções, timeout, isso tudo fica a cargo da arquitetura.

O modelo desconectado do protocolo, permite que possamos desenvolver soluções com múltiplas distribuições, e permite mudar a estratégia de deploy em minutos, saindo de WCF, para WebServices, embedded, ou usando filas RabbitMQ. São muitas possibilidades com um código apenas. Nenhuma dessas mudanças exigem mudanças nos códigos e essa é a mágica dessa arquitetura: Abdicar das decisões mais complexas pelo tempo necessário para que elas obtenham o maior grau de maturidade possível. O pipeline em cima do RabbitMQ também é excelente, permite que um fluxo completo seja dividido em steps com filas de erro e filas de processamento, viabilizando uma grande escalabilidade horizontal.

Mas não são só flores, foram muitos anos construindo o projeto, evoluindo a cada parceiro e empresa que eu passava.

Muito mudou ao longo dos anos, os aspectos de AOP não trabalhavam em conjunto, agora cooperam entre si. A cada demanda nova, novas features nascem, novos conceitos são aplicados. Me orgulho de ter feito, para a BRQ um modelo de MVC, bem próximo do que há no Web API. Quando comecei a estudar Web API, joguei muito código fora, assim como quando encontrei o TopShelf. Me orgulho muito disso pois sempre que eu puder usar algo pronto, para me fornecer uma infraestrutura robusta, vou fazê-lo.

O papel do Oragon Architecture não é recriar a roda, mas coordenar as engrenagens para que se construa software mais fácil.

Muitas vezes sou questionado sobre a performance e escalabilidade da arquitetura:

A arquitetura provê uma infraestrutura robusta para o desenvolvimento corporativo, conta com soluções mais e menos performáticas/escaláveis, mas se usarmos as soluções corretas, como cache, por exemplo, podemos escalar bem mais que soluções que usam ADO.NET para acesso a dados e código 100% inline. Ganhando infinitamente no tempo de desenvolvimento. Não há mistério, cada escolha implica necessariamente em uma renúncia, mas para cada dificuldade, há um facilitador ao lado, e com um pouco de conhecimento de arquitetura, é possível chegar ao infinito!!

 

Grande abraço,

espero que gostem!

Roadmap de Arquitetura – Um exemplo real

Roadmap de Arquitetura – Um exemplo real

As vezes sou questionado sobre meus desenhos de arquitetura e porque criar tantas abstrações, tanta configuração e tantas dependências e frameworks de terceiros, alguns que só eu e um tibetano conhecemos. No post Como definir Arquitetura de Software, cito quais são os pensamentos primários necessários para se desenhar um arquitetura, mas vamos aplicar isso a um contexto real para exemplificar.

Tudo começa com minha chegada na atual empresa, onde encontrei um cenário extremamente complexo e difícil de se trabalhar.

  • Umas 8 bases de dados, entre SQL Server e MySQL, cada uma seguindo dois ou mais padrões antagônicos (sim, na mesma base), diversos padrões nomenclatura. As bases não possuíam um responsável. Eram “da empresa”, não havia um dono.  O acoplamento do modelo de dados era entre database/tecnologia. Isso quer dizer que um dado em uma base SQL Server só deixa de ser dado vira informação em conjunto com dados que estavam em outras bases MySQL, por exemplo.
  • Alto acoplamento entre as diversas aplicações e suas bases de dados.
  • Um parque de aplicações baseadas em .net framework 2.0, usando ADO.NET puro para escrever o acesso a dados: Manualmente. Uma quantidade infinita de código de acesso a dados realizando N+1 Anti-Pattern por causa da forma como o acesso a dados era escrito.
  • Dezenas de micro aplicações, forks de uma aplicação inicial que realizava um processo batch de importação de conteúdo, extremamente importantes para o business. Muitas delas exigiam rodavam na IDE, em modo debug pois um dev trocava algumas strings e executava em modo supervisionado. Isso não era ao acaso, era a forma como processávamos dados de produção no dia-a-dia.
  • Gerência de configuração inexistente, havia um SVN, mas mal era usado para versionador de código.
  • Nenhuma gestão de releases.
  • Centenas de micro-aplicações responsáveis por micro-tarefas, sem documentação, sem identificação e algumas sem sequer os fontes. Sem falar que muitos dos fontes sequer eram versionados ou estavam defasados no repositório de código.
  • Todo o conhecimento tecnológico dependia exclusivamente da memória do time ou daqueles que passaram pela equipe e ainda estavam na empresa.

Dá para ver que há muito a se trabalhar nesse lugar. Né?! Ok, assim começamos.

Primeiro passo foi definir um fluxo geral de gerência de configuração: A partir de então 100% do que fazíamos era versionado. 100% do que conhecemos também precisa ser. Agora com algum alívio, por não ter mais medo de que HD’s queimados me tirassem o sono, poderíamos partir para a identificação e melhoria da vida das pessoas.

  1. Identificação do cenário atual

Um dos processos, que considero dos mais importantes da empresa é a ingestão de conteúdo, por ele recebíamos milhares de XML’s e mídias(WAV, AVI) todos os dias, são diversos formatos e padrões de integração, o que nos demandava uma quantidade razoável de storage, alguns petabytes. Esse processo era composto por diversas micro-aplicações, onde cada uma era um fork de um projeto base, e nela tínhamos um desenvolvedor gerindo esse código, uma loucura para uma pessoa só. Óbvio que daria merda em algum momento. Fiz o trabalho de entender o cenário de negócio e compreender quais são os gaps do processo atual. Entre eles estão:

  • Alta segmentação das versões da base de código, cada uma com ligeiras mudanças para atender ligeiras diferenças entre parceiros.
  • Alta complexidade em cada uma das aplicações que possuíam formatos de XML diferentes.
  • Baixa completude no processamento do conteúdo recebido (boa parte das aplicações, ou precisava de tapas na hora de processar, ou só processava parte do XML que recebíamos)
  • Alta complexidade na gestão do processo
  • Falta de visibilidade do estágio do processo
  • Nenhuma escalabilidade
  • Paralelismo Moleculá (o moleco lá tentava paralelizar com braço esquerdo e direito)
  • Equipe tecnicamente desatualizada, que vivia uma era inteira apagando incêndios
  • Equipe reduzidíssima

Como resolver essa questão:

1) Durante as discussões sobre os formatos de XML, vimos que muitos parceiros, e o mercado em si, estavam convergindo para um padrão chamado DDEX. Esse standard serve para qualquer integração imaginável (programas, músicas, vídeos, ringtones, wallpapers etc). Apostar nesse formato foi uma escolha ousada, mas trouxe muito resultado. Como o padrão é extremamente amplo e aberto para diversas finalidades, há campos demais para dados de menos. O resultado é a possibilidade de interpretação particular de cada parceiro sobre cada uma das áreas do XML. São 8 formas diferentes de se tirar um conteúdo do ar, de forma implícita ou explícita entre outras variações bem peculiares. Mesmo com essa complexidade, unificar o modelo de entrega nesse formato nos trouxe a segurança para criar uma única aplicação só para processar esse conteúdo, então conseguimos ter uma visão unificada do processo, e ainda escalável.

2) Viabilizar a análise eficiente dos XML`s

Primeiro pensei em um database SQL para armazenar o conteúdo do XML, mas quando estávamos próximos de modelar 100 tabelas, e não estávamos sequer na metade do mapeamento, pedi que parassem a atividade para que pudesse repensar. Já haviam empenhado algum esforço nessa mesma iniciativa no passado, mas ignorei. Acreditei que com alguma supervisão mais técnica seria possível: Não. não era! Subestimei o padrão “dos infernos”. Era surreal, se modelássemos, seriam pelo menos 300 tabelas para gerir. Era necessário encontrar uma alternativa.

Após a desistência de bases SQL, pensei em usar uma base XML, mas nada me agradou: nem bancos, nem tecnologias, nada. Então pensando muito na possibilidade, optei por usar o MongoDB para realizar essa tarefa. Como MongoDB não trabalha com XML, geramos conjunto de classes com base no XSD, fazendo o parser direto de XML para .Net, que em seguida é persistido diretamente os objetos no MongoDB. O resultado foi um sucesso, extremamente eficiente, agora permitindo consultas nos dados que eram do XML, e agora são BSON direto no MongoDB. Foi uma excelente decisão, poupando muito tempo e facilitando muito o trabalho. Pelo fato das classes terem sido geradas com base no XSD, as garantias de formato estão todas presentes, então tenho o melhor dos mundos!

3) A falta de visibilidade dos pacotes (pacotes são entregas de 1 XML de metadados com suas N mídias) e a pulverização do FileSystem era problema do passado e do presente, agravante e complicómetro para o processo. Analisar diretórios de servidores de produção era tarefa diária. Entenda, eram pelo menos 16 storages, onde cada pacote poderia estar em qualquer um deles. O processo de gestão de espaço em disco era apontar o FTP para O MESMO path relativo EM OUTRO DISCO. Todos os 16 storages possuíam as mesmas estruturas base, e o endpoint do FTP hora apontava para qualquer um desses 16 storages, de acordo com a disponibilidade de discos.

Gerir esse caos, era complicado, pois o acesso a disco é muito custoso. Nesses storages onde petabytes eram armazenados em poucas pastas, era surreal fazer qualquer busca ou leitura. Não havia eficiência na leitura desses discos, portanto, eu precisava de alguma representação desse file system, mais rápida e eficiente. Algo como um file system virtual que representasse o file system físico. Pois diversas análises precisam ser feitas com base na estrutura, nos nomes do arquivos, para que pudesse tomar uma decisão sistêmica. Na prática eu precisava consolidar os pacotes fragmentados, para que no final do processo, fosse eliminada a fragmentação.

Criei uma base SQL para gerir o processo com uma visão semelhante à do FileSystem (Diretórios, arquivos, tamanho), tudo exceto o conteúdo do arquivo em si, permitindo com queries determinar exatamente o que cada disco possui. Agora não precisamos estar em contato direto com o File System, queries em um banco SQL mostravam todo a estrutura do File System. Um processo simples fazia a sincronização periodicamente. Uma característica legal é que por mais que o pessoal de infraestrutura cuidasse do FTP da mesma forma como sempre fizeram, meu trabalho poderia ser feito de forma coesa, reduzindo os impactos dessas decisões. Por outro lado, para o processamento das tarefas, havia uma escolha de prioridade entre os discos, isso permitia que eu mesmo não sofresse com as necessidades específicas de gerir storages tão grandes.

4) Criação de uma infraestrutura de arquitetura robusta que facilitasse e promovesse o refactoring contínuo.

Geração de Código

O Oragon Architecture foi fundamental para a criação desse modelo. Com ele geramos código de acesso a dados, para todas as bases necessárias, gerando código e configuração para NHibernate e FluentNHibernate. Trabalhando com MySQL e SQL Server side-by-side, com múltiplos databases, hora em contexto transacional, hora somente read-only. Agora, a ausência de um owner de cada database, não representa mais um problema. O banco muda, mas periodicamente geramos código a partir do banco, e é comum ver coisas mudando. O que nos afetar, diretamente e for incompatível, gera uma quebra no build: automaticamente, aumentando a segurança e resiliência na solução. Da mesma forma que geramos código com uma grande riqueza de detalhes, estamos aptos a regerar os bancos a qualquer momento, e identificar o que mudou estruturalmente no banco, bastando analisar nosso mapping com a ajuda de históricos do scm.

AOP

Sob o framework de AOP do Spring.Net, o Oragon Architecture implementa uma série de aspectos que torna viável trabalhar com Redis, MongoDB, MySQL, DB2 e SQL Server de forma transparente e simples. Um único template é necessário para todos os bancos SQL citados. Para o NoSQL, como é schemaless (não é mais), não havia como gerar código, mas os aspectos são úteis por definir a conectividade da mesma forma para todas as soluções. Do ponto de vista do código, nenhum OpenSession ou Connect é visível, apenas precisa-se marcar os métodos com os atributos corretos para que a infraestrutura cuide de todo o resto. Outro ganho direto é a resolução de connection leaks.

Ainda falando do Spring.Net, jobs agendados foram criados com Quartz.Net para iniciar operações de negócio agendadas, como emissão de relatórios que anteriormente eram criados manualmente periodicamente entre outras operações recorrentes agendadas. A sincronização do File System com o banco SQL que citei acima, também era agendada aqui.

Mensageria e Exception Handling

Para escalar a solução, um pipeline foi criado em cima do RabbitMQ para processar todo o fluxo de trabalho das importações, desde a gestão (monitoramento) do file system, até a ingestão propriamente dita. Nesse modelo o Oragon fornece as abstrações para que você só implemente as operações necessárias em cada passo do pipeline, sem que seu código saiba sequer que faz parte de um. Assim basta programar uma operação de negócio e plugar no pipeline, caso haja erro no processamento, lance suas exceptions e o próprio orquestrador (Oragon) se encarregará de colocar a mensagem na fila de erro, para que a mensagem não seja reprocessada infinitamente, com o mesmo status de erro. Geralmente os erros são de duas naturezas, ou o cliente errou no contrato ou o código encontrou uma situação não prevista, há um terceiro cenário, pouco comum que é uma falha em validação de negócio. Para essa temos um fluxo de tratamento diferenciado.

Em todos os casos, as exceptions são logadas, automaticamente, pelos aspectos Oragon Architecture, em filas RabbitMQ, isso com ajuda do pacote Spring.NET AMQP, posteriormente são armazenadas no Sentry sem causar gargalos na aplicação. Mais tarde, após inúmeros problemas, substituímos o Sentry por ElasticSearch.

Worker Process

Agora, estamos aptos a iniciar diversos hosts de processos, para consumir uma infinidade de filas, segmentando cada operação por parceiro se assim nos interessar. Conseguimos trabalhar com diversos processos em diversas máquinas, escalando até quanto o hardware e os databases suportarem.

A arquitetura, cheia de abstrações viabilizou tomar as decisões que envolviam estratégia de deploy o mais tarde possível. Esse é o ganho trazido por uma aplicação extremamente configurável. Mas não há almoço grátis. As configurações são tão relevantes quanto o código e são extensas. No código, não precisamos ver sequer um try-catch, e não há nada de errado nisso. Os Aspectos são responsáveis por pela gestão e tratamento das mensagens. As operações transacionais, são geridas pelos aspectos e eles são responsáveis pelo rollback em caso de erro. As configurações são muito mutáveis e maleáveis, mas são de extrema importância para o fluxo. Enfim, toda escolha, uma renúncia.

Continous Integration e Continuous Deploy

Para o processo de build e deploy, havíamos configurado o Jenkins, mas agora estou apanhando um bocado do TeamCity. mas em breve teremos novamente um processo de build e deploy automatizado nele.

Esse foi o roadmap de um ano de trabalho. É realmente muito excitante trabalhar com essas tecnologias, mas também bem relevante conseguir atender e endereçar todas essas demandas.

Hoje a maior dificuldade está no Business. Compreender e estabilizar as expectativas quanto ao business, que no caso de um processo de importação de metadados, é preencher as devidas tabelas, garantindo retro-compatibilidade com um parque de aplicações complexo e infinito.

Resumo

Bom, novamente, os frameworks utilizados foram utilizados para sanar necessidades reais.

Spring.Net para IoC, DI e AOP

Oragon Architecture para AOP (implementação)

Quartz.Net para Agendamentos

TopShelf para Serviços Windows (joguei um monte de código fonte fora, depois que achei esse framework)

T4 para os templates de geração de código

Spring AMQP para abstrações com RabbitMQ

Lembre-se das premissas:

Equipe:

Equipe reduzida.

Contexto:

GAP técnico, falta de Gerência de Configuração, e segmentação do código. Falta de padronização de recebimento de conteúdo e dificuldade de gestão de código, plataforma e manutenção.

Enfim, Arquitetura

A correlação entre as diversas peças que irão compor a solução, cada uma oferecendo uma feature de negócio.

Espero que tenha gostado!

 

[03/04/2018] – Revisão do Texto para melhorar a explicação sobre algumas necessidades.

Logs Estruturados

Redis, MongoDB, RabbitMQ, Sentry e muito mais que o mundo Linux pode oferecer para aplicações .Net

Se você não conhece nenhuma dessas soluções, vou fazer um breve apanhado sobre as capacidades de cada uma delas.

Redis ( http://redis.io/ )

Redis é um Key-Value Storage. Basicamente um tipo de NoSQL baseado em chave-valor. Nele armazenamos objetos complexos. É uma solução muito útil para cache, entre outras abordagens. Se você já usou SharedCache, Memcached, e outras soluções de cache, sabe muito bem do que estou falando. Para você que nunca usou, pense que quando a descoberta de um dado é relativamente custosa, armazenar esse dado em um cache é uma das primeiras estratégias para garantir carga em seu site ou aplicação. Com um cache server você armazena seus objetos, determina um tempo de expiração para o objeto que está armazenando, e na medida que a aplicação vai sendo utilizada, cada vez mais você deixa de executar aquele longo processamento para entregar dados já processados que estão no cache. Já vi pessoas dizendo que o esforço para ir a um cache de rede e entregar um resultado a partir dele possa parecer infundado, mas quando você recebe milhares de requisições por segundo, processar algumas queries a cada request te custam caro, muito caro.

MongoDB ( https://www.mongodb.org/ )

Não há quem não tenha lido sobre NoSQL que não tenha ouvido falar nele. MongoDB é uma das soluções NoSQL mais conhecidas e usadas pelo mercado. Mas admito considerar que nossa relação esteja por um fio. De qualquer forma, se você pode usar um document store em seu ambiente de produção, MongoDB é sempre uma solução a se pensar.

Existem diversos fatores que te fazem pensar em NoSQL, seja por flexibilidade, performance, e capacidade de lidar com volumes dados realmente grandes. MongoDB sempre terá seu espaço. Mas considere o Couchbase como alternativa, eu venho olhando para o produto com um olhar muito benevolente nos últimos meses.

RabbitMQ ( https://www.rabbitmq.com/ )

Nessa área, embora o RabbitMQ tenha se mostrado uma das melhores soluções do mercado, existem diversas alternativas. O principal motivador para usar filas, invariavelmente é pensar em modelos assíncronos e escaláveis de processamento. A adoção do RabbitMQ se dá pela implementação do standard AMQP, esse sim merece muito sua atenção. Se você ainda usa MSMQ, principalmente! MSMQ está fadado ao fracasso e a tendência é que a Microsoft ofereça um serviço AMQP fora do Azure. Acredito que o Service Bus for Windows Server já esteja compatível com AMQP, mas a adoção de AMQP no Azure é o principal sinal de que todo o mercado, inclusive a Microsoft, estão convergindo para padrões universais, inclusive no quesito message broker.

Sentry ( https://getsentry.com/ )

O Sentry me foi apresentado por um rapaz que trabalha comigo, e admito nunca ter sequer chegado perto dessa solução antes disso. Já conhecia o LogEntries e ao conhecê-lo adito ter me frustrado muito, principalmente por estar planejando um produto exatamente assim. Fato que criei o LogEngine, que por sua vez tinha uma performance muito boa se comparado a estes, mas me faltou braço para criar a interface Web. O Sentry vem como uma solução centralizada para armazenamento e visualização de eventos de aplicação, servindo para que você tenha um console completo com tudo o que acontece com sua aplicação.

E o Linux?

O que todas essas soluções trazem de interessante é que foram concebidas para performar melhor em ambiente linux, mas nem por isso, soluções baseadas em tecnologia Microsoft não podem se valer do grande benefício que esses serviços nos prestam. Ter um parque heterogêneo não é fácil, mas podemos usar o melhor que cada mundo tem a nos oferecer!

Enfim sem bairrismo, use aquilo que te ajuda a te entregar mais, com menos!

E você, está usando outros serviços hospedados em servidores linux que podem ajudar aplicações .net? Quais?