Denoで保存されている/tmp/deno_cacheに何が保管されているか気になる

6 min read

こんにちは、無能です。
前回の記事で/tmp/deno_cacheにキャッシュが保管されていることがわかりましたが何を保存しているのか気になったので見てみる。

SQLiteで保存されている

まずは/tmp/deno_cacheに移動してみます。

cd /tmp/deno_cache
ls
7b52928d182b91e3d058c899022fc32b63d560748054c3143d70a24863f0f2ba

cd 7b52928d182b91e3d058c899022fc32b63d560748054c3143d70a24863f0f2ba
ls -la
合計 120
drwxrwxr-x 3 haturatu haturatu  4096 1010 21:58 .
drwxrwxr-x 3 haturatu haturatu  4096 1010 21:58 ..
drwxrwxr-x 3 haturatu haturatu  4096 1010 21:58 1
-rw-r--r-- 1 haturatu haturatu  4096 1010 21:58 cache_metadata.db
-rw-r--r-- 1 haturatu haturatu 32768 1010 22:02 cache_metadata.db-shm
-rw-r--r-- 1 haturatu haturatu 70072 1010 22:02 cache_metadata.db-wal

どうやらこの感じはSQLiteで保存している様子。なので接続してみましょう。

sqlite> .tables
cache_storage          request_response_list
sqlite> select * from cache_storage;
1|lume_remote_files
sqlite> select * from request_response_list;
1|1|https://cdn.jsdelivr.net/npm/svg2png-wasm@1.4.1/svg2png_wasm_bg.wasm||access-control-allow-origin
*
access-control-expose-headers
*
timing-allow-origin
*
cache-control
public, max-age=31536000, s-maxage=31536000, immutable
cross-origin-resource-policy
cross-origin
~~省略~~~
max-age=31536000; includeSubDomains; preload
x-content-type-options
nosniff
server
cloudflare
|200|OK|5f1e6f823db55b21ff1e048860bbbce7b104fe57fa3a4449ce36989784625f32|1728565376
sqlite>

じゃあdeno_cache/01_cache.jsあたりから深堀して見てみよう

alleycat:[haturatu]:~/git/deno/ext/cache$ ls -la
合計 60
drwxr-xr-x  2 haturatu haturatu  4096 10月 12 18:16 .
drwxr-xr-x 24 haturatu haturatu  4096 10月 12 18:16 ..
-rw-r--r--  1 haturatu haturatu  9048 10月 12 18:16 01_cache.js
-rw-r--r--  1 haturatu haturatu   491 10月 12 18:16 Cargo.toml
-rw-r--r--  1 haturatu haturatu  1043 10月 12 18:16 README.md
-rw-r--r--  1 haturatu haturatu  2042 10月 12 18:16 lib.deno_cache.d.ts
-rw-r--r--  1 haturatu haturatu  9827 10月 12 18:16 lib.rs
-rw-r--r--  1 haturatu haturatu 12666 10月 12 18:16 sqlite.rs
alleycat:[haturatu]:~/git/deno/ext/cache$ cat Cargo.toml 
# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

[package]
name = "deno_cache"
version = "0.103.0"
authors.workspace = true
edition.workspace = true
license.workspace = true
readme = "README.md"
repository.workspace = true
description = "Implementation of Cache API for Deno"

[lib]
path = "lib.rs"

[dependencies]
async-trait.workspace = true
deno_core.workspace = true
rusqlite.workspace = true
serde.workspace = true
sha2.workspace = true
tokio.workspace = true

