🐘 PostgreSQL Driver Pitfalls
Issue: “Failed to parse JSON: expected value at line 1 column 1”
Symptom
When querying aggregated events or JSONB payloads from a PostgreSQL database under a WebAssembly runtime (such as Fermyon Spin), you encounter a parsing/deserialization error like:Root Cause
This error occurs when using PostgreSQL-native aggregation functions (e.g.,json_agg or json_build_object) inside custom raw SQL queries.
Because the low-level Fermyon Spin PostgreSQL WASI driver doesn’t have native, high-level rust-postgres type-mappings for aggregated JSON results, it returns them under the low-level DbValue::Unsupported(Vec<u8>) (or SpinPgDbVal::Unsupported) variant rather than standard strings or structured types.
If this unsupported byte payload is unmatched or printed raw via its debug representation, it generates a non-JSON debug string like "DbValue::Unsupported([91, 123, ...])". Feeding this raw string into serde_json::from_str throws the expected value at line 1 column 1 error (because the string begins with "D" instead of valid JSON brackets/braces [ or {).
Solution
Our database adapter handles the unsupported and JSONB value variants explicitly instead of assuming every runtime value is scalar. Instead of falling back to debug string formatting, the adapter attempts to parse the raw byte slices directly usingserde_json::from_slice:
- Avoid treating JSON-aggregated query columns as simple text strings (
DbValue::Str). - Always check if your driver returns them as
DbValue::UnsupportedorDbValue::Jsonbbyte arrays. - Safely parse using
serde_json::from_sliceto prevent decoding crashes.
🗄️ SQLite Driver Pitfalls
Issue: Dynamic Type Conversions
In SQLite, columns are dynamically typed (affinity-based). If a query column containsjson_object or json_group_array, the driver may return the result as a raw Value::Blob(Vec<u8>) or Value::Text(String).
- Pitfall: Attempting to extract a text string using
.as_str()directly on a value returned as aBlobwill fail. - Best Practice: Use our core adapter’s helper methods, which safely handle both text strings and binary bytes dynamically:
🔄 Concurrency Collision vs. Database Locking
Symptom: RepositoryError::Concurrency vs Gateway Timeout
- Concurrency Collision: Occurs when two request threads try to commit events with the same
revisionsequence under Optimistic Concurrency Control (OCC). This is a healthy, expected system transition. The transaction rolls back cleanly, and the client should load the latest event stream and retry. - Database Deadlock / Timeout: Occurs when your database connection pool is exhausted or a connection block is held open indefinitely by long-running transactions.
- Tip: In Spin or Wasmtime serverless environments, avoid holding long-running synchronous locks or blocks. Projections should write and checkpoint asynchronously to prevent blocking the write transaction paths.