Class: LLMExperiment::Cleaner

Inherits:
Object
  • Object
show all
Defined in:
lib/llm_experiment/cleaner.rb

Overview

Disk reclamation, planned and verified by observation.

Two measured facts shape this class. First, what an image costs is its unpacked snapshot, not the layer sizes container image inspect reports: deleting three app images returned about 16 GB against the 1.2-1.6 GB the manifests sum to. So the plan comes from du on the data directory.

Second, container builder stop can report success and do nothing. On 0.5.0, with a builder up for three days, builder stop, delete --force buildkit and system stop all exited 0 while container ls -a went on reporting the builder as running and its 6.8 GB directory stayed put. So the builder is checked afterwards, and an exit code is never the evidence.

Constant Summary collapse

UNITS =
{ "B" => 1, "K" => 1024, "M" => 1024**2, "G" => 1024**3, "T" => 1024**4 }.freeze
APP_PREFIX =
"llmx-app-"
BASE_PREFIX =
"llmx-base"
APP_IMAGE_GB =

Roughly what one app image costs unpacked, measured on 0.5.0.

5
SNAPSHOT_NOTE =
<<~NOTE
  Sizes above are the unpacked snapshot, which is what an image actually
  costs. `container image inspect` sums layers instead and reports about a
  third of it: three app images that summed to 1.2-1.6 GB each returned
  about 16 GB when deleted. Plan against this listing, not the manifest.
NOTE
RECOVERY =

Printed instead of run. The runtime line is unambiguous because it names --uuid buildkit, but the VM line is a bare Virtualization.framework XPC service that looks identical for any VM on the Mac, so a pattern-kill on the name can take out Docker, UTM, Parallels or a Simulator.

<<~TEXT
  The builder is still running. Its exit codes lied; end the VM directly:

    ps -eo pid,etime,command | grep -E 'container-runtime-linux|Virtualization' | grep -v grep
    kill -TERM <runtime-pid> <vm-pid>
    container delete --force buildkit

  Read that `ps` output before killing anything. The runtime line names
  `--uuid buildkit`, so it is unambiguous. The VM line is a bare
  `Virtualization.framework` XPC service and looks identical for ANY VM on
  this Mac (Docker, UTM, Parallels, a Simulator). Match it to the runtime
  process; never pattern-kill on the name.

  A plain TERM was enough when this happened; -9 was not needed. The
  apiserver does not need to be touched. The builder is recreated on the
  next build, which re-downloads the builder image once.
TEXT

Instance Method Summary collapse

Constructor Details

#initialize(container:, shell: Shell) ⇒ Cleaner

Returns a new instance of Cleaner.



54
55
56
57
# File 'lib/llm_experiment/cleaner.rb', line 54

def initialize(container:, shell: Shell)
  @container = container
  @shell = shell
end

Instance Method Details

#clean_builder(dry_run:) ⇒ Object

Stops the builder, then checks whether it actually stopped. The exit codes of the two commands are ignored on purpose.



100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/llm_experiment/cleaner.rb', line 100

def clean_builder(dry_run:)
  if dry_run
    puts "would run: container builder stop"
    puts "would run: container delete --force buildkit"
    puts "then verify with `container ls -a`, because those two can exit 0 and do nothing"
    return
  end

  @shell.sh("container", "builder", "stop", allow_failure: true)
  @shell.sh("container", "delete", "--force", "buildkit", allow_failure: true)
  verify_builder_gone!
end

#clean_images(dry_run:, base: false) ⇒ Object

App images only. The base image takes 20-40 minutes to rebuild, so it goes only when asked for by name.



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/llm_experiment/cleaner.rb', line 75

def clean_images(dry_run:, base: false)
  targets = app_images
  targets += base_images if base
  if targets.empty?
    puts "no llmx images to delete"
    return
  end

  if dry_run
    targets.each { |image| puts "would delete #{image} (about #{APP_IMAGE_GB} GB unpacked)" }
    puts "nothing deleted (--dry-run)"
    return
  end

  before = @container.free_gb
  targets.each do |image|
    puts "deleting #{image}"
    @container.delete_image(image)
  end
  after = @container.free_gb
  puts "free space: #{before} GB -> #{after} GB"
end

#reportObject

Never deletes anything. Shows what is on disk and what it would cost to get back.



61
62
63
64
65
66
67
68
69
70
71
# File 'lib/llm_experiment/cleaner.rb', line 61

def report
  puts "container data: #{data_dir}"
  print_disk
  puts
  print_images
  puts
  puts SNAPSHOT_NOTE
  puts "  llmx clean --builder    the BuildKit cache; regenerable, often the largest item"
  puts "  llmx clean --images     every #{APP_PREFIX}* image, about #{APP_IMAGE_GB} GB each unpacked"
  puts "  add --dry-run to either to see the list without deleting"
end