7 puntos por GN⁺ 2023-07-29 | 3 comentarios | Compartir por WhatsApp
  • La modalidad opt-in de cancelación de RPC cambió de la llamada context.allowCancellation() a la anotación allowCancellation en el schema
  • Los usuarios que no usaban context.allowCancellation() no necesitan cambios al actualizar a 1.0, y la cancelación seguirá deshabilitada por defecto
  • La versión 1.0 es un lanzamiento de tipo soporte a largo plazo (LTS), y se planea hacer backport de correcciones de errores a la rama 1.0 durante mucho tiempo
  • Se aplicaron mejoras de rendimiento en Cap’n Proto RPC, incluyendo menor asignación de memoria en la implementación de RPC y en el framework KJ I/O, omisión de algunos mensajes del protocolo RPC y reducción de syscalls mediante mejoras en el buffering de mensajes pequeños
  • KJ usa kqueue() para el manejo de I/O asíncrono en MacOS y sistemas de la familia BSD, reemplazando el método anterior basado en poll(), que era más lento
  • Las implementaciones de cliente y servidor HTTP de KJ ahora soportan el método CONNECT
  • Se agregó capnp::RevocableServer, que ofrece una estructura para ayudar a evitar errores de use-after-free al exportar como wrapper RPC objetos cuyo wrapper no controla el lifetime
  • El desarrollo de 2.0 avanzará en la nueva rama v2, y la rama master pasará a ser la rama LTS de 1.0 para evitar que los proyectos existentes se rompan por cambios
  • El plan para 2.0 no incluye cambios incompatibles hacia atrás en el formato de serialización ni en el protocolo RPC, sino cambios centrados en la API de C++ y KJ, por lo que las aplicaciones escritas en otros lenguajes no se verán afectadas
  • El plan de 2.0 incluye requerir compiladores C++20 o C++23, usar coroutines de C++20, dejar de soportar modo sin excepciones y modo sin RTTI, y cambios en las APIs de kj::Maybe y kj::AsyncOutputStream

3 comentarios

 
alstjr7375 2023-07-29

