review classifier: recover from a stored record it can't decode
The controller keeps the classifier's newest runs as one JSON array and its settings as one JSON object, each under one KV key. A document this build could not decode, as after a rollback past a build that added an outcome or a settings field, failed every read and every write until someone deleted the key by hand. A classifier newer than its controller refused every run request for the missing `model`. The run store now decodes the array one record at a time: a record that does not decode is dropped with a WARN naming the bucket, key, index and error, and a document that is not an array reads as no runs. Listing keeps working, and the next recorded run writes back only the records that decoded. The stored settings ignore a field this build does not know; one that still does not decode reads as the defaults with a WARN. The PUT body keeps `deny_unknown_fields`. A PUT now always writes, so a PUT of the defaults also replaces an undecodable document. A run request without `model` runs on haiku, the swarm-wide setting's default, now one constant in swarm-queue-client. `schedules::read` gains a sibling, `read_with`, taking the decoder. Closes #5162
This commit is contained in:
parent
30d9e1d8e6
commit
6f0a72b246
5 changed files with 208 additions and 17 deletions
|
|
@ -407,6 +407,18 @@ async fn open_or_create_definitions(client: &async_nats::Client) -> anyhow::Resu
|
|||
pub(crate) async fn read<T: Default + serde::de::DeserializeOwned>(
|
||||
store: &Store,
|
||||
key: &str,
|
||||
) -> anyhow::Result<(T, Option<u64>)> {
|
||||
read_with(store, key, |bytes| {
|
||||
serde_json::from_slice(bytes).with_context(|| format!("{key} is not decodable"))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// As [`read`], with the stored bytes decoded by `decode`.
|
||||
pub(crate) async fn read_with<T: Default>(
|
||||
store: &Store,
|
||||
key: &str,
|
||||
decode: impl FnOnce(&[u8]) -> anyhow::Result<T>,
|
||||
) -> anyhow::Result<(T, Option<u64>)> {
|
||||
let Some(entry) = store
|
||||
.entry(key)
|
||||
|
|
@ -418,9 +430,7 @@ pub(crate) async fn read<T: Default + serde::de::DeserializeOwned>(
|
|||
if entry.operation != Operation::Put {
|
||||
return Ok((T::default(), Some(entry.revision)));
|
||||
}
|
||||
let value =
|
||||
serde_json::from_slice(&entry.value).with_context(|| format!("{key} is not decodable"))?;
|
||||
Ok((value, Some(entry.revision)))
|
||||
Ok((decode(&entry.value)?, Some(entry.revision)))
|
||||
}
|
||||
|
||||
/// Write `value` to `key` against `revision`. `Ok(false)` when another write
|
||||
|
|
|
|||
Loading…
Reference in a new issue