15 个不稳定版本 (7 个破坏性版本)

0.8.0 2024 年 1 月 24 日
0.7.1 2023 年 8 月 28 日
0.6.1 2023 年 4 月 13 日
0.5.4 2022 年 4 月 5 日
0.1.0 2021 年 2 月 2 日

#25 in 缓存

Download history 23/week @ 2024-03-10 29/week @ 2024-03-17 6/week @ 2024-03-24 25/week @ 2024-03-31 13/week @ 2024-04-07 24/week @ 2024-04-14 16/week @ 2024-04-21 7/week @ 2024-04-28 3/week @ 2024-05-05 12/week @ 2024-05-19 1/week @ 2024-05-26 25/week @ 2024-06-02 47/week @ 2024-06-09 24/week @ 2024-06-16 15/week @ 2024-06-23

每月 111 次下载

MIT 许可证

88KB
1K SLoC

bkt

releases crates.io docs.rs build status issues license

bkt(发音为 "bucket")是一个用 Rust 编写的子进程缓存实用工具,受 bash-cache 的启发。使用 bkt 包装昂贵的进程调用允许调用者重用最近的调用,而不会复杂化其应用程序逻辑。这在 shell 提示、交互式应用程序(如 fzf)以及轮询其他进程的长运行程序中很有用。

bkt 作为独立二进制文件以及 Rust 库 提供。有关库文档,请参阅 https://docs.rs/bkt/。本 README 涵盖了 bkt 二进制文件。

安装

运行 cargo install bkt 编译和安装 bkt。如果您系统上尚未安装 cargo,则需要 安装 cargo

常见平台的预编译二进制文件附在每个 版本(从 0.5 开始)。如果您希望发布包括更多平台二进制文件,请提出问题或发送 PR。

正在跟踪包管理器支持 此处;欢迎志愿者。

Packaging status

用法

bkt [--ttl=DURATION] [--stale=DURATION] [--cwd] [--env=ENV ...] [--modtime=FILE ...] [--scope=SCOPE] [--discard-failures] [--warm|--force] -- <command>...

使用 bkt 最简单的方法是在您打算缓存的命令前加上 bkt -- 前缀,例如

# Execute and cache an invocation of 'date +%s.%N'
$ bkt -- date +%s.%N
1631992417.080884000

# A subsequent invocation reuses the same cached output
$ bkt -- date +%s.%N
1631992417.080884000

bkt 接收到它之前(或最近)未看到的命令时,它将同步执行该命令,并缓存其 stdout、stderr 和退出代码。再次使用相同命令调用 bkt 将从缓存中读取数据,并输出仿佛该命令再次运行一样。

缓存有效期

有两个标志 --ttl--stale,用于配置缓存数据的保留时间。默认情况下,bkt 使用 60 秒的 TTL(存活时间),这意味着超过六十秒的缓存数据将被丢弃,并重新执行后台命令。通过传递不同的值,例如 --ttl=1d,将改变缓存数据被视为有效的时间。默认的 TTL 可以通过定义一个 BKT_TTL 环境变量来覆盖。

当数据过期时,bkt 必须同步重新执行命令,这可能会引入意外的延迟。为了避免这种情况,传递 --stale 与 TTL 的时间长度更短。当缓存数据超过过期阈值时,这将导致 bkt 在后台刷新缓存,同时仍然及时返回缓存的数据。

这两个标志(以及 BKT_TTL)接受持续时间字符串,如 10s1hour 30min。确切的语法由 humantime 库定义。

执行环境

某些命令的行为不仅取决于命令行参数。可以调整 bkt 缓存这些命令的方式,以便无关的调用被分别缓存。

工作目录

例如,默认情况下尝试缓存 pwd 不会按预期工作

$ $ bkt -- pwd
/tmp/foo

$ cd ../bar

# Cached output for 'pwd' is reused even though the directory has changed
$ bkt -- pwd
/tmp/foo

要使 bkt 不仅要根据命令行参数,还要根据当前工作目录进行缓存,请传递 --cwd

$ bkt --cwd -- pwd
/tmp/foo

$ cd ../bar

$ bkt --cwd -- pwd
/tmp/bar

环境变量

同样,要指定一个或多个环境变量作为缓存的命令的相关变量,请使用 --env,例如 --env=LANG。此标志可以多次提供,以根据其他变量进行缓存。具有给定变量不同值的调用将被分别缓存。

文件修改

还可以让 bkt 检查一个或多个文件的最后修改时间,并使用 --modtime 将其包含在缓存键中。例如,传递 --modtime=/etc/passwd 将导致在 /etc/passwd 修改时重新执行后台命令。

手动刷新

还可以使用 --force--warm 触发刷新。前者的行为与缓存数据未找到时完全相同,执行进程并缓存结果。这在你知道缓存数据不再是最新的情况下很有用,例如,因为外部更改了某些东西。

或者,可以使用 --warm 提供的异步刷新缓存。这将在后台触发刷新,但立即结束当前进程而不输出。这在你预期不久将会有额外的调用并希望确保它们获得缓存命中时很有用。请注意,直到预热过程完成,并发调用仍然可能看到缓存未命中并触发它们自己的调用。

设置缓存范围

