🐘 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 (adapters.rs) has been updated to explicitly pattern-matchSpinPgDbVal::Unsupported(b) and SpinPgDbVal::Jsonb(j) variants.
Instead of falling back to debug string formatting, the adapter attempts to parse the raw byte slices directly using serde_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.