mirror of
https://github.com/btcsuite/btcd.git
synced 2025-03-26 18:08:42 +01:00
database: Prevent write transaction with low disk space
Currently the user is not informed of issues that are due to low disk space (such as block processing failures and resulting orphaning blocks). Instead, check for available disk space before starting a write transaction to preventively fail and inform the user of the problem.
This commit is contained in:
parent
2be2f12b35
commit
203a9130fc
5 changed files with 89 additions and 0 deletions
|
@ -62,6 +62,11 @@ const (
|
|||
// the database was attempted against a read-only transaction.
|
||||
ErrTxNotWritable
|
||||
|
||||
// ErrAvailableDiskSpace indicates that the user is running out of
|
||||
// disk space. The database preventively decided to not allow the
|
||||
// transaction to prevent causing hard-to-detect problems.
|
||||
ErrAvailableDiskSpace
|
||||
|
||||
// **************************************
|
||||
// Errors related to metadata operations.
|
||||
// **************************************
|
||||
|
@ -143,6 +148,7 @@ var errorCodeStrings = map[ErrorCode]string{
|
|||
ErrCorruption: "ErrCorruption",
|
||||
ErrTxClosed: "ErrTxClosed",
|
||||
ErrTxNotWritable: "ErrTxNotWritable",
|
||||
ErrAvailableDiskSpace: "ErrAvailableDiskSpace",
|
||||
ErrBucketNotFound: "ErrBucketNotFound",
|
||||
ErrBucketExists: "ErrBucketExists",
|
||||
ErrBucketNameRequired: "ErrBucketNameRequired",
|
||||
|
|
|
@ -27,6 +27,7 @@ func TestErrorCodeStringer(t *testing.T) {
|
|||
{database.ErrCorruption, "ErrCorruption"},
|
||||
{database.ErrTxClosed, "ErrTxClosed"},
|
||||
{database.ErrTxNotWritable, "ErrTxNotWritable"},
|
||||
{database.ErrAvailableDiskSpace, "ErrAvailableDiskSpace"},
|
||||
{database.ErrBucketNotFound, "ErrBucketNotFound"},
|
||||
{database.ErrBucketExists, "ErrBucketExists"},
|
||||
{database.ErrBucketNameRequired, "ErrBucketNameRequired"},
|
||||
|
|
|
@ -43,6 +43,13 @@ const (
|
|||
// The serialized block index row format is:
|
||||
// <blocklocation><blockheader>
|
||||
blockHdrOffset = blockLocSize
|
||||
|
||||
// bytesMiB is the number of bytes in a mebibyte.
|
||||
bytesMiB = 1024 * 1024
|
||||
|
||||
// minAvailableSpaceUpdate is the minimum space available (in bytes) to
|
||||
// allow a write transaction. The value is 25 MiB.
|
||||
minAvailableSpaceUpdate = 25 * bytesMiB
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -1752,6 +1759,22 @@ func (db *db) Type() string {
|
|||
// which is used by the managed transaction code while the database method
|
||||
// returns the interface.
|
||||
func (db *db) begin(writable bool) (*transaction, error) {
|
||||
// Make sure there is enough available disk space so we can inform the
|
||||
// user of the problem instead of causing a db failure.
|
||||
if writable {
|
||||
freeSpace, err := getAvailableDiskSpace()
|
||||
if err != nil {
|
||||
return nil, makeDbErr(database.ErrDriverSpecific,
|
||||
"failed to inspect available disk space", err)
|
||||
}
|
||||
if freeSpace < minAvailableSpaceUpdate {
|
||||
errMsg := fmt.Sprintf("available disk space too low: "+
|
||||
"%.1f MiB", float64(freeSpace)/float64(bytesMiB))
|
||||
return nil, makeDbErr(database.ErrAvailableDiskSpace,
|
||||
errMsg, nil)
|
||||
}
|
||||
}
|
||||
|
||||
// Whenever a new writable transaction is started, grab the write lock
|
||||
// to ensure only a single write transaction can be active at the same
|
||||
// time. This lock will not be released until the transaction is
|
||||
|
|
26
database/ffldb/disk_posix.go
Normal file
26
database/ffldb/disk_posix.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
// +build !windows
|
||||
|
||||
// Copyright (c) 2013-2018 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ffldb
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// getAvailableDiskSpace returns the number of bytes of available disk space.
|
||||
func getAvailableDiskSpace() (uint64, error) {
|
||||
var stat syscall.Statfs_t
|
||||
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
syscall.Statfs(wd, &stat)
|
||||
|
||||
return stat.Bavail * uint64(stat.Bsize), nil
|
||||
}
|
33
database/ffldb/disk_windows.go
Normal file
33
database/ffldb/disk_windows.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
// +build windows
|
||||
|
||||
// Copyright (c) 2013-2018 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ffldb
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// getAvailableDiskSpace returns the number of bytes of available disk space.
|
||||
func getAvailableDiskSpace() (uint64, error) {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
h := syscall.MustLoadDLL("kernel32.dll")
|
||||
c := h.MustFindProc("GetDiskFreeSpaceExW")
|
||||
|
||||
var freeBytes int64
|
||||
_, _, err := c.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(wd))),
|
||||
uintptr(unsafe.Pointer(&freeBytes)), nil, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint64(freeBytes), nil
|
||||
}
|
Loading…
Add table
Reference in a new issue