Skip to content

Commit

Permalink
Minimize tempfile churn (#186)
Browse files Browse the repository at this point in the history
Closes #185.

Try was making way too many tempfiles. Now we store everything in the sandbox, with a tiny bit of nuance: $IGNORE_FILE needs to be created in advance to handle the args properly. So we create that temporary unconditionally. When running a command (the try() function), we'll move $IGNORE_FILE into the sandbox. When running try commit or try summary, we just delete $IGNORE_FILE at the end.
  • Loading branch information
mgree authored Nov 20, 2024
1 parent 872845f commit b63d1e7
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 11 deletions.
30 changes: 30 additions & 0 deletions test/tempfiles.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/sh
# shellcheck disable=SC2010,SC2126,SC2181

TRY_TOP="${TRY_TOP:-$(git rev-parse --show-toplevel --show-superproject-working-tree 2>/dev/null || echo "${0%/*}")}"
TRY="$TRY_TOP/try"

workdir="$(mktemp -d)"
cd "$workdir" || exit 1

initial_count="$(ls "${TMPDIR-/tmp}" | grep -e "^.*\.try-[0-9]*$" | wc -l)"

sandbox=$($TRY -n "touch $HOME/foo")
[ $? -eq 0 ] || exit 2

post_count="$(ls "${TMPDIR-/tmp}" | grep -e "^.*\.try-[0-9]*$" | wc -l)"

# just one new tempfile
[ "$((initial_count + 1))" -eq "$post_count" ] || exit 3
[ -f "$sandbox/upperdir$HOME/foo" ] || exit 4

# deliberately not the pattern of try sandboxes
sandbox=local
mkdir "$sandbox" || exit 5
$TRY -D "$sandbox" "touch $HOME/bar" || exit 6

final_count="$(ls "${TMPDIR-/tmp}" | grep -e "^.*\.try-[0-9]*$" | wc -l)"

# no new tempfiles!
[ "$post_count" -eq "$final_count" ] || exit 7
[ -f "$sandbox/upperdir$HOME/bar" ] || exit 8
39 changes: 28 additions & 11 deletions try
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,15 @@ try() {
if [ "$SANDBOX_DIR" ]
then
## If the name of a sandbox is given then we need to exit prematurely if its directory doesn't exist
! [ -d "$SANDBOX_DIR" ] && { error "could not find sandbox directory $SANDBOX_DIR" 2; }
[ -d "$SANDBOX_DIR" ] || error "could not find sandbox directory $SANDBOX_DIR" 2
# Force absolute path
SANDBOX_DIR="$(cd "$SANDBOX_DIR" && pwd)"

# shellcheck disable=SC2181
[ "$?" -eq 0 ] || error "could not find sandbox directory $SANDBOX_DIR (could not cd in)" 2
else
## Create a new sandbox if one was not given
SANDBOX_DIR=$(mktemp -d --suffix ".try-$EXECID")
SANDBOX_DIR="$(mktemp -d --suffix ".try-$EXECID")"
fi

## If the sandbox is not valid we exit early
Expand All @@ -50,7 +55,11 @@ try() {
## because we have already checked if it valid.
export SANDBOX_DIR

try_mount_log="$(mktemp --suffix ".try-$EXECID")"
# We created "$IGNORE_FILE" up front, but now we can stash it in the sandbox.
mv "$IGNORE_FILE" "$SANDBOX_DIR"/ignore
IGNORE_FILE="$SANDBOX_DIR"/ignore

try_mount_log="$SANDBOX_DIR"/mount.log
export try_mount_log

# If we're in a docker container, we want to mount tmpfs on sandbox_dir, #136
Expand All @@ -66,14 +75,14 @@ try() {
mkdir -p "$SANDBOX_DIR/upperdir" "$SANDBOX_DIR/workdir" "$SANDBOX_DIR/temproot"

## Find all the directories and mounts that need to be mounted
DIRS_AND_MOUNTS="$(mktemp --suffix ".try-$EXECID")"
DIRS_AND_MOUNTS="$SANDBOX_DIR"/mounts
export DIRS_AND_MOUNTS
find / -maxdepth 1 >"$DIRS_AND_MOUNTS"
findmnt --real -r -o target -n >>"$DIRS_AND_MOUNTS"
sort -u -o "$DIRS_AND_MOUNTS" "$DIRS_AND_MOUNTS"

# Calculate UPDATED_DIRS_AND_MOUNTS that contains the merge arguments in LOWER_DIRS
UPDATED_DIRS_AND_MOUNTS="$(mktemp --suffix ".try-$EXECID")"
UPDATED_DIRS_AND_MOUNTS="$SANDBOX_DIR"/mounts.updated
export UPDATED_DIRS_AND_MOUNTS
while IFS="" read -r mountpoint
do
Expand Down Expand Up @@ -124,9 +133,9 @@ try() {

chmod "$(stat -c %a /)" "$SANDBOX_DIR/temproot"

mount_and_execute="$(mktemp --suffix ".try-$EXECID")"
chroot_executable="$(mktemp --suffix ".try-$EXECID")"
script_to_execute="$(mktemp --suffix ".try-$EXECID")"
mount_and_execute="$SANDBOX_DIR"/mount_and_execute.sh
chroot_executable="$SANDBOX_DIR"/chroot_executable.sh
script_to_execute="$SANDBOX_DIR"/script_to_execute.sh

export chroot_executable
export script_to_execute
Expand Down Expand Up @@ -227,7 +236,8 @@ do
## We can ignore this mountpoint, if the user program tries to use it, it will crash, but if not we can run normally
printf "%s: Warning: Failed mounting $mountpoint as an overlay and mergerfs or unionfs not set and could not be found, see \"$try_mount_log\"\n" "$TRY_COMMAND" >&2
else
merger_dir=$(mktemp -d --suffix ".try-$EXECID")
merger_dir="$SANDBOX_DIR"/mergerdir."$(echo "$pure_mountpoint" | tr '/' '.')"
mkdir "$merger_dir"
## Create a union directory
"$UNION_HELPER" $mountpoint $merger_dir 2>>"$try_mount_log" ||
Expand Down Expand Up @@ -612,6 +622,9 @@ EOF
NO_COMMIT="interactive"

# Includes all patterns given using the `-i` flag; will be used with `grep -f`
#
# We have to create this temporary up front.
# We move it to $SANDBOX_DIR/ignore in `try()`, but delete it when we don't move it.
IGNORE_FILE="$(mktemp --suffix ".try-$EXECID")"

while getopts ":yvnhxi:D:U:L:" opt
Expand Down Expand Up @@ -657,9 +670,13 @@ fi
TRY_EXIT_STATUS=1
case "$1" in
(summary) : "${SANDBOX_DIR=$2}"
summary;;
summary
rm "$IGNORE_FILE" # we didn't move it to the sandbox, so clean up
;;
(commit) : "${SANDBOX_DIR=$2}"
commit;;
commit
rm "$IGNORE_FILE" # we didn't move it to the sandbox, so clean up
;;
(explore) : "${SANDBOX_DIR=$2}"
try "$SHELL";;
(--) shift
Expand Down

0 comments on commit b63d1e7

Please sign in to comment.