Cap'n Proto 1.0
(capnproto.org)- O modelo opt-in de cancelamento de RPC mudou de uma chamada a
context.allowCancellation()para a annotationallowCancellationno schema - Usuários que não usavam
context.allowCancellation()não precisam fazer alterações ao atualizar para a 1.0, e o cancelamento continua desabilitado por padrão - A versão 1.0 é um release com caráter de suporte de longo prazo (LTS), com planos de fazer backport de correções de bugs para o branch 1.0 por um longo período
- Melhorias de desempenho no Cap’n Proto RPC reduzem alocações de memória na implementação de RPC e no framework de I/O KJ, omitem algumas mensagens do protocolo RPC e reduzem syscalls por meio de melhorias no buffering de mensagens pequenas
- O KJ passa a usar
kqueue()para I/O assíncrono no MacOS e em sistemas da família BSD, substituindo a abordagem anterior, mais lenta, baseada empoll() - As implementações de client e server HTTP do KJ passam a suportar o método
CONNECT - Foi adicionado
capnp::RevocableServer, oferecendo uma estrutura que ajuda a evitar bugs de use-after-free ao exportar como wrapper RPC objetos cujo lifetime não é controlado pelo wrapper - O desenvolvimento da 2.0 seguirá no novo branch
v2, enquanto o branchmasterdeve se tornar o branch 1.0 LTS para evitar que projetos existentes sejam interrompidos por mudanças - O plano para a 2.0 não envolve mudanças incompatíveis no formato de serialização nem no protocolo RPC, mas sim mudanças centradas na API C++ e no KJ; aplicações escritas em outras linguagens não serão afetadas
- Os planos para a 2.0 incluem exigir um compilador C++20 ou C++23, usar coroutines de C++20, encerrar o suporte a exception disabled e ao modo no-RTTI, além de mudanças nas APIs
kj::Maybeekj::AsyncOutputStream
3 comentários
Oh.. isso finalmente chegou
Opiniões no Hacker News
Tenho uma notícia muito triste para compartilhar com as comunidades do Cap'n Proto e do Sandstorm. Ian Denhardt (zenhack no HN), que foi um colaborador central da implementação em Go, faleceu repentinamente algumas semanas atrás. Nos últimos 3 anos, colaboramos muito e nos tornamos amigos, então sinto uma grande perda.
Na prática, como líder do projeto, agora preciso preencher o enorme vazio deixado por Ian; portanto, se você puder contribuir com o projeto, a ajuda é extremamente necessária. Se você é colaborador ou mantenedor de outras implementações, como C++ ou Rust, eu gostaria muito de entrar em contato. Para dar continuidade ao trabalho de Ian, precisamos contar com pessoas muito inteligentes.
RIP Ian, aprendi muitíssimo trabalhando com você. Contato: https://github.com/lthibault, https://matrix.to/#/#go-capnp:matrix.org, Telegram @lthibault, gmail louist87
Não é que eu ignore o futuro, mas passei a tentar aproveitar mais as pequenas coisas da vida. Ian deixou um legado incrível que tocou muitas pessoas. RIP Ian
Além do Cap'n Proto, é surpreendente que haja tão poucos protocolos com promise pipelining. O único outro exemplo que me vem à cabeça é o 9p, mas o 9p não é um protocolo de uso geral
https://capnproto.org/news/2013-12-13-promise-pipelining-cap...
Ainda assim, é um recurso que poderia ser mais amplamente usado
Por exemplo, se o cliente Joe chama o Service A e passa o resultado prometido para o Service B, o Service B precisa fazer uma chamada remota ao Service A para obter o resultado dessa promise
Nesse ponto, a fronteira de segurança fica imediatamente complicada. O modelo de delegação já é um problema, mas o mais sério é que a contrapressão desaparece. Se o cliente Joe criar milhares de chamadas ao Service A e passar ao Service B resultados que ainda não foram materializados, o Service A pode ser bombardeado como em um DDoS, fazendo o Service B estourar o tempo limite
Mas promise pipelining também exige suporte do cliente. Fico curioso se existe algum cliente 9p que, quando o usuário emite vários walks, envie todos sem esperar a resposta do primeiro walk
Além disso, o 9p oferece promise pipelining apenas para descritores de arquivo. Só isso já permite fazer muita coisa, mas, se você quiser ler todos os arquivos de um diretório, teria de emitir um read e então fazer walk sobre o resultado desse read; o 9p não parece oferecer suporte a isso. Na minha biblioteca de protocolo de chamadas de sistema remotas, rsyscall, isso de fato é suportado
Pelo que lembro, Mark Miller mais tarde também escreveu a proposta de promises para JavaScript. A extensão de RPC, porém, não se concretizou; em vez disso, recebemos async/await, que não parece combinar muito bem com pipelining
Spritely Goblins e OCapN, tentativas mais recentes de criar sistemas distribuídos de capacidades ao estilo de E, também buscam pipelining; portanto, se você estiver na cap-talk, provavelmente ouvirá falar de mais alguns protocolos semelhantes, ainda que com poucos casos de uso reais
Ainda assim, embora seja um recurso bacana, parece que promise pipelining exige programar com promises explícitas de verdade, e já se sabe bem como isso pode ficar complicado
Do outro lado, isto é, no lado de loop de eventos e dos “objetos ativos” com concorrência cooperativa, uma ideia interessante e menos conhecida são os causality IDs do DCOM/COM+. Eles são um meio de controlar reentrância; veja a documentação da Microsoft sobre CoGetCurrentLogicalThreadId e a discussão de CALLTYPE_TOPLEVEL_CALLPENDING em Effective COM. Depois, acho que tentaram vendê-los como um recurso novo no ASTA do Win8/UWP
[1] http://erights.org/elib/distrib/captp/index.html
[2] http://erights.org/talks/thesis/index.html
[3] https://spritely.institute/goblins/
[4] https://github.com/ocapn/ocapn
[5] https://groups.google.com/g/captalk/
[6] https://learn.microsoft.com/openspecs/windows_protocols/ms-d...
[7] https://learn.microsoft.com/windows/win32/api/combaseapi/nf-...
[8] https://archive.org/details/effectivecom50wa00boxd/page/150
[9] https://devblogs.microsoft.com/oldnewthing/20210224-00/?p=10...
O segundo caso parece possível de algum jeito, mas o primeiro depende de coisas como a quantidade de valores na lista de resultados
Parabéns pelo lançamento. Depois de 10 anos, deve ser realmente empolgante. Fico curioso para saber se haverá mais trabalho daqui para frente também nas implementações em outras linguagens
Gosto muito das ideias do formato, mas as principais linguagens da nossa stack não têm suporte suficiente para eu querer usá-lo em produto
Infelizmente, é difícil prometer algo novo aqui. O trabalho no Cap'n Proto é movido pelas necessidades do meu projeto principal, o runtime do Cloudflare Workers, que é majoritariamente em C++. Ele também interage com serviços em Go e Rust, e essas implementações parecem cumprir seu papel nesses casos
Em outras palavras, o Cap'n Proto é um projeto open source, e espero que seja útil para as pessoas, mas não é um produto que eu esteja tentando vender, então não tenho um foco especial em fazer todo mundo adotá-lo. Como sempre, contribuições são bem-vindas
Um caso em que poderia haver uma grande mudança seria se a Cloudflare decidisse tornar o Cap'n Proto um recurso público da plataforma Workers. Aí surgiria uma necessidade direta de realmente polir o suporte em várias linguagens. Às vezes discutimos isso, mas não há planos no momento
Se quiser tentar, pode tentar. As pessoas provavelmente seriam bastante colaborativas. Só é bom saber que simplesmente há muito trabalho envolvido
Para acrescentar contexto, Kenton operou por anos o sistema proto interno do Google e, depois de sair, criou sua própria versão open source
Se houver alguém da Cloudflare que tenha participado da escolha do Cap'n Proto em vez de outras opções como protobuf, fico curioso sobre quais foram as considerações. Quero saber se esses motivos também são importantes para mim, ou se é algo com que não preciso me preocupar a menos que esteja lidando com uma escala enorme
Ainda hoje, o pipeline de dados da Cloudflare, ou seja, o sistema que gera logs e analytics na borda, é em grande parte baseado em serialização Cap'n Proto. Eu pessoalmente não estive muito envolvido nesse projeto
No caso do Cloudflare Workers, eu iniciei o projeto, então usei o que era meu. Provavelmente não é a explicação que você queria
Ainda assim, acho que o protocolo de RPC do Cap'n Proto é muito mais expressivo do que as alternativas, o que ajudou bastante na implementação do sandboxing do Workers Runtime e de funcionalidades de sistemas distribuídos como Durable Objects. https://blog.cloudflare.com/introducing-workers-durable-obje...
Se você escolhe dados em um formato mais próximo de registros, não orientado a colunas como Parquet, e quer um formato com tempo de parsing inicial praticamente zero, as opções ficam bem reduzidas. Se você altera valores de campos ao ler, pode haver marshaling
https://capnproto.org/news/2014-06-17-capnproto-flatbuffers-... aborda esse trade-off
O Cap'n Proto foi originalmente criado para https://sandstorm.io/, e o trabalho que Kenton fez depois de ser contratado pela Cloudflare provavelmente acabou levando ao Cloudflare Workers
Outra consideração: https://github.com/google/flatbuffers/issues/2#issuecomment-...
Pelo que entendi, Albert Strasheim, o engenheiro principal que construiu tudo isso, fez benchmarks das opções de serialização mais prováveis e obteve resultados mostrando que o Cap'n Proto era bem mais rápido que protobuf
Havia uma ótima implementação em C++, e ela podia ser usada no nginx. Se bem me lembro, um pouco de Lua também estava envolvido. A implementação em Go usada no lado consumidor tinha algumas arestas, mas as partes essenciais de que precisavam puderam ser corrigidas pelas pessoas
A Cloudflare sempre deu importância à eficiência de custo das máquinas, então, considerando os requisitos de desempenho da época, foi uma escolha natural. Durante o período em que estive na equipe de dados, o Cap'n Proto foi bastante fácil de trabalhar, e o compartilhamento das definições de proto a partir de um repositório central de schemas também funcionava bem
Estou animado com a participação do Cap'n Proto no trabalho de padronização do OCapN. Fico curioso se isso fará parte do trabalho no Cap'n Proto 2.0
https://github.com/ocapn/ocapn
Gosto de OCapN, mas, com tantas coisas que estou tocando agora, não sei quanto tempo poderei dedicar pessoalmente. Gostaria de poder trazer notícias melhores, mas é uma perda trágica para todos nós
Nunca usei Cap'n Proto, mas agradeço pela resposta de FAQ extremamente útil do kentonv [1], que explica por que campos obrigatórios são um problema em protocolos
Costumo linká-la sempre que alguém pergunta por que o protobuf 3 não tem campos
required[1] https://capnproto.org/faq.html#how-do-i-make-a-field-require...
Portanto, o mantra amplamente repetido de “tornar estados ilegais impossíveis de representar” não se aplica aqui
https://github.com/stepchowfun/typical#asymmetric-fields-can...
Para adicionar e remover campos obrigatórios com segurança, Typical oferece o asymmetric, um estado intermediário entre campos opcionais e obrigatórios. Um campo asymmetric de uma struct é considerado obrigatório para quem escreve e opcional para quem lê. Ao contrário de campos opcionais, campos asymmetric podem ser promovidos com segurança a obrigatórios, ou revertidos no sentido contrário
Eu gostaria que houvesse um parâmetro para ativar e desativar a validação de mensagens obrigatórias, em vez de depender do código da aplicação. Dentro de uma aplicação ou em um barramento de mensagens, ela poderia ficar desativada, mas em geral, quando ativada, ajudaria muito os desenvolvedores
[1] https://capnproto.org/faq.html#how-do-i-make-a-field-require...
Uma coisa que eu realmente gosto nas interfaces de Go é que posso tipar remotamente apenas o que uso e pular o resto. Não é divertido um serviço cair por causa de uma mudança de contrato que ele nem usa, e isso também é difícil de encontrar
Excelente conquista. Falando francamente, eu não recomendaria Cap'n Proto. A API em C++ é muito estranha
O benefício do parsing sem cópia também é menor do que eu esperava. Como a usabilidade é esquisita, é pouco provável que você queira manter os dados nas estruturas do Cap'n Proto, e em 99% dos casos acabará copiando para suas próprias estruturas
O mundo, nesse tipo de implementação binária, em geral se consolidou em torno de Protobuf, então o atrito com outros sistemas também é maior
Eu só usei para serialização. A parte de RPC talvez seja mais atraente
Eu realmente gostaria que Thrift tivesse vencido em vez de Protobuf/gRPC. O design era muito melhor e mais flexível do que qualquer coisa que vi antes ou depois. Parece ter desaparecido principalmente por causa da documentação péssima, e talvez também por não ter um grande nome por trás
Sempre quis adicionar ao Cap'n Proto uma API alternativa opcional que usasse “structs C comuns”, ou algo próximo disso, para casos de uso em que zero-copy não importa muito, oferecendo serialização com uma cópia como no protobuf, mas ainda não fiz isso
Pessoalmente, porém, sempre me interessei muito mais pelo protocolo RPC do que pela serialização. Acho que o protocolo RPC é uma verdadeira mudança de paradigma em quase todos os casos de uso não triviais
De qualquer forma, os principais problemas da serialização binária são gerenciamento de versões de schemas e mensagens, e codificação de deltas. Se você ignora esses dois, serialização binária plana é trivial
Não existe biblioteca que ofereça uma solução que trate bem as duas coisas
Sempre gostei das ideias do capnp, mas me incomoda o fato de que, no fim, sendo um protocolo de codificação de mensagens, ele tenha opiniões até sobre como devo estruturar minha arquitetura de servidor.
O gRPC certamente também tem esse problema, mas é separado de forma muito clara do protobuf, e o protobuf até tem funcionalidades relacionadas ao gRPC.
Por causa desse acoplamento, toda vez que comparo com capnp acabo tendendo para flatbuffers ou protobuf. Especialmente porque as implementações de fb e pb são muito mais simples, e eu valorizo muito a simplicidade do ponto de vista de segurança e manutenção.
Também vejo a falta de boas implementações de terceiros em outras linguagens como um sinal direto de que essa avaliação faz sentido. O bus factor e a sobrevivência no longo prazo também são muito ruins. A simplicidade domina.
A versão em Rust também exige seu próprio modo de vida prescritivo. Muitas dessas coisas fazem sentido numa visão de mundo zero-copy, mas ainda assim dificultavam várias formas de expressão, e a documentação também era difícil de ler.
Então, só por esse motivo, normalmente eu escolheria flatbuffers.
Ainda assim, espero que algum dia surja uma situação em que cap'n'proto seja necessário e útil, e quero terminar pelo menos um dos vários projetos de hobby que deixei em fork ao longo dos anos para tentar usá-lo. É verdade que há engenharia de alta qualidade ali.
Não é algo novo, mas fico me perguntando se o nome é uma homenagem deliberada a Star Trek Voyager, ou se há alguma outra referência que eu não conheço.
https://memory-alpha.fandom.com/wiki/Captain_Proton
O nome Cap'n Proto originalmente significava Capabilities and Protobufs. Era um protocolo de RPC baseado em capabilities, construído sobre Protocol Buffers. Mas logo no começo deu vontade de experimentar um formato de serialização completamente diferente. Mesmo assim, “Proto” ainda fazia sentido como protocolo, então mantive o nome.
O trocadilho “cerealization protocol” na verdade foi outra pessoa que me contou, mas eu o adicionei ao logo imediatamente.
Parece ser Serialization/RPC, então acho que é um erro de digitação.