Class: OsCtld::GarbageCollector

Inherits:
Object
  • Object
show all
Includes:
OsCtl::Lib::Utils::File, OsCtl::Lib::Utils::Log, Lockable
Defined in:
lib/osctld/garbage_collector.rb

Overview

Ensures that temporary datasets are moved to trash after they’re not needed

It can be used to tie datasets to specific container runs. Add dataset using #add_container_run_dataset and it will be kept as long as the same run configuration is active. When the container is stopped, restarted, or #free_container_run_dataset is called, the dataset is moved to the trash bin.

Defined Under Namespace

Classes: ContainerRunDataset

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Lockable

#exclusively, included, #inclusively, #init_lock, #lock, #unlock

Constructor Details

#initialize(pool) ⇒ GarbageCollector

Returns a new instance of GarbageCollector.

Parameters:



72
73
74
75
76
77
78
79
80
# File 'lib/osctld/garbage_collector.rb', line 72

def initialize(pool)
  init_lock
  @pool = pool
  @config_path = File.join(pool.conf_path, 'pool', 'garbage-collector.yml')
  @queue = OsCtl::Lib::Queue.new
  @stop = false

  load_config
end

Instance Attribute Details

#poolPool (readonly)

Returns:



69
70
71
# File 'lib/osctld/garbage_collector.rb', line 69

def pool
  @pool
end

Class Method Details

.add_container_run_dataset(run_conf, dataset) ⇒ Object

Parameters:



54
55
56
# File 'lib/osctld/garbage_collector.rb', line 54

def self.add_container_run_dataset(run_conf, dataset)
  run_conf.pool.garbage_collector.add_container_run_dataset(run_conf, dataset)
end

.free_container_run_dataset(run_conf, dataset) ⇒ Object

Parameters:



60
61
62
# File 'lib/osctld/garbage_collector.rb', line 60

def self.free_container_run_dataset(run_conf, dataset)
  run_conf.pool.garbage_collector.free_container_run_dataset(run_conf, dataset)
end

Instance Method Details

#add_container_run_dataset(run_conf, dataset) ⇒ Object

Parameters:



114
115
116
117
118
119
# File 'lib/osctld/garbage_collector.rb', line 114

def add_container_run_dataset(run_conf, dataset)
  exclusively do
    @container_run_datasets << ContainerRunDataset.new(run_conf.run_id, dataset)
    save_config
  end
end

#assets(add) ⇒ Object



82
83
84
85
86
87
88
# File 'lib/osctld/garbage_collector.rb', line 82

def assets(add)
  add.file(
    @config_path,
    desc: 'Configuration file for garbage collector',
    optional: true
  )
end

#container_run_dataset_in_use?(ct_run_ds) ⇒ Boolean (protected)

Returns:

  • (Boolean)


210
211
212
213
214
215
216
217
218
219
# File 'lib/osctld/garbage_collector.rb', line 210

def container_run_dataset_in_use?(ct_run_ds)
  ct = DB::Containers.find(ct_run_ds.container_id, ct_run_ds.pool_name)
  return false if ct.nil?

  run_conf = ct.run_conf
  next_run_conf = ct.next_run_conf

  (run_conf && run_conf.run_id == ct_run_ds.run_id) \
    || (next_run_conf && next_run_conf.run_id == ct_run_ds.run_id)
end

#do_free_container_run_dataset(ct_run_ds) ⇒ Object (protected)



196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/osctld/garbage_collector.rb', line 196

def do_free_container_run_dataset(ct_run_ds)
  # It may be that the dataset wasn't previously registered within the GC.
  # It could be a dataset that existed before GC support was introduced. In any
  # case, the intent is to delete the dataset and so we do not perform a lookup
  # in @container_run_datasets.
  log(:info, "Moving #{ct_run_ds.dataset} to trash")
  return unless trash_dataset(ct_run_ds.dataset)

  exclusively do
    @container_run_datasets.delete(ct_run_ds)
    save_config
  end
