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 "dbm"
|
||||||
require "json"
|
require "json"
|
||||||
|
require "timeout"
|
||||||
|
|
||||||
#
|
#
|
||||||
# `CacheStoreDatabase` acts as an interface to a persistent storage mechanism
|
# `CacheStoreDatabase` acts as an interface to a persistent storage mechanism
|
||||||
@ -46,7 +47,7 @@ class CacheStoreDatabase
|
|||||||
#
|
#
|
||||||
# @return [Boolean]
|
# @return [Boolean]
|
||||||
def created?
|
def created?
|
||||||
File.exist?(cache_path)
|
cache_path.exist?
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@ -57,6 +58,10 @@ class CacheStoreDatabase
|
|||||||
# https://docs.oracle.com/cd/E17276_01/html/api_reference/C/envopen.html
|
# https://docs.oracle.com/cd/E17276_01/html/api_reference/C/envopen.html
|
||||||
DATABASE_MODE = 0664
|
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
|
# Lazily loaded database in read/write mode. If this method is called, a
|
||||||
# database file with be created in the `HOMEBREW_CACHE` with name
|
# database file with be created in the `HOMEBREW_CACHE` with name
|
||||||
# corresponding to the `@type` instance variable
|
# corresponding to the `@type` instance variable
|
||||||
@ -66,6 +71,29 @@ class CacheStoreDatabase
|
|||||||
# DBM::WRCREAT: Creates the database if it does not already exist
|
# DBM::WRCREAT: Creates the database if it does not already exist
|
||||||
@db ||= begin
|
@db ||= begin
|
||||||
HOMEBREW_CACHE.mkpath
|
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)
|
DBM.open(dbm_file_path, DATABASE_MODE, DBM::WRCREAT)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -83,7 +111,7 @@ class CacheStoreDatabase
|
|||||||
#
|
#
|
||||||
# @return [String]
|
# @return [String]
|
||||||
def dbm_file_path
|
def dbm_file_path
|
||||||
File.join(HOMEBREW_CACHE, @type.to_s)
|
"#{HOMEBREW_CACHE}/#{@type}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# The path where the database resides in the `HOMEBREW_CACHE` for the given
|
# The path where the database resides in the `HOMEBREW_CACHE` for the given
|
||||||
@ -91,7 +119,7 @@ class CacheStoreDatabase
|
|||||||
#
|
#
|
||||||
# @return [String]
|
# @return [String]
|
||||||
def cache_path
|
def cache_path
|
||||||
"#{dbm_file_path}.db"
|
Pathname("#{dbm_file_path}.db")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,8 @@ end
|
|||||||
class SystemCommand
|
class SystemCommand
|
||||||
extend Predicable
|
extend Predicable
|
||||||
|
|
||||||
|
attr_reader :pid
|
||||||
|
|
||||||
def self.run(executable, **options)
|
def self.run(executable, **options)
|
||||||
new(executable, **options).run!
|
new(executable, **options).run!
|
||||||
end
|
end
|
||||||
@ -122,6 +124,7 @@ class SystemCommand
|
|||||||
|
|
||||||
raw_stdin, raw_stdout, raw_stderr, raw_wait_thr =
|
raw_stdin, raw_stdout, raw_stderr, raw_wait_thr =
|
||||||
Open3.popen3(env, [executable, executable], *args, **options)
|
Open3.popen3(env, [executable, executable], *args, **options)
|
||||||
|
@pid = raw_wait_thr.pid
|
||||||
|
|
||||||
write_input_to(raw_stdin)
|
write_input_to(raw_stdin)
|
||||||
raw_stdin.close_write
|
raw_stdin.close_write
|
||||||
@ -191,6 +194,7 @@ class SystemCommand
|
|||||||
end
|
end
|
||||||
|
|
||||||
def success?
|
def success?
|
||||||
|
return false if @exit_status.nil?
|
||||||
@exit_status.zero?
|
@exit_status.zero?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -118,15 +118,15 @@ describe CacheStoreDatabase do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe "#created?" do
|
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
|
before(:each) do
|
||||||
allow(subject).to receive(:cache_path).and_return(cache_path)
|
allow(subject).to receive(:cache_path).and_return(cache_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
context "`File.exist?(cache_path)` returns `true`" do
|
context "`cache_path.exist?` returns `true`" do
|
||||||
before(:each) 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
|
end
|
||||||
|
|
||||||
it "returns `true`" do
|
it "returns `true`" do
|
||||||
@ -134,9 +134,9 @@ describe CacheStoreDatabase do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "`File.exist?(cache_path)` returns `false`" do
|
context "`cache_path.exist?` returns `false`" do
|
||||||
before(:each) 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
|
end
|
||||||
|
|
||||||
it "returns `false`" do
|
it "returns `false`" do
|
||||||
|
|||||||
@ -47,7 +47,7 @@ setup-ruby-path() {
|
|||||||
then
|
then
|
||||||
odie "Failed to install vendor Ruby."
|
odie "Failed to install vendor Ruby."
|
||||||
fi
|
fi
|
||||||
rm -rf "$vendor_dir/bundle/ruby" "$HOMEBREW_CACHE/linkage.db"
|
rm -rf "$vendor_dir/bundle/ruby"
|
||||||
HOMEBREW_RUBY_PATH="$vendor_ruby_path"
|
HOMEBREW_RUBY_PATH="$vendor_ruby_path"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user