alleycat:[haturatu]:~/git/deno/ext/cache$ grep "tmp" ./*
alleycat:[haturatu]:~/git/deno/ext/cache$ grep "deno_cache" ./*
./Cargo.toml:name = "deno_cache"
./README.md:# deno_cache
./lib.rs:deno_core::extension!(deno_cache,

alleycat:[haturatu]:~/git/deno/ext/cache$ cat lib.rs 
~~~
deno_core::extension!(deno_cache,
  deps = [ deno_webidl, deno_web, deno_url, deno_fetch ],
  parameters=[CA: Cache],
  ops = [
    op_cache_storage_open<CA>,
    op_cache_storage_has<CA>,
    op_cache_storage_delete<CA>,
    op_cache_put<CA>,
    op_cache_match<CA>,
    op_cache_delete<CA>,
  ],
  esm = [ "01_cache.js" ],
  options = {
    maybe_create_cache: Option<CreateCache<CA>>,
  },
  state = |state, options| {
    if let Some(create_cache) = options.maybe_create_cache {
      state.put(create_cache);
    }
  },
);
~~~

alleycat:[haturatu]:~/git/deno/ext/cache$ grep " op_cache_storage" ./*
./01_cache.js:  op_cache_storage_delete,
./01_cache.js:  op_cache_storage_has,
./01_cache.js:  op_cache_storage_open,
./01_cache.js:    const cacheId = await op_cache_storage_open(cacheName);
./01_cache.js:    return await op_cache_storage_has(cacheName);
./01_cache.js:    return await op_cache_storage_delete(cacheName);
./lib.rs:    op_cache_storage_open<CA>,
./lib.rs:    op_cache_storage_has<CA>,
./lib.rs:    op_cache_storage_delete<CA>,
./lib.rs:pub async fn op_cache_storage_open<CA>(
./lib.rs:pub async fn op_cache_storage_has<CA>(
./lib.rs:pub async fn op_cache_storage_delete<CA>(

alleycat:[haturatu]:~/git/deno/ext/cache$ grep "cacheName" ./*
./01_cache.js:  async open(cacheName) {
./01_cache.js:    cacheName = webidl.converters["DOMString"](cacheName, prefix, "Argument 1");
./01_cache.js:    const cacheId = await op_cache_storage_open(cacheName);
./01_cache.js:  async has(cacheName) {
./01_cache.js:    cacheName = webidl.converters["DOMString"](cacheName, prefix, "Argument 1");
./01_cache.js:    return await op_cache_storage_has(cacheName);
./01_cache.js:  async delete(cacheName) {
./01_cache.js:    cacheName = webidl.converters["DOMString"](cacheName, prefix, "Argument 1");
./01_cache.js:    return await op_cache_storage_delete(cacheName);
./lib.deno_cache.d.ts:  open(cacheName: string): Promise<Cache>;
./lib.deno_cache.d.ts:  has(cacheName: string): Promise<boolean>;
./lib.deno_cache.d.ts:  delete(cacheName: string): Promise<boolean>;

sqlite.rsを見てみます。

alleycat:[haturatu]:~/git/deno/ext/cache$ grep "cache_storage_dir" ./*
./sqlite.rs:  pub cache_storage_dir: PathBuf,
./sqlite.rs:  pub fn new(cache_storage_dir: PathBuf) -> Self {
./sqlite.rs:      std::fs::create_dir_all(&cache_storage_dir)
./sqlite.rs:      let path = cache_storage_dir.join("cache_metadata.db");
./sqlite.rs:        cache_storage_dir,
./sqlite.rs:    let cache_storage_dir = self.cache_storage_dir.clone();
./sqlite.rs:      let responses_dir = get_responses_dir(cache_storage_dir, cache_id);
./sqlite.rs:    let cache_storage_dir = self.cache_storage_dir.clone();
./sqlite.rs:        let cache_dir = cache_storage_dir.join(cache_id.to_string());
./sqlite.rs:    let cache_storage_dir = self.cache_storage_dir.clone();
./sqlite.rs:        get_responses_dir(cache_storage_dir, request_response.cache_id);
./sqlite.rs:    let cache_storage_dir = self.cache_storage_dir.clone();
./sqlite.rs:          get_responses_dir(cache_storage_dir, request.cache_id)
./sqlite.rs:fn get_responses_dir(cache_storage_dir: PathBuf, cache_id: i64) -> PathBuf {
./sqlite.rs:  cache_storage_dir

alleycat:[haturatu]:~/git/deno/ext/cache$ head -5 sqlite.rs 
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::borrow::Cow;
use std::path::PathBuf;
use std::pin::Pin;
use std::rc::Rc;

alleycat:[haturatu]:~/git/deno$ grep "PathBuf" ./*/* 2>/dev/null | grep -v use
./cli/build.rs:    let op_crate_libs = state.borrow::<HashMap<&str, PathBuf>>();
./cli/build.rs:    let path_dts = state.borrow::<PathBuf>();
./cli/build.rs:          PathBuf::from(op_crate_lib).canonicalize()?
./cli/build.rs:      op_crate_libs: HashMap<&'static str, PathBuf>,
./cli/build.rs:      path_dts: PathBuf,
./cli/build.rs:  pub fn create_compiler_snapshot(snapshot_path: PathBuf, cwd: &Path) {
./cli/build.rs:      PathBuf::from(env::var_os("OUT_DIR").unwrap())
~~~

alleycat:[haturatu]:~/git/deno$ cd ./ext/cache/
alleycat:[haturatu]:~/git/deno/ext/cache$ grep let ./* | grep dir
./sqlite.rs:      let path = cache_storage_dir.join("cache_metadata.db");
./sqlite.rs:    let cache_storage_dir = self.cache_storage_dir.clone();
./sqlite.rs:      let responses_dir = get_responses_dir(cache_storage_dir, cache_id);
./sqlite.rs:    let cache_storage_dir = self.cache_storage_dir.clone();
./sqlite.rs:        let cache_dir = cache_storage_dir.join(cache_id.to_string());
./sqlite.rs:    let cache_storage_dir = self.cache_storage_dir.clone();
./sqlite.rs:      let responses_dir =
./sqlite.rs:      let response_path = responses_dir.join(&body_key);
./sqlite.rs:    let cache_storage_dir = self.cache_storage_dir.clone();

alleycat:[haturatu]:~/git/deno/ext/cache$ vim ./sqlite.rs
alleycat:[haturatu]:~/git/deno/ext/cache$ cat ./sqlite.rs
~~~
use std::path::PathBuf;
~~~
#[derive(Clone)]
pub struct SqliteBackedCache {
  pub connection: Arc<Mutex<Connection>>,
  pub cache_storage_dir: PathBuf,
}
~~~

PathBuf

公式ドキュメント

alleycat:[haturatu]:~/git/deno/ext/cache$ cd -
alleycat:[haturatu]:~/git/deno$ grep "let mut" ./*/*/* | grep "PathBuf"
grep: ./cli/bench/fs: ディレクトリです
grep: ./cli/bench/http: ディレクトリです
grep: ./cli/bench/napi: ディレクトリです
grep: ./cli/bench/stdio: ディレクトリです
grep: ./cli/bench/testdata: ディレクトリです
./cli/cache/disk_cache.rs:    let mut out = PathBuf::new();

お!

alleycat:[haturatu]:~/git/deno$ cd ./cli/cache/
alleycat:[haturatu]:~/git/deno/cli/cache$ ls -la
合計 136
drwxr-xr-x  2 haturatu haturatu  4096 10月 12 18:16 .
drwxr-xr-x 15 haturatu haturatu  4096 10月 12 18:16 ..
-rw-r--r--  1 haturatu haturatu 17292 10月 12 18:16 cache_db.rs
-rw-r--r--  1 haturatu haturatu  3643 10月 12 18:16 caches.rs
-rw-r--r--  1 haturatu haturatu  5190 10月 12 18:16 check.rs
-rw-r--r--  1 haturatu haturatu  5668 10月 12 18:16 code_cache.rs
-rw-r--r--  1 haturatu haturatu  1090 10月 12 18:16 common.rs
-rw-r--r--  1 haturatu haturatu  8449 10月 12 18:16 deno_dir.rs
-rw-r--r--  1 haturatu haturatu  7647 10月 12 18:16 disk_cache.rs
-rw-r--r--  1 haturatu haturatu  7195 10月 12 18:16 emit.rs
-rw-r--r--  1 haturatu haturatu  4531 10月 12 18:16 fast_check.rs
-rw-r--r--  1 haturatu haturatu  8367 10月 12 18:16 incremental.rs
-rw-r--r--  1 haturatu haturatu 12142 10月 12 18:16 mod.rs
-rw-r--r--  1 haturatu haturatu  7796 10月 12 18:16 module_info.rs
-rw-r--r--  1 haturatu haturatu  4847 10月 12 18:16 node.rs
-rw-r--r--  1 haturatu haturatu  4567 10月 12 18:16 parsed_source.rs

alleycat:[haturatu]:~/git/deno/cli/cache$ vim disk_cache.rs 
~~~
impl DiskCache {
  /// `location` must be an absolute path.
  pub fn new(location: &Path) -> Self {
    assert!(location.is_absolute());
    Self {
      location: location.to_owned(),
    }
  }

  fn get_cache_filename(&self, url: &Url) -> Option<PathBuf> {
    let mut out = PathBuf::new();
~~~

alleycat:[haturatu]:~/git/deno/cli/cache$ grep "get_cache_filename" ./* | grep "let"
./disk_cache.rs:    let base = self.get_cache_filename(url)?;

なんとなくはどこが呼び出されているかは把握できました。
ちなみに前回のエラーが起きるのはおそらくバグなんかではなく、所有権を変えた場合の稀なパターンであり、例えば

which deno
/home/haturatu/.deno/bin/deno

であるときにこのdenoの実行ユーザはhaturatuです。
と、これをわざわざ別ユーザの権限で実行し、生成された/tmp/deno_cacheであり他ユーザと衝突を起こしてしまいます。
複数ユーザ間でdenoコマンドを実行するべきでは無い(必要なパターンを除く)し、これは妥当だと思います。
厳密に行うのであれば、ちゃんとdenoユーザを作成しそれでインストールして実行したほうが多分良いですね。

でも気になるとするならば

deno infoをしたときに/tmp/deno_cacheが出力されないことかなあ。

alleycat:[haturatu]:~/git/deno/ext/cache$ deno info
DENO_DIR location: /home/haturatu/.cache/deno
Remote modules cache: /home/haturatu/.cache/deno/deps
npm modules cache: /home/haturatu/.cache/deno/npm
Emitted modules cache: /home/haturatu/.cache/deno/gen
Language server registries cache: /home/haturatu/.cache/deno/registries
Origin storage: /home/haturatu/.cache/deno/location_data

ちょっと気になったのでメモ書き程度に。
それでは。
またよろしくおねがいします。