缓存数据将持久化到磁盘(但请参阅以下 内容),并且对调用 bkt 的任何进程都可用。通常这是所希望的,但某些用法可能希望将它们的调用与其他可能的并发调用隔离。

为此,请使用足够独特的参数传递 --scope=...,例如调用程序的固定标签、当前进程 ID 或时间戳。

$ bkt -- date +%s.%N
1631992417.080884000

# Changing the scope causes the command to be cached separately
$ bkt --scope=foo -- date +%s.%N
1631992418.010562000

或者,定义一个 BKT_SCOPE 环境变量来配置调用之间的统一范围。这在脚本中很有用,可以确保所有命令共享一个范围。

#!/bin/bash

# Set a unique scope for this script invocation using the PID and current time
export BKT_SCOPE="my_script_$$_$(date -Ins)"

丢弃失败的调用

默认情况下,所有调用都会被缓存,无论它们的输出或退出代码如何。在应该不缓存失败的情况下,通过传递--discard-failures来仅保留成功的调用(返回0退出代码的调用)。

警告:传递此标志可能会导致支持命令的调用频率高于--ttl所建议的,这反过来又可能产生意外的负载。如果支持命令由于中断或错误(如过载的网站)而失败,触发额外的调用可能会加剧问题,实际上会导致DDoS攻击受影响的系统。通常,设置此标志,而是使客户端能够应对偶尔的失败,会更安全。

更改缓存目录

默认情况下,缓存数据存储在/tmp或类似的临时目录下;可以通过--cache-dir标志或通过定义BKT_CACHE_DIR环境变量来自定义。

如果定义了BKT_TMPDIR环境变量,则将使用它而不是系统的临时目录。尽管BKT_TMPDIRBKT_CACHE_DIR具有类似的效果,但BKT_TMPDIR旨在用于配置全局缓存位置(例如,通过在您的.bashrc或类似的文件中声明),而--cache-dir/BKT_CACHE_DIR应用于自定义特定调用集的缓存位置,这些调用集不应使用默认缓存目录。

请注意,目录的选择可能会影响bkt的性能:如果缓存存储在tmpfs或固态分区下,它将比缓存到机械硬盘快得多。

安全和隐私

默认缓存目录可能是世界可读的。在Unix上,缓存目录以700权限创建,这意味着只有当前用户可以访问它,但这并非万无一失。

您可以将缓存目录自定义(见上面)到一个您信任的位置,例如~/.bkt,但请注意,您的家目录可能比默认选择的临时目录慢。

一般来说,如果您不是系统上的唯一用户,最好将您的TMPDIR配置为只有您可以访问的位置。如果不可能,请使用BKT_TMPDIR配置一个专门为bkt配置的自定义临时目录。

模式和技巧

请在讨论板上分享您如何使用bkt

加快fzf和其他预览工具的速度

bkt与执行其他命令的交互式工具(如fzf)配合良好。由于fzf在每次选择元素时都会执行--preview命令,因此当命令执行时间较长时,浏览可能会变得缓慢且繁琐。使用bkt允许为每个选择缓存预览。比较

$ printf '%s\n' 1 0.2 3 0.1 5 | \
  fzf --preview="bash -c 'sleep {}; echo {}'"

$ printf '%s\n' 1 0.2 3 0.1 5 | \
  fzf --preview="bkt --ttl=10m --stale=10s -- bash -c 'sleep {}; echo {}'"

通常情况下,您希望使用较长的TTL和较短的过期时间,这样即使您让 fzf 运行一段时间,缓存也能保持活跃并在后台刷新。如果您希望在下一次调用时使缓存失效,也可以设置一个 --scope

请参阅 这个讨论,以获取更完整的使用 bktfzf 的示例,包括在用户开始导航选择器之前预热命令。

仅在安装 bkt 时使用

您可能想要分发使用 bkt 的shell脚本,而无需每个用户都安装 bkt。通过将 bkt 包装在shell函数中,您的脚本可以在 bkt 可用的情况下干净地调用它,而不会复杂化用户的流程。当然,如果他们选择安装 bkt,他们将以更快的脚本作为结果!

# Cache commands using bkt if installed
if command -v bkt >&/dev/null; then
  bkt() { command bkt "$@"; }
else
  # If bkt isn't installed skip its arguments and just execute directly.
  # Optionally write a msg to stderr suggesting users install bkt.
  bkt() {
    while [[ "$1" == --* ]]; do shift; done
    "$@"
  }
fi

# Now you can call bkt (the function) just like you'd call bkt (the binary):
bkt -- expensive_cmd ...

在shell脚本中使用 bkt 装饰命令

有时在shell脚本或您的shell环境中缓存一个命令的所有调用是有帮助的。您可以使用类似于 bash-cache 的装饰函数模式来透明地启用缓存,如下所示

# This is Bash syntax, but other shells support similar syntax
expensive_cmd() {
  bkt [bkt args ...] -- expensive_cmd "$@"
}

现在,对shell中的 expensive_cmd 的调用将通过 bkt 在幕后进行。这可以用于简洁和一致性,但显然改变这种行为是双刃剑,所以请谨慎使用。如果您需要绕过单个调用的缓存,Bash 提供了 command 内置命令,因此 command expensive_cmd ... 将直接调用 expensive_cmd。其他shell也提供了类似的功能。

依赖关系

~4–13MB
~166K SLoC