wip: deserialize and serialize nice bytes

this is nice, but fails in tests. looks like:

{
  "Finished": {
    "before": {
      "config": {
        "evict_bytes": "92.23MiB"
      },
      "freed_bytes": "0B"
    },
    "planned": {
      "respecting_tenant_min_resident_size": {
        "config": {
          "evict_bytes": "92.23MiB"
        },
        "freed_bytes": "95.28MiB"
      },
      "fallback_to_global_lru": null
    },
    "assumed": {
      "projected_after": {
        "config": {
          "evict_bytes": "92.23MiB"
        },
        "freed_bytes": "95.28MiB"
      },
      "failed": {
        "file_sizes": 0,
        "count": 0
      }
    }
  }
}
This commit is contained in:
Joonas Koivunen
2023-03-21 12:10:29 +02:00
parent d364f5936b
commit 81425123ee
4 changed files with 68 additions and 1 deletions

10
Cargo.lock generated
View File

@@ -2484,6 +2484,7 @@ dependencies = [
"tokio-util",
"toml_edit",
"tracing",
"ubyte",
"url",
"utils",
"walkdir",
@@ -4417,6 +4418,15 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "ubyte"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c81f0dae7d286ad0d9366d7679a77934cfc3cf3a8d67e82669794412b2368fe6"
dependencies = [
"serde",
]
[[package]]
name = "uname"
version = "0.1.1"

View File

@@ -56,6 +56,7 @@ tokio-postgres.workspace = true
tokio-util.workspace = true
toml_edit = { workspace = true, features = [ "serde" ] }
tracing.workspace = true
ubyte = { version = "0.10.3", features = ["serde"] }
url.workspace = true
walkdir.workspace = true
metrics.workspace = true

View File

@@ -40,7 +40,11 @@ paths:
- evict_bytes
properties:
evict_bytes:
type: integer
description: Unsigned bytes or a human writable amount of bytes with IEC kibibyte suffixes.
oneOf:
- type: integer
- type: string
pattern: '^[0-9]+ ?(([KMGTP]i)?B)?$'
responses:
"200":
description: |

View File

@@ -1208,14 +1208,66 @@ async fn disk_usage_eviction_run(mut r: Request<Body>) -> Result<Response<Body>,
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
struct Config {
/// How many bytes to evict before reporting that pressure is relieved.
#[serde(
deserialize_with = "deserialize_bytes",
serialize_with = "serialize_bytes"
)]
evict_bytes: u64,
}
fn serialize_bytes<S: serde::Serializer>(x: &u64, ser: S) -> Result<S::Ok, S::Error> {
use ubyte::ByteUnit;
let x = ByteUnit::from(*x);
// ByteUnit has a nice lossy serialization format as it's Display
ser.collect_str(&x)
}
fn deserialize_bytes<'d, D: serde::Deserializer<'d>>(des: D) -> Result<u64, D::Error> {
struct Visitor;
impl<'de> serde::de::Visitor<'de> for Visitor {
type Value = u64;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("positive nsigned number of bytes or positive number of bytes with SI/IEC suffix in a string")
}
fn visit_u64<E>(self, v: u64) -> std::result::Result<Self::Value, E>
where
E: serde::de::Error,
{
if v == 0 {
Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Unsigned(v),
&self,
))
} else {
Ok(v)
}
}
fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
where
E: serde::de::Error,
{
use std::str::FromStr;
let bytes = ubyte::ByteUnit::from_str(v).map_err(serde::de::Error::custom)?;
let bytes = u64::from(bytes);
self.visit_u64(bytes)
}
}
des.deserialize_any(Visitor)
}
#[derive(Debug, Clone, Copy, serde::Serialize)]
struct Usage {
// remains unchanged after instantiation of the struct
config: Config,
// updated by `add_available_bytes`
#[serde(serialize_with = "serialize_bytes")]
freed_bytes: u64,
}