end

#free_container_run_dataset(run_conf, dataset) ⇒ Object

Parameters:



123
124
125
# File 'lib/osctld/garbage_collector.rb', line 123

def free_container_run_dataset(run_conf, dataset)
  @queue << [:free_container_run_dataset, ContainerRunDataset.new(run_conf.run_id, dataset)]
end

#load_configObject (protected)



133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/osctld/garbage_collector.rb', line 133

def load_config
  @container_run_datasets = []

  begin
    cfg = OsCtl::Lib::ConfigFile.load_yaml_file(@config_path)
  rescue Errno::ENOENT
    return
  end

  @container_run_datasets = cfg.fetch('container_run_datasets', []).map do |ct_run_ds_cfg|
    ContainerRunDataset.load(ct_run_ds_cfg)
  end
end

#log_typeObject



127
128
129
# File 'lib/osctld/garbage_collector.rb', line 127

def log_type
  "#{pool.name}:gc"
end

#pruneObject



108
109
110
# File 'lib/osctld/garbage_collector.rb', line 108

def prune
  @queue << :prune
end

#prune_container_run_datasetsObject (protected)



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/osctld/garbage_collector.rb', line 175

def prune_container_run_datasets
  unused =
    inclusively do
      @container_run_datasets.reject do |ct_run_ds|
        container_run_dataset_in_use?(ct_run_ds)
      end
    end

  unused.each do |ct_run_ds|
    break if @stop

    log(:info, "Container dataset #{ct_run_ds.dataset} from run=#{ct_run_ds.run_id} is no longer in use, moving to trash")
    next unless trash_dataset(ct_run_ds.dataset)

    exclusively do
      @container_run_datasets.delete(ct_run_ds)
      save_config
    end
  end
end

#run_gcObject (protected)



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/osctld/garbage_collector.rb', line 157

def run_gc
  loop do
    v = @queue.pop(timeout: Daemon.get.config.garbage_collector.prune_interval)

    case v
    in :stop
      return

    in :prune | nil
      log(:info, 'Pruning container run datasets')
      prune_container_run_datasets

    in [:free_container_run_dataset, ct_run_ds]
      do_free_container_run_dataset(ct_run_ds)
    end
  end
end

#save_configObject (protected)



147
148
149
150
151
152
153
154
155
# File 'lib/osctld/garbage_collector.rb', line 147

def save_config
  exclusively do
    regenerate_file(@config_path, 0o400) do |f|
      f.write(OsCtl::Lib::ConfigFile.dump_yaml({
        'container_run_datasets' => @container_run_datasets.map(&:dump)
      }))
    end
  end
end

#startObject



90
91
92
93
# File 'lib/osctld/garbage_collector.rb', line 90

def start
  @stop = false
  @thread = Thread.new { run_gc }
end

#started?Boolean

Returns:

  • (Boolean)


104
105
106
# File 'lib/osctld/garbage_collector.rb', line 104

def started?
  !@stop
end

#stopObject



95
96
97
98
99
100
101
102
# File 'lib/osctld/garbage_collector.rb', line 95

def stop
  return unless @thread

  @stop = true
  @queue << :stop
  @thread.join
  @thread = nil
end

#trash_dataset(dataset) ⇒ Boolean (protected)

Returns true if the dataset was moved to trash or if it doesn’t exist.

Returns:

  • (Boolean)

    true if the dataset was moved to trash or if it doesn’t exist



222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/osctld/garbage_collector.rb', line 222

def trash_dataset(dataset)
  pool.trash_bin.add_dataset(dataset)
  true
rescue SystemCommandFailed => e
  if dataset.exist?
    log(:warn, "Unable to trash dataset '#{dataset}': #{e.message}")
    false
  else
    log(:warn, "Attempted to trash a non-existent dataset '#{dataset}' (original error: #{e.message})")
    true
  end
end