Feature request: update cache synchronously
This is really awesome. Thank you for making this!
I found your repo while searching for something that can fulfill my use case, and bkt comes very close, but not quite. Seeing as you mentioned fzf in the readme, I figured this feature might be useful for you and others too, so creating a request here.
I have a command foo that takes about 1s to complete. So running foo | fzf does not give a very smooth experience.
bkt --ttl=1y --warm -- foo | fzf is much better, but if the output of foo changes, I need to run the command twice in order to see the new lines show up in fzf.
Ideally, what I would want, is for the cached content to show up in fzf immediately, and then have any changes drop in within a second or so. Usually, by the time I finish typing and having selected the entry, 1 second has already passed, so this would give a really smooth experience.
So the feature I'm hoping for is something like bkt --ttl=1y --sync-diff -- foo | fzf.
This would:
- write the cache to stdout (very fast, so at this point fzf would already be pre-populated with a cached result)
- synchronously run
foo - do a diff with the cache and write any "new" lines to stdout (this would take a second to complete, but when it does, fzf is not missing any data)
- update the cache with the latest output
I realize that if the cache contains any stale lines that do not show up in the stdout of the foo call from step 2, they would still show up in fzf, but this is not much of a problem to me. It's fine to have stale data, but missing new data is a problem.
Thank you for considering! I can also imagine if you'd not want to add this functionality, so feel free to close this!
Thanks for your feature request, I'm glad you like bkt! Sorry for the delayed reply, I was on parental leave when you posted and just coming back to things now.
Ideally, what I would want, is for the cached content to show up in fzf immediately, and then have any changes drop in within a second or so.
I think we can actually get the behavior you want out of fzf and bkt today, thanks to the reload directive (more details). Basically, we'll use bkt --ttl=x -- [cmd] as the input to fzf's stdin, and then bkt --ttl=x --force -- [cmd] in a load:reload-sync() binding, causing it to force-refresh the cache asynchronously and update the fzf selector once complete.
Here's a simple demo, supporting both a background refresh on first load and manual refreshes via ctrl+R:
# optional: isolate the scope to the script's name so we don't conflict with other usages
export BKT_SCOPE=$0-fzf
cmd=(bash -c 'for i in {0..9}; do date; sleep .5; done')
bkt --ttl=1y -- "${cmd[@]}" \
| fzf \
--bind "load:reload-sync(bkt --ttl=1y --force -- ${cmd[*]@Q})+unbind(load)" \
--bind="ctrl-r:reload(bkt --ttl=1y --force -- ${cmd[*]@Q})"
Thank you for raising this request, I had not tried to apply this pattern before! I'm going to add it to the README as an example. You may also find https://github.com/dimo414/bkt/discussions/29 interesting if you use fzf's --preview feature.
One thing that's slightly suboptimal about the above (same issue with your proposed solution) is that we run the command twice if the cache started off cold. It's ~fine, but a little wasteful. Ideally we'd do the load only if the cache wasn't just warmed a moment ago.
You almost get the right experience by passing bkt --ttl=5s (instant if cache is warm, reloads otherwise), but this has the effect of overwriting the stored TTL, meaning the next time we run the cache will likely have been invalidated again. This suggests we might want another mechanism similar to --stale but synchronous. I've filed #59 to track that idea.
Alternatively, if we implement #47 it could be used to the same effect (though it's a bit more boilerplate for callers), checking the cache age and only triggering a reload if it's stale.