Merge pull request #4949 from MikeMcQuaid/dbm_crash
cache_store: handle corrupt DBM database.
This commit is contained in:
commit
97dca89e7e
@ -1,5 +1,6 @@
|
||||
require "dbm"
|
||||
require "json"
|
||||
require "timeout"
|
||||
|
||||
#
|
||||
# `CacheStoreDatabase` acts as an interface to a persistent storage mechanism
|
||||
@ -46,7 +47,7 @@ class CacheStoreDatabase
|
||||
#
|
||||
# @return [Boolean]
|
||||
def created?
|
||||
File.exist?(cache_path)
|
||||
cache_path.exist?
|
||||
end
|
||||
|
||||
private
|
||||
@ -57,6 +58,10 @@ class CacheStoreDatabase
|
||||
# https://docs.oracle.com/cd/E17276_01/html/api_reference/C/envopen.html
|
||||
DATABASE_MODE = 0664
|
||||
|
||||
# Spend 5 seconds trying to read the DBM file. If it takes longer than this it
|
||||
# has likely hung or segfaulted.
|
||||
DBM_TEST_READ_TIMEOUT = 5
|
||||
|
||||
# Lazily loaded database in read/write mode. If this method is called, a
|
||||
# database file with be created in the `HOMEBREW_CACHE` with name
|
||||
# corresponding to the `@type` instance variable
|
||||
@ -66,6 +71,29 @@ class CacheStoreDatabase
|
||||
# DBM::WRCREAT: Creates the database if it does not already exist
|
||||
@db ||= begin
|
||||
HOMEBREW_CACHE.mkpath
|
||||
if created?
|
||||
dbm_test_read_cmd = SystemCommand.new(
|
||||
ENV["HOMEBREW_RUBY_PATH"],
|
||||
args: [
|
||||
"-rdbm",
|
||||
"-e",
|
||||
"DBM.open('#{dbm_file_path}', #{DATABASE_MODE}, DBM::READER).size",
|
||||
],
|
||||
print_stderr: false,
|
||||
must_succeed: true,
|
||||
)
|
||||
dbm_test_read_success = begin
|
||||
Timeout.timeout(DBM_TEST_READ_TIMEOUT) do
|
||||
dbm_test_read_cmd.run!
|
||||
true
|
||||
end
|
||||
rescue ErrorDuringExecution, Timeout::Error
|
||||
odebug "Failed to read #{dbm_file_path}!"
|
||||
Process.kill(:KILL, dbm_test_read_cmd.pid)
|
||||
false
|
||||
end
|
||||
cache_path.delete unless dbm_test_read_success
|
||||
end
|
||||
DBM.open(dbm_file_path, DATABASE_MODE, DBM::WRCREAT)
|
||||
end
|
||||
end
|
||||
@ -83,7 +111,7 @@ class CacheStoreDatabase
|
||||
#
|
||||
# @return [String]
|
||||
def dbm_file_path
|
||||
File.join(HOMEBREW_CACHE, @type.to_s)
|
||||
"#{HOMEBREW_CACHE}/#{@type}"
|
||||
end
|
||||
|
||||
# The path where the database resides in the `HOMEBREW_CACHE` for the given
|
||||
@ -91,7 +119,7 @@ class CacheStoreDatabase
|
||||
#
|
||||
# @return [String]
|
||||
def cache_path
|
||||
"#{dbm_file_path}.db"
|
||||
Pathname("#{dbm_file_path}.db")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -19,6 +19,8 @@ end
|
||||
class SystemCommand
|
||||
extend Predicable
|
||||
|
||||
attr_reader :pid
|
||||
|
||||
def self.run(executable, **options)
|
||||
new(executable, **options).run!
|
||||
end
|
||||
@ -122,6 +124,7 @@ class SystemCommand
|
||||
|
||||
raw_stdin, raw_stdout, raw_stderr, raw_wait_thr =
|
||||
Open3.popen3(env, [executable, executable], *args, **options)
|
||||
@pid = raw_wait_thr.pid
|
||||
|
||||
write_input_to(raw_stdin)
|
||||
raw_stdin.close_write
|
||||
@ -191,6 +194,7 @@ class SystemCommand
|
||||
end
|
||||
|
||||
def success?
|
||||
return false if @exit_status.nil?
|
||||
@exit_status.zero?
|
||||
end
|
||||
|
||||
|
||||
@ -118,15 +118,15 @@ describe CacheStoreDatabase do
|
||||
end
|
||||
|
||||
describe "#created?" do
|
||||
let(:cache_path) { "path/to/homebrew/cache/sample.db" }
|
||||
let(:cache_path) { Pathname("path/to/homebrew/cache/sample.db") }
|
||||
|
||||
before(:each) do
|
||||
allow(subject).to receive(:cache_path).and_return(cache_path)
|
||||
end
|
||||
|
||||
context "`File.exist?(cache_path)` returns `true`" do
|
||||
context "`cache_path.exist?` returns `true`" do
|
||||
before(:each) do
|
||||
allow(File).to receive(:exist?).with(cache_path).and_return(true)
|
||||
allow(cache_path).to receive(:exist?).and_return(true)
|
||||
end
|
||||
|
||||
it "returns `true`" do
|
||||
@ -134,9 +134,9 @@ describe CacheStoreDatabase do
|
||||
end
|
||||
end
|
||||
|
||||
context "`File.exist?(cache_path)` returns `false`" do
|
||||
context "`cache_path.exist?` returns `false`" do
|
||||
before(:each) do
|
||||
allow(File).to receive(:exist?).with(cache_path).and_return(false)
|
||||
allow(cache_path).to receive(:exist?).and_return(false)
|
||||
end
|
||||
|
||||
it "returns `false`" do
|
||||
|
||||
@ -47,7 +47,7 @@ setup-ruby-path() {
|
||||
then
|
||||
odie "Failed to install vendor Ruby."
|
||||
fi
|
||||
rm -rf "$vendor_dir/bundle/ruby" "$HOMEBREW_CACHE/linkage.db"
|
||||
rm -rf "$vendor_dir/bundle/ruby"
|
||||
HOMEBREW_RUBY_PATH="$vendor_ruby_path"
|
||||
fi
|
||||
fi
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user