Oh... esto por fin

 
GN⁺ 2023-07-29
Opiniones de Hacker News
  • Comparto una noticia muy triste para las comunidades de Cap'n Proto y Sandstorm. Ian Denhardt (zenhack en HN), quien fue un colaborador clave de la implementación en Go, falleció repentinamente hace unas semanas; en los últimos 3 años colaboré mucho con él y nos hicimos amigos, así que siento una gran pérdida.
    En la práctica, como líder del proyecto, ahora tengo que llenar el enorme vacío que dejó Ian, así que si puedes contribuir al proyecto, la ayuda es muy necesaria. Si eres colaborador o maintainer de otras implementaciones, como C++, Rust, etc., me encantaría que te pusieras en contacto. Para continuar el trabajo de Ian, necesitamos trabajar con personas muy inteligentes.
    RIP Ian, aprendí muchísimo trabajando contigo. Contacto: https://github.com/lthibault, https://matrix.to/#/#go-capnp:matrix.org, Telegram @lthibault, gmail louist87

    • Me ha tocado vivir varias veces la partida repentina de personas, y de verdad te rompe el alma. Cada vez que pasa algo así, me recuerda lo frágil que es la vida y lo rápido que puede cambiar todo.
      No es que le dé la espalda al futuro, pero me ha llevado a intentar disfrutar más las pequeñas cosas de la vida. Ian dejó un legado increíble que llegó a muchísima gente. RIP Ian.
    • Me impacta enterarme, no lo sabía. Me encantaba el blog de Ian; qué tristeza.
    • Parece que @zenhack también mantenía los bindings de Haskell.
    • No lo conocí personalmente, pero admiraba mucho su trabajo en Tempest, un fork reciente que intentaba revivir Sandstorm. Gracias por compartir esta noticia.
    • Es una noticia realmente triste. Ian inspiró a muchos y será una gran pérdida para toda la comunidad.
  • Sorprende que haya tan pocos protocolos además de Cap'n Proto que tengan promise pipelining. El único otro ejemplo que se me ocurre es 9p, pero 9p no es un protocolo de propósito general
    https://capnproto.org/news/2013-12-13-promise-pipelining-cap...

    • Es una función genial, pero parece más difícil optimizar el backend que con un enfoque de agrupar consultas explícitamente. Por ejemplo, en vez de varias llamadas RPC separadas en pipeline, podría ser mejor una llamada RPC personalizada que termine en una sola consulta SQL
      Aun así, parece una función que debería usarse más ampliamente
    • El pipelining es una mala idea. Al materializar instancias de objetos, hace mucho más difícil una implementación robusta. Ya no son llamadas sin estado, sino una estructura donde se ejecuta una función sobre una instancia de objeto específica
      Por ejemplo, si el cliente Joe llama al Service A y pasa el resultado prometido a Service B, Service B tiene que hacer una llamada remota a Service A para obtener el resultado de esa promesa
      En ese momento, los límites de seguridad se complican de inmediato. También está el problema de qué hacer con el modelo de delegación, pero lo más grave es que desaparece la contrapresión. Si el cliente Joe crea miles de llamadas a Service A y pasa a Service B resultados que todavía no se han materializado, Service A puede verse inundado como en un DDoS y Service B puede agotar el tiempo de espera
    • No sabía que 9p tuviera promise pipelining. Más precisamente, como el cliente puede elegir el descriptor de archivo, puede enviar una solicitud para leer un archivo apenas lo abre, y si la apertura falla, la lectura también falla con EBADF; es una estructura genial
      Sin embargo, el promise pipelining también requiere soporte del cliente. Me pregunto si existe algún cliente 9p que, cuando el usuario emite varios walk, los envíe todos sin esperar la respuesta del primer walk
      Además, 9p solo ofrece promise pipelining para descriptores de archivo. Con eso ya se puede hacer mucho, pero si quieres leer todos los archivos de un directorio, tendrías que poder emitir un read y luego hacer walk sobre el resultado de ese read, y parece que 9p no lo soporta. En rsyscall, mi biblioteca de protocolo para llamadas remotas al sistema, de hecho sí lo soporto
    • Como antepasado espiritual de Cap'n Proto está CapTP/VatTP, también conocido como Pluribus, desarrollado junto con el lenguaje E de Mark Miller. Como juego de nombres, también hay un artefacto llamado “unum”. Para una genealogía más profunda se puede ver su tesis, que también incluye ideas relacionadas de Barbara Liskov sobre promise pipelining y de Argus, una extensión de CLU
      Recuerdo que Mark Miller escribió más tarde la propuesta de promises para JavaScript. Sin embargo, la extensión RPC no se concretó y, en su lugar, obtuvimos async/await, que no parece encajar tan bien con el pipelining
      Spritely Goblins y OCapN, intentos más recientes de crear un sistema de capacidades distribuidas al estilo de E, también buscan el pipelining, así que si estás en cap-talk probablemente puedas enterarte de algunos protocolos similares más, aunque haya pocos casos de uso reales
      Dicho eso, aunque es una función genial, el promise pipelining parece requerir programar con promises explícitas reales, y ya se sabe bien lo complicado que eso puede llegar a ser
      En el lado opuesto, es decir, el de los “objetos activos” con bucles de eventos y concurrencia cooperativa, una idea interesante y menos conocida son los causality IDs de DCOM/COM+. Son un medio para controlar la reentrancia; se pueden ver CoGetCurrentLogicalThreadId en la documentación de Microsoft y la discusión de CALLTYPE_TOPLEVEL_CALLPENDING en Effective COM. Más tarde parece que intentaron venderlo como una función nueva en ASTA de 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...
    • No sé exactamente cómo funciona el promise pipelining de Cap'n Proto, pero me preocupa qué pasa cuando quieres leer un directorio y hacer stat de todos los elementos dentro, o cuando quieres pasar solo uno de dos valores de respuesta a la siguiente llamada
      Lo segundo parece posible de alguna manera, pero lo primero depende de cosas como la cantidad de valores en la lista de resultados
  • Felicitaciones por el lanzamiento. Después de 10 años, de verdad debe ser muy emocionante. Me pregunto si habrá más trabajo en implementaciones para otros lenguajes
    La idea del formato me gusta mucho, pero los lenguajes principales de nuestro stack no tienen un soporte que me dé confianza para usarlo en producto

    • En la práctica, esta es la mayor debilidad de Cap'n Proto. Lo único que mantengo bien es la implementación en C++, y las demás implementaciones vienen de varios contribuidores, así que el nivel de completitud y calidad varía
      Lamentablemente, es difícil prometer algo nuevo al respecto. El trabajo en Cap'n Proto está impulsado por las necesidades de mi proyecto principal, el runtime de Cloudflare Workers, y eso es principalmente C++. También interactúa con servicios en Go y Rust, y esas implementaciones parecen cumplir su función ahí
      Dicho de otra forma, Cap'n Proto es un proyecto open source y espero que sea útil para la gente, pero no es un producto que esté vendiendo, así que no me enfoco especialmente en lograr que todos lo adopten. Como siempre, las contribuciones son bienvenidas
      Un caso en el que podría haber un gran cambio sería si Cloudflare decidiera convertir Cap'n Proto en una funcionalidad pública de la plataforma Workers. Entonces habría una necesidad directa de pulirlo mucho en varios lenguajes. A veces lo conversamos, pero por ahora no hay planes
    • Hubo gente que quiso escribir directamente la capa RPC, en vez de un wrapper alrededor de la implementación en C++, pero es muchísimo código para reescribir en comparación con el beneficio directo
      Si quieres intentarlo, adelante. Es bastante probable que la gente colabore. Solo hay que tener claro que simplemente es muchísimo trabajo
  • Para agregar contexto, Kenton operó durante años el sistema proto interno de Google y, después de irse, creó su propia versión open source

    • Probablemente él fue quien publicó protobuf como open source
  • Si hay alguien de Cloudflare que haya participado en la elección de Cap'n Proto por sobre otras opciones como protobuf, me gustaría saber qué consideraciones hubo. Quisiera entender si esas razones también son importantes para mí, o si no hace falta preocuparse a menos que uno maneje una escala enorme

    • Hay un post del blog sobre el uso de Cap'n Proto en Cloudflare en 2014. Fue 3 años antes de que yo entrara: https://blog.cloudflare.com/introducing-lua-capnproto-better...
      Incluso hoy, el pipeline de datos de Cloudflare, es decir, el sistema que genera logs y analítica en el edge, se basa en gran medida en la serialización de Cap'n Proto. Yo personalmente no participé mucho en ese proyecto
      En el caso de Cloudflare Workers, como yo inicié el proyecto, usé lo mío. Probablemente no sea la explicación que querías
      Aun así, creo que el protocolo RPC de Cap'n Proto es mucho más expresivo que las alternativas, y eso ayudó mucho a implementar el sandboxing del Workers Runtime y funcionalidades de sistemas distribuidos como Durable Objects. https://blog.cloudflare.com/introducing-workers-durable-obje...
    • No trabajo en Cloudflare, pero sigo lo que hacen y a veces trabajo en proyectos sensibles al rendimiento. Si tuviera que especular, Cap'n Proto, flatbuffers, SBE y similares se habrán considerado una categoría aparte de otros formatos de datos como Avro o protobuf
      Si eliges datos con una forma más cercana a registros, no orientados a columnas como Parquet, y quieres un formato con tiempo de parsing previo prácticamente cero, las opciones se reducen bastante. Si al leer cambias valores de campos, puede haber marshaling
      En https://capnproto.org/news/2014-06-17-capnproto-flatbuffers-... se tratan estos trade-offs
      Cap'n Proto se creó originalmente para https://sandstorm.io/, y el trabajo que Kenton habría hecho después de ser contratado por Cloudflare probablemente terminó llevando a Cloudflare Workers
      Otra consideración: https://github.com/google/flatbuffers/issues/2#issuecomment-...
    • Para resumir la situación poco más de un año después de que yo entrara, Cloudflare estaba construyendo una forma de enviar logs desde el edge a un punto central para ofrecer analítica a clientes y logs a clientes enterprise
      Tengo entendido que Albert Strasheim, el ingeniero clave que construyó todo eso, hizo benchmarks de las principales opciones de serialización y obtuvo como resultado que Cap'n Proto era bastante más rápido que protobuf
      Había una excelente implementación en C++ y se podía usar con nginx. Si mal no recuerdo, también había algo de Lua involucrado. La implementación en Go que se usaba del lado del consumo tenía partes ásperas, pero la gente pudo arreglar las partes centrales que necesitaban
      Cloudflare siempre le dio importancia a la eficiencia de costos de las máquinas, así que, considerando los requisitos de rendimiento de la época, fue una elección natural. Durante mi tiempo en el equipo de datos, Cap'n Proto fue bastante fácil de usar, y compartir las definiciones proto desde un repositorio central de esquemas también funcionó bien
    • Es probable que la elección haya sido fácil porque el desarrollador líder de Cloudflare Workers es el creador de Cap'n Proto
  • Me entusiasma que Cap'n Proto participe en el trabajo de estandarización de OCapN. Me pregunto si esto será parte del trabajo de Cap'n Proto 2.0
    https://github.com/ocapn/ocapn

    • Lamentablemente, Ian “zenhack” Denhardt, quien lideraba esa participación, falleció repentinamente hace poco
      Me gusta OCapN, pero con todas las cosas que tengo a cargo ahora, no sé cuánto tiempo podré dedicarle personalmente. Ojalá pudiera dar mejores noticias, pero es una pérdida trágica para todos nosotros
  • Nunca he usado Cap'n Proto, pero agradezco la muy útil respuesta de kentonv en la FAQ [1], donde explica por qué los campos obligatorios son un problema en los protocolos.
    La enlazo seguido cada vez que alguien pregunta por qué protobuf 3 no tiene campos required.
    [1] https://capnproto.org/faq.html#how-do-i-make-a-field-require...

    • Es una perspectiva realmente valiosa. A mí también antes me costaba entender por qué era así. La clave fue entender protobuf y Cap'n Proto no como estructuras de datos en memoria que representan el mundo visto por una única versión de un único proceso, sino como formatos de serialización que deben funcionar a través de límites de API, con distintas versiones de esquema y compatibilidad hacia atrás y hacia adelante.
      Por lo tanto, el mantra tan repetido de “hacer que los estados ilegales sean imposibles de representar” no aplica aquí.
    • Typical ofrece campos asimétricos para ayudar con la evolución de tipos:
      https://github.com/stepchowfun/typical#asymmetric-fields-can...
      Para agregar y eliminar campos obligatorios de forma segura, Typical ofrece asymmetric, un estado intermedio entre los campos opcionales y los obligatorios. Un campo asymmetric de una estructura se considera obligatorio para quien escribe y opcional para quien lee. A diferencia de un campo opcional, un campo asymmetric puede promoverse de forma segura a obligatorio, o revertirse en sentido contrario.
    • Si extendemos esa lógica, creo que también se podría llegar a la conclusión de eliminar prácticamente todos los tipos estáticos. Sinceramente, tampoco es una idea del todo equivocada, y por eso Alan Kay definía la orientación a objetos con tipos dinámicos y late binding. Pero nosotros vamos en la dirección opuesta y seguimos reaprendiendo la misma lección.
    • La FAQ [1] dice que “la respuesta correcta es que la aplicación valide en código de nivel de aplicación según lo necesite”.
      Me habría gustado que hubiera un parámetro para activar y desactivar la validación de mensajes obligatorios, en lugar de depender del código de la aplicación. Podría desactivarse dentro de la aplicación o en el bus de mensajes, pero en general, tenerla activada sería de gran ayuda para los desarrolladores.
      [1] https://capnproto.org/faq.html#how-do-i-make-a-field-require...
    • Hay otra trampa en la misma línea: el problema de deserializar incluso cosas que no necesitas, como ocurre cuando las recibes desde un cliente generado.
      Algo que realmente me gusta de las interfaces de Go es que puedo tipar remotamente solo lo que uso y omitir el resto. No es divertido que un servicio falle por cambios de contrato que ni siquiera usa, y además es difícil de encontrar.
  • Es un gran logro. Si soy sincero, no recomendaría Cap'n Proto. La API de C++ es muy incómoda.
    El beneficio del parsing sin copias también es menor de lo que esperaba. Como la usabilidad es incómoda, es poco probable que quieras mantener los datos dentro de las estructuras de datos de Cap'n Proto, y en el 99% de los casos terminas copiándolos a tus propias estructuras.
    El mundo, en general, se ha asentado en Protobuf para este tipo de implementaciones binarias, así que también hay más fricción con otros sistemas.
    Yo solo lo he usado para serialización. Tal vez la parte de RPC sea más atractiva.
    Me habría encantado que Thrift hubiera triunfado en lugar de Protobuf/gRPC. Tenía un diseño mucho mejor y más flexible que cualquier cosa que haya visto antes o después. Creo que desapareció principalmente por su pésima documentación, y quizá también porque no tenía un gran nombre detrás.

    • Estoy de acuerdo en que la API necesaria para no hacer copias se vuelve algo incómoda, en especial del lado de escritura. Del lado de lectura no es tan distinta. Por otro lado, sin copias se acerca a un cambio de paradigma solo en escenarios específicos, como cuando se usa con mmap(); en comunicación de red, no cambia mucho salvo que uses técnicas fuertes como RDMA.
      Siempre quise agregar a Cap'n Proto una API alternativa opcional que usara “estructuras C normales”, o algo cercano, para casos de uso donde no hacer copias no importa demasiado, y que ofreciera serialización con una copia al estilo protobuf, pero todavía no lo he hecho.
      Dicho eso, personalmente siempre me ha interesado mucho más el protocolo RPC que la serialización. Creo que el protocolo RPC sí es un cambio de paradigma real para casi cualquier caso de uso no trivial.
    • Creo que te refieres a flatbuffers, no a protobuf. En la práctica se ha convertido en el estándar, y en algunos otros lugares se usa SBE.
      En cualquier caso, los principales problemas de la serialización binaria son el versionado de esquemas y mensajes, y la codificación delta. Si ignoras esas dos cosas, la serialización binaria plana es trivial.
      No hay ninguna biblioteca que ofrezca una solución que maneje bien ambas cosas.
    • Si no necesitas un esquema, creo que MessagePack es bastante excelente. Dicho eso, la serialización JSON de V8 es anormalmente rápida, así que incluso a MessagePack le cuesta ganarle; en otros lenguajes muchas veces sí es más rápido, y además reduce la cantidad de bytes.
    • fbthrift todavía sigue muy vivo.
  • Siempre me gustó la idea de capnp, pero al final me molesta que, siendo un protocolo de codificación de mensajes, tenga opiniones incluso sobre cómo debería estructurar la arquitectura de mi servidor.
    gRPC sin duda también tiene este problema, pero está separado de protobuf de forma muy clara, aunque protobuf sí tiene algunas funcionalidades relacionadas con gRPC.
    Por ese acoplamiento, cada vez que lo comparo con capnp termino inclinándome por flatbuffers o protobuf. En especial porque las implementaciones de fb y pb son mucho más simples, y valoro mucho la simplicidad desde el punto de vista de seguridad y mantenimiento.
    La falta de buenas implementaciones de terceros en otros lenguajes también me parece una señal directa de que ese juicio es válido. El bus factor y la supervivencia a largo plazo también son muy malos. La simplicidad manda.

    • El problema que tuve cada vez que me acerqué a cap'n'proto no es solo que tenga opiniones sobre la arquitectura del servidor, sino que en C++ además trae consigo KJ, una alternativa muy opinada al STL. Cuando lo probé hace unos años, era difícil trabajar con él porque se metía en muchas partes de la base de código existente.
      La versión de Rust también exige su propio estilo de vida normativo. Mucho de eso tiene sentido dentro de una visión de mundo de cero copias, pero aun así dificultaba muchas expresiones y la documentación también era difícil de leer.
      Por eso, solo por esa razón, normalmente elegiría flatbuffers.
      Aun así, espero que algún día llegue una situación en la que cap'n'proto sea necesario y útil, y quisiera terminar al menos uno de los varios proyectos personales que he fork-eado durante años para probarlo. Es cierto que tiene ingeniería de alta calidad.
    • Me da curiosidad cómo la capa de serialización influye en la elección de RPC.
  • No es nuevo, pero me pregunto si el nombre es un homenaje intencional a Star Trek Voyager, o si hay alguna otra referencia que desconozco.
    https://memory-alpha.fandom.com/wiki/Captain_Proton

    • En realidad nunca había pensado en esa referencia.
      El nombre Cap'n Proto originalmente significaba Capabilities and Protobufs. Era un protocolo RPC basado en capacidades y construido sobre Protocol Buffers. Pero al principio me dieron ganas de probar un formato de serialización completamente distinto. Aun así, “Proto” seguía encajando como referencia a protocolo, así que mantuve el nombre.
      El juego de palabras “cerealization protocol” en realidad me lo contó otra persona, pero lo agregué al logo de inmediato.
    • Como lo promocionaban como “cerealization protocol”, siempre pensé que era una referencia al cereal Cap'n Crunch.
 
bbulbum 2023-07-30

Parece que es Serialization/RPC, así que probablemente sea un error tipográfico.