7 pontos por GN⁺ 2023-07-29 | 3 comentários | Compartilhar no WhatsApp
  • O modelo opt-in de cancelamento de RPC mudou de uma chamada a context.allowCancellation() para a annotation allowCancellation no 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 em poll()
  • 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 branch master deve 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::Maybe e kj::AsyncOutputStream

3 comentários

 
alstjr7375 2023-07-29

Oh.. isso finalmente chegou

 
GN⁺ 2023-07-29
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

    • Já passei algumas vezes pela perda repentina de pessoas, e é algo que realmente destroça a alma. Sempre que isso acontece, me faz lembrar como a vida é frágil e como tudo pode mudar tão rapidamente.
      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
    • Fiquei chocado, não sabia dessa notícia. Eu gostava muito do blog do Ian; é uma pena enorme.
    • Parece que @zenhack também mantinha os bindings de Haskell.
    • Eu não o conhecia pessoalmente, mas admirava muito o trabalho dele no Tempest, um fork recente que tentava reviver o Sandstorm. Obrigado por nos contar essa notícia.
    • Que notícia realmente triste. Ian inspirou muita gente, e será uma grande perda para toda a comunidade.
  • 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...

    • É um recurso bacana, mas parece que seria difícil otimizar o backend em comparação com uma abordagem de agrupar consultas explicitamente. Por exemplo, em vez de várias chamadas RPC separadas em pipeline, talvez seja melhor uma chamada RPC customizada que se resolva em uma única consulta SQL
      Ainda assim, é um recurso que poderia ser mais amplamente usado
    • Pipelining é uma má ideia. Como materializa instâncias de objetos, torna muito mais difícil fazer uma implementação robusta. Já não são chamadas sem estado; passa a ser uma estrutura em que se executam funções sobre instâncias específicas de objetos
      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
    • Eu não sabia que o 9p tinha promise pipelining. Mais precisamente, como o cliente pode escolher o descritor de arquivo, ele pode enviar uma solicitação para ler um arquivo assim que o abre; se a abertura falhar, a leitura também falha com EBADF. É uma estrutura elegante
      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
    • Como ancestral espiritual do Cap'n Proto, há o CapTP/VatTP, também conhecido como Pluribus, desenvolvido junto com a linguagem E de Mark Miller. Também há um dispositivo chamado “unum”, em um jogo de palavras. Para uma genealogia mais profunda, veja a tese dele, que também aborda o promise pipelining de Barbara Liskov e ideias relacionadas do Argus, uma extensão do CLU
      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...
    • Não sei exatamente como funciona o promise pipelining do Cap'n Proto, mas me preocupam casos como ler um diretório e dar stat em todos os itens dentro dele, ou querer passar apenas um de dois valores de resposta para a próxima chamada
      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

    • Esse é, na prática, o maior ponto fraco do Cap'n Proto. A única implementação que eu mantenho de fato é a em C++, e as outras vêm de vários contribuidores, então o nível de completude e qualidade varia
      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
    • Houve pessoas que tentaram escrever diretamente a camada de RPC, em vez de um wrapper em torno da implementação em C++, mas há muitíssimo código a reescrever em comparação com o ganho direto
      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

    • Provavelmente foi ele quem tornou o protobuf 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

    • Há um post no blog sobre o uso do Cap'n Proto pela Cloudflare em 2014. Foi 3 anos antes de eu entrar: https://blog.cloudflare.com/introducing-lua-capnproto-better...
      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...
    • Não trabalho na Cloudflare, mas acompanho o trabalho deles e, às vezes, lido com projetos sensíveis a desempenho. Eu chutaria que eles consideraram Cap'n Proto, flatbuffers, SBE etc. como uma categoria separada de outros formatos de dados como Avro ou protobuf
      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-...
    • Resumindo a situação pouco mais de um ano depois de eu ter entrado, a Cloudflare estava criando uma forma de enviar logs da borda para um ponto central, para analytics de clientes e fornecimento de logs a clientes enterprise
      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
    • É bem possível que a escolha tenha sido fácil porque o desenvolvedor líder do Cloudflare Workers é o criador do Cap'n Proto
  • 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

    • Infelizmente, Ian “zenhack” Denhardt, que liderava essa participação, faleceu recentemente de forma repentina
      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...

    • É uma perspectiva realmente valiosa. Eu também tive dificuldade para entender o porquê no passado. O ponto central foi entender protobuf e Cap'n Proto como formatos de serialização que atravessam fronteiras de API e precisam funcionar com versões diferentes de schema e compatibilidade retroativa e futura, não como estruturas de dados em memória que representam o mundo visto por um único processo em uma única versão
      Portanto, o mantra amplamente repetido de “tornar estados ilegais impossíveis de representar” não se aplica aqui
    • Typical oferece campos assimétricos para ajudar na evolução de tipos:
      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
    • Se levarmos essa lógica adiante, acho que também dá para chegar à conclusão de eliminar praticamente todos os tipos estáticos. Sinceramente, não é algo totalmente errado, e também é por isso que Alan Kay definiu orientação a objetos com tipagem dinâmica e late binding. Só que estamos indo no sentido oposto e reaprendendo a mesma lição repetidamente
    • A FAQ [1] diz que “a resposta correta é que a aplicação valide isso em código no nível da aplicação, conforme necessá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...
    • Há mais uma armadilha na mesma linha: o problema de desserializar até coisas de que você não precisa, como acontece quando se recebe de um cliente gerado
      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

    • Concordo que a API necessária para zero-copy fica meio esquisita, especialmente no lado da escrita. No lado da leitura, não é muito diferente. Por outro lado, zero-copy só chega perto de uma mudança de paradigma em cenários específicos, como quando usado com mmap(); em comunicação de rede, não muda muito, a menos que você use técnicas fortes como RDMA
      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
    • Acho que você quer dizer flatbuffers, não protobuf. Ele praticamente se tornou padrão, e alguns outros lugares usam SBE
      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
    • Se você não precisa de schema, acho que MessagePack é muito bom. Só que a serialização JSON do V8 é anormalmente rápida, então até o MessagePack tem dificuldade para vencê-la; em outras linguagens ele costuma ser mais rápido e reduz a quantidade de bytes
    • fbthrift ainda está bem vivo
  • 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.

    • O problema que tive toda vez que abordei cap'n'proto não foi só ele ter opiniões sobre a arquitetura de servidor, mas também, em C++, trazer junto o KJ, uma alternativa muito opinativa à STL. Quando mexi nisso alguns anos atrás, era difícil trabalhar porque ele se infiltrava por várias partes da base de código existente.
      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.
    • Fico curioso sobre como a camada de serialização influencia a escolha de RPC.
  • 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

    • Na verdade, nunca tinha pensado nessa referência.
      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.
    • Como eles promoviam como “cerealization protocol”, sempre achei que fosse uma referência ao cereal Cap'n Crunch.
 
bbulbum 2023-07-30

Parece ser Serialization/RPC, então acho que é um erro de digitação.