Si estás en el negocio de construir cosas que funcionan en computadoras a largo plazo, creo que eventualmente adquirirás una historia de errores favorita. Esta es la mía. También he creado una herramienta interactiva donde puedes explorar los conceptos subyacentes a este error. El error: dos emojis entran, ninguno sale Estaba trabajando en la migración de un editor legado a una experiencia colaborativa con mi equipo. TipTap en la parte superior (que es un envoltorio alrededor de ProseMirror), Yjs debajo que maneja la magia del CRDT para la sincronización en tiempo real. Funcionaba bien! Mayormente. En nuestros días de alpha/early release, cuando todavía era casi interno y/o usuarios de lanzamiento temprano, a veces el editor simplemente se detenía a guardar el contenido. Silenciosamente. Seguías escribiendo y todo parecía bien, pero tus cambios no se sincronizaban con el documento Yjs. La próxima vez que abrías la página, todo lo que habías escrito después del punto de falla había desaparecido. Era terriblemente aterrador, muy raro y casi imposible de diagnosticar porque no podíamos reproducirlo. Habíamos intentado de verdad! Mis primeras sospechas generalmente se centraban en conexiones Wi-Fi inestables y comportamientos WebSocket extraños, pero ninguna cantidad de ralentización o encendido/apagado del Wi-Fi parecía reproducir el error. La experiencia era sorprendentemente resistente en esos casos, en mi memoria. Parecía que sucedía al azar, nunca cuando alguien estaba mirando. No había errores evidentes en la consola, no había trazas de pila, no había crash. Solo... "Hey, creo que mis cambios no se guardaron." Luego un día nuestro gerente de producto lo resolvió. No era una cosa fácil de encontrar. Él había experimentado más que nadie (probablemente porque era el mejor en dogfoodar nuestro producto) y había estado estrechando metódicamente. "Me siento como si estuviera loco, pero creo que es cuando escribo caracteres específicos juntos, voy hacia atrás y inserto un carácter entre ellos..." Él había utilizado 🟢 y 🔴 en sus correos semanales de estado del proyecto para comunicar la salud general. Verde para en línea, rojo para a riesgo. Cada semana el modelo que estaba utilizando tenía ambos caracteres ya presentes y él simplemente los habría eliminado (Generalmente el rojo, estoy feliz de decir!). En esta ocasión había copiado el círculo verde y pegado el rojo delante de él en algún punto, o quizás viceversa. Esa operación específica— insertando un emoji multi-byte adyacente a otro— estaba activando una división en la biblioteca CRDT subyacente, que estaba dividiendo una pareja de sustitutos a la mitad. Recuerdo estar en una llamada cuando me mostró esto a mí y a uno de mis empleados directos que se había ocupado de la transición de edición colaborativa. Debo haberme emocionado un poco demasiado—I vivo por errores exóticos:""Me siento como si estuviera emocionado por esto," dijo. No estaba equivocado. Agregando diversión, no todos los emojis activaban el problema. Solo los que estaban sobre U+FFFF que requerían parejas de sustitutos. Y no todas las modificaciones resultaban en el problema ni—solo las que causaban una división a un byte offset exactamente incorrecto. Era un error difícil de depurar antes de que supiéramos qué estaba sucediendo. Unidades de código, puntos de código y clusters de grafemas ¿Qué estaba sucediendo? ¿Qué significa "sobre U+FFFF" en ese párrafo anterior? ¿Qué byte offsets? Para entender este error debemos introducir tres piezas de vocabulario: Unidades de código → Puntos de código → Clusters de grafemas Unidades de código son los valores 16-bit crudos que JavaScript utiliza para almacenar cadenas internamente (UTF-16). Esto es lo que .length cuenta. Esto es lo que .slice() y charCodeAt() operan sobre como bien. JavaScript opera a nivel de unidades de código por defecto Puntos de código son lo que Unicode define efectivamente como un solo carácter. Un punto de código como U+1F920 (🤠) es un carácter en vista de Unicode, pero es demasiado grande para adaptarse a un solo 16-bit unidad de código. Entonces UTF-16 lo divide en dos unidades de código llamadas una pareja de sustitutos: un sustituto alto y un sustituto bajo. Simples caracteres ASCII y muchos símbolos comunes se adaptan a una unidad de código, por lo que la distinción no cuenta para ellos. Emoji, sin embargo? Casi siempre dos. Clusters de grafemas son lo que un ser humano percibe como un solo carácter. Esto es lo que ves cuando miras una cadena en un editor de texto. No es necesariamente lo que se almacena en memoria, sin embargo. En el caso de nuestro error, el cluster de grafemas era dos emojis, pero las unidades de código eran una pareja de sustitutos. La operación de división estaba dividiendo la pareja de sustitutos a un byte offset incorrecto, resultando en el error.
Comentarios (0)
Login or Register to apply