Em suma, eu concordo com o seu CTO. Você provavelmente ganhou algum desempenho às custas da escalabilidade (se esses termos forem confusos, esclareceremos abaixo). Minhas duas maiores preocupações seriam a manutenção e a falta de opções para escalar horizontalmente (supondo que você precise disso).
Proximidade dos dados: vamos dar um passo atrás. Existem algumas boas razões para inserir código em um banco de dados. Eu argumentaria que o maior deles seria a proximidade com os dados - por exemplo, se você espera que um cálculo retorne um punhado de valores, mas essas são agregações de milhões de registros, enviando milhões de registros (sob demanda) a rede a ser agregada em outro lugar é um grande desperdício e pode matar facilmente o seu sistema. Dito isso, é possível alcançar essa proximidade de dados de outras maneiras, essencialmente usando caches ou bancos de dados de análise nos quais parte da agregação é feita antecipadamente.
Desempenho do código no banco de dados:Efeitos de desempenho secundários, como "armazenamento em cache de planos de execução", são mais difíceis de argumentar. Às vezes, os planos de execução em cache podem ser uma coisa muito negativa, se o plano de execução errado foi armazenado em cache. Dependendo do seu RDBMS, você pode tirar o máximo proveito disso, mas na maioria dos casos não obterá muito mais do SQL parametrizado (esses planos geralmente também são armazenados em cache). Eu também argumentaria que a maioria das linguagens compiladas ou JIT 'normalmente apresentam desempenho melhor que seus equivalentes SQL (como T-SQL ou PL / SQL) para operações básicas e programação não relacional (manipulação de strings, loops etc.), portanto, você não não estará perdendo nada lá, se você usou algo como Java ou C # para fazer o processamento de números. A otimização refinada também é bastante difícil - no banco de dados, você muitas vezes é preso a uma árvore B genérica (índice) como sua única estrutura de dados. Para ser justo, uma análise completa, incluindo coisas como transações mais demoradas, escalação de bloqueios, etc., pode encher livros.
Manutenção: SQL é uma linguagem maravilhosa para o que foi projetado para fazer. Não tenho certeza se é um ótimo ajuste para a lógica do aplicativo. A maioria das ferramentas e práticas que tornam nossa vida suportável (TDD, refatoração etc.) é difícil de aplicar à programação de banco de dados.
Desempenho versus escalabilidade:Para esclarecer esses termos, quero dizer o seguinte: desempenho é a rapidez com que você esperaria que uma única solicitação passasse pelo seu sistema (e retorne ao usuário), assumindo, por um momento, pouca carga. Geralmente, isso é limitado por fatores como o número de camadas físicas pelas quais passa, quão otimizadas são essas camadas, etc. Escalabilidade é como o desempenho muda com o aumento do número de usuários / carga. Você pode ter desempenho médio / baixo (digamos, 5 segundos ou mais para uma solicitação), mas uma escalabilidade incrível (capaz de suportar milhões de usuários). No seu caso, você provavelmente terá um bom desempenho, mas sua escalabilidade será limitada pelo tamanho do servidor que você pode construir fisicamente. Em algum momento, você atingirá esse limite e será forçado a recorrer a coisas como sharding, que podem não ser possíveis, dependendo da natureza do aplicativo.
Otimização prematura: acho que você cometeu o erro de otimizar prematuramente. Como outros já apontaram, você realmente não tem medições mostrando como as outras abordagens funcionariam. Bem, nem sempre podemos criar protótipos em larga escala para provar ou refutar uma teoria ... Mas, em geral, eu sempre hesitaria em escolher uma abordagem que negocie a capacidade de manutenção (provavelmente a qualidade mais importante de um aplicativo) para desempenho .
EDIT: Em uma nota positiva, a escala vertical pode se estender bastante em alguns casos. Até onde eu sei, o SO funcionou em um único servidor por algum tempo. Não tenho certeza de como ele corresponde aos seus 10.000 usuários (acho que depende da natureza do que eles estão fazendo no seu sistema), mas dá uma idéia do que pode ser feito (na verdade, existem exemplos mais impressionantes, esse é apenas um popular que as pessoas podem entender facilmente).
EDIÇÃO 2: Para esclarecer e comentar algumas coisas levantadas em outros lugares:
- Re: Consistência atômica - A consistência do ACID pode muito bem ser um requisito do sistema. O exposto acima realmente não se opõe a isso, e você deve perceber que a consistência do ACID não exige que você execute toda a lógica de negócios dentro do banco de dados. Ao mover o código que não precisa estar presente no banco de dados, você o restringe a ser executado no ambiente físico do resto do banco de dados - ele está competindo pelos mesmos recursos de hardware que a parte de gerenciamento de dados real do seu banco de dados. Quanto ao dimensionamento apenas do código para outros servidores de banco de dados (mas não para os dados reais) - com certeza, isso pode ser possível , mas o que exatamente você está ganhando aqui, além dos custos adicionais de licenciamento na maioria dos casos? Mantenha coisas que não precisam estar no banco de dados, fora dele.
- Re: SQL / C # performance - uma vez que este parece ser um tópico de interesse, vamos adicionar um pouco à discussão. Certamente você pode executar código nativo / Java / C # dentro dos bancos de dados, mas, tanto quanto eu sei, não foi isso que foi discutido aqui - estamos comparando a implementação de código de aplicativo típico em algo como T-SQL versus algo como C #. Há vários problemas que foram difíceis de resolver com o código relacional no passado - por exemplo, considere o problema do "máximo de logins simultâneos", onde você tem registros indicando um logon ou logout e o horário, e precisa descobrir qual o número máximo de usuários conectados a qualquer momento era. A solução mais simples possível é percorrer os registros e continuar incrementando / diminuindo um contador à medida que você encontrar logins / logouts, além de acompanhar o máximo desse valor.pode, Eu não sei), o melhor que você pode fazer é um CURSOR (as soluções puramente relacionais estão todas em diferentes ordens de complexidade, e tentar resolvê-lo usando um loop while resulta em pior desempenho). Nesse caso, sim, a solução C # é realmente mais rápida do que você pode obter no período T-SQL. Isso pode parecer absurdo, mas esse problema pode se manifestar facilmente nos sistemas financeiros, se você estiver trabalhando com linhas que representam mudanças relativas e precisar calcular agregações em janelas. As invocações de proc armazenadas também tendem a ser mais caras - invoque um SP trivial um milhão de vezes e veja como isso se compara à chamada de uma função C #. Eu sugeri alguns outros exemplos acima - ainda não encontrei ninguém implementando uma tabela de hash adequada no T-SQL (uma que realmente oferece alguns benefícios), embora seja bastante fácil de fazer em C #. Novamente, há coisas em que os bancos de dados são impressionantes e coisas em que eles não são tão impressionantes. Assim como eu não gostaria de fazer JOINs, SOMAS e GROUP BYs em C #, não quero escrever nada particularmente intensivo em CPU no T-SQL.