disklayer_sqlite/
auto_cache.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! [`LayerAttach`] implementation for automatically opening a dbhd to use as a
5//! read cache, based on the identity of the next layer.
6
7use crate::FormatParams;
8use crate::SqliteDiskLayer;
9use anyhow::Context;
10use disk_layered::LayerAttach;
11use disk_layered::LayerIo;
12use fs_err::PathExt;
13use std::path::PathBuf;
14
15pub struct AutoCacheSqliteDiskLayer {
16    path: PathBuf,
17    key: Option<String>,
18    read_only: bool,
19}
20
21impl AutoCacheSqliteDiskLayer {
22    pub fn new(path: PathBuf, key: Option<String>, read_only: bool) -> Self {
23        Self {
24            path,
25            key,
26            read_only,
27        }
28    }
29}
30
31impl LayerAttach for AutoCacheSqliteDiskLayer {
32    type Error = anyhow::Error;
33    type Layer = SqliteDiskLayer;
34
35    async fn attach(
36        self,
37        lower_layer_metadata: Option<disk_layered::DiskLayerMetadata>,
38    ) -> Result<Self::Layer, Self::Error> {
39        let metadata = lower_layer_metadata.context("no layer to cache")?;
40        let key = self.key.map_or_else(
41            || {
42                let disk_id = metadata
43                    .disk_id
44                    .context("cannot cache without a disk ID to use as a key")?;
45                Ok(disk_id.map(|b| format!("{b:2x}")).join(""))
46            },
47            anyhow::Ok,
48        )?;
49        if key.is_empty() {
50            anyhow::bail!("empty cache key");
51        }
52        let path = self.path.join(key).join("cache.dbhd");
53        let format_dbhd = if path.fs_err_try_exists()? || self.read_only {
54            None
55        } else {
56            fs_err::create_dir_all(path.parent().unwrap())?;
57            Some(FormatParams {
58                logically_read_only: true,
59                len: metadata.sector_count * metadata.sector_size as u64,
60                sector_size: metadata.sector_size,
61            })
62        };
63        let layer = SqliteDiskLayer::new(&path, self.read_only, format_dbhd)?;
64        if layer.sector_count() != metadata.sector_count {
65            anyhow::bail!(
66                "cache layer has different sector count: {} vs {}",
67                layer.sector_count(),
68                metadata.sector_count
69            );
70        }
71        Ok(layer)
72    }
73}