Na rede testnet
, cardano-db-sync em tags/5.0.0
no modo extended
, está lançando exceções no bloco 1816809 devido a problemas de processamento / análise unicode. Não é possível sincronizar após este bloco.
[db-sync-node:Info:234667] [2020-09-19 14:23:20.41 UTC] Starting chainSyncClient
[db-sync-node:Info:234667] [2020-09-19 14:23:20.48 UTC] Cardano.Db tip is at slot 6061839, block 1816809
[db-sync-node:Info:234672] [2020-09-19 14:23:20.48 UTC] Running DB thread
[db-sync-node:Info:234672] [2020-09-19 14:23:20.65 UTC] Rolling back to slot 6061839, hash 98414d12d1d1f05210dea6ce4082e1bcbbcfdf56343fd1cb44a8778c4c9ea57a
[db-sync-node:Info:234672] [2020-09-19 14:23:20.66 UTC] Deleting blocks numbered: []
[db-sync-node:Error:234672] [2020-09-19 14:23:21.03 UTC] runDBThread: DbInsertException "TxMetadata" (SqlError {sqlState = "22P05", sqlExecStatus = FatalError, sqlErrorMsg = "unsupported Unicode escape sequence", sqlErrorDetail = "\\u0000 cannot be converted to text.", sqlErrorHint = ""})
[db-sync-node:Error:234667] [2020-09-19 14:23:21.05 UTC] ChainSyncWithBlocksPtcl: DbInsertException "TxMetadata" (SqlError {sqlState = "22P05", sqlExecStatus = FatalError, sqlErrorMsg = "unsupported Unicode escape sequence", sqlErrorDetail = "\\u0000 cannot be converted to text.", sqlErrorHint = ""})
[db-sync-node.Subscription:Error:234663] [2020-09-19 14:23:21.05 UTC] [String "Application Exception: LocalAddress {getFilePath = \"/run/cardano-node/node.socket\"} DbInsertException \"TxMetadata\" (SqlError {sqlState = \"22P05\", sqlExecStatus = FatalError, sqlErrorMsg = \"unsupported Unicode escape sequence\", sqlErrorDetail = \"\\\\u0000 cannot be converted to text.\", sqlErrorHint = \"\"})",String "SubscriptionTrace"]
[db-sync-node.ErrorPolicy:Error:4] [2020-09-19 14:23:21.05 UTC] [String "ErrorPolicyUnhandledApplicationException (DbInsertException \"TxMetadata\" (SqlError {sqlState = \"22P05\", sqlExecStatus = FatalError, sqlErrorMsg = \"unsupported Unicode escape sequence\", sqlErrorDetail = \"\\\\u0000 cannot be converted to text.\", sqlErrorHint = \"\"}))",String "ErrorPolicyTrace",String "LocalAddress {getFilePath = \"/run/cardano-node/node.socket\"}"]
Esta é uma dor ENORME no pescoço.
Os metadados tx que estão sendo inseridos são
(1,S "\NUL\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX\ETX")
que está sendo codificado como JSON:
"\u0000\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003"
que obviamente não é UTF8 válido e, portanto, o banco de dados o rejeita.
Pior ainda, o código acima é codificado como UTF8 pela biblioteca Haskell Text
conforme mostrado, mas o PostgreSQL então o rejeita.
Deixe-me reiterar como isso é uma enorme dor no pescoço.
A string em questão é decodificada para UTF8 pela biblioteca Haskell Text
e então passada para o Postgres que lança uma exceção SqlError
. Eu tentei capturar essa exceção, mas ela não parece funcionar.
Posso capturar e registrar a exceção:
[db-sync-node:Warning:37] [2020-09-21 00:26:18.77 UTC] insertTxMetadata: Failed to insert (1,S "\NUL...\ETX")
SqlError {sqlState = "22P05", sqlExecStatus = FatalError, sqlErrorMsg
= "unsupported Unicode escape sequence", sqlErrorDetail = "\\u0000 cannot be converted to text.", sqlErrorHint = ""}
mas então há uma segunda exceção:
[db-sync-node:Error:37] [2020-09-21 00:26:18.77 UTC] runDBThread: SqlError {sqlState = "25P02", sqlExecStatus = FatalError, sqlErrorMsg = "current transaction is aborted, commands ignored until end of transaction
block", sqlErrorDetail = "", sqlErrorHint = ""}
Isso sugere que preciso validar mais completamente o JSON antes da inserção.
Podemos armazenar UTF8 inválido no PostgreSQL, mas apenas como bytea
dados não como jsonb
e se formos para o antigo suporte de consulta JSON do Postgres não estará disponível.
Eu tentei uma decodificação UTF8 manual:
decodeUtf8IO :: ByteString -> IO (Either Text.UnicodeException Text)
decodeUtf8IO = try . evaluate . Text.decodeUtf8With Text.strictDecode
e isso também tem o mesmo problema.
De: https://stackoverflow.com/questions/31671634/handling-unicode-sequences-in-postgresql
Um byte nulo não é válido em uma string PostgreSQL. Nenhum deles é um ponto de código zero.
Atualmente acho que isso é devido à maneira como Haskell / Persistent serializa isso para enviar para o postgres. Atualmente tentando validar essa teoria.
Aeson.encode
já escapa do NUL
char para \u0000
que Persistente então rejeita.
Encontrar uma solução simples para isso não será fácil.
Eu estava investigando isso e parece que essas propriedades são válidas:
"\u0000\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003"
é bastante válido como UTF-8, uma vez que UTF-8 permite que \u0000
seja representado diretamente.\u0000
não é permitido para jsonb
porque não pode ser convertido em text
.A única solução temporária para codificá-lo para um JSON e, em seguida, para Text
e, em seguida, detectar a sequência de caracteres inválida e rejeitar o objeto JSON se contiver a sequência inválida. Também me pergunto se \u0000
é a única sequência que preciso rejeitar,
Você não poderia armazenar duas colunas? o bytea e o JSON quando não contém o caractere não textual, para que qualquer pessoa seja livre para usar o que precisar.
@erikd A \u0000
é claramente declarada aqui , junto com todas as outras regras que o Postgres segue. Resumindo: há rejeições e transformações acontecendo.
Para evitar uma abstração com vazamento, os valores exatos (conforme fornecidos pelo usuário) devem ser armazenados. Isso significa aproximadamente um blob (= sem estrutura no banco de dados) ou valores de string JSON que de alguma forma preservam o significado original: por exemplo, valores de string codificados em base64 no banco de dados (recuperando assim a estrutura às custas de processamento extra no aplicativo em para converter de base64 para o valor real).
A correção temporária (simplesmente descartar objetos de metadados que não podem ser inseridos no Postgres) foi mesclada com o master.
É assim que fica agora com correção temporária:
[db-sync-node:Info:37] [2020-09-22 10:19:09.66 UTC] insertShelleyBlock: epoch 84, slot 6060000, block 1816721, hash 3ec15354c53deae4eb26a206cc3185f799e80bd09393f279bce7e53a7d633144
[db-sync-node:Warning:37] [2020-09-22 10:19:24.08 UTC] insertTxMetadata: dropped due to a Unicode NUL character.
[db-sync-node:Warning:37] [2020-09-22 10:19:24.08 UTC] insertTxMetadata: dropped due to a Unicode NUL character.
[db-sync-node:Warning:37] [2020-09-22 10:19:24.10 UTC] insertTxMetadata: dropped due to a Unicode NUL character.
[db-sync-node:Warning:37] [2020-09-22 10:19:24.10 UTC] insertTxMetadata: dropped due to a Unicode NUL character.
[db-sync-node:Info:37] [2020-09-22 10:19:24.93 UTC] insertShelleyBlock: epoch 84, slot 6065000, block 1816973, hash 0ab0dd5c36c6eb480b8bbd05508be952c5ed8597a4e422af25bd80a905a9368d
Se o docker foi reparado e qual é a versão reparada
Não foi corrigido, mas 5.0.x
(mas eu recomendaria 5.0.2
) tem uma solução alternativa que evita esse problema eliminando tx_metadata
contendo o caractere Unicode NUL
.
Comentários muito úteis
É assim que fica agora com correção temporária: