Reserve some disk space for the garbage collector

We can't open a SQLite database if the disk is full.  Since this
prevents the garbage collector from running when it's most needed, we
reserve some dummy space that we can free just before doing a garbage
collection.  This actually revives some old code from the Berkeley DB
days.

Fixes #27.
This commit is contained in:
Eelco Dolstra 2012-05-29 22:59:12 -04:00
parent 2c26985835
commit 4bc4da331a
9 changed files with 35 additions and 11 deletions

View file

@ -195,7 +195,7 @@ void checkStoreNotSymlink()
} }
LocalStore::LocalStore() LocalStore::LocalStore(bool reserveSpace)
{ {
substitutablePathsLoaded = false; substitutablePathsLoaded = false;
@ -221,6 +221,24 @@ LocalStore::LocalStore()
checkStoreNotSymlink(); checkStoreNotSymlink();
/* We can't open a SQLite database if the disk is full. Since
this prevents the garbage collector from running when it's most
needed, we reserve some dummy space that we can free just
before doing a garbage collection. */
try {
Path reservedPath = nixDBPath + "/reserved";
if (reserveSpace) {
int reservedSize = queryIntSetting("gc-reserved-space", 1024 * 1024);
struct stat st;
if (stat(reservedPath.c_str(), &st) == -1 ||
st.st_size != reservedSize)
writeFile(reservedPath, string(reservedSize, 'X'));
}
else
deletePath(reservedPath);
} catch (SysError & e) { /* don't care about errors */
}
/* Acquire the big fat lock in shared mode to make sure that no /* Acquire the big fat lock in shared mode to make sure that no
schema upgrade is in progress. */ schema upgrade is in progress. */
try { try {

View file

@ -91,7 +91,7 @@ public:
/* Initialise the local store, upgrading the schema if /* Initialise the local store, upgrading the schema if
necessary. */ necessary. */
LocalStore(); LocalStore(bool reserveSpace = true);
~LocalStore(); ~LocalStore();

View file

@ -43,7 +43,7 @@ RemoteStore::RemoteStore()
} }
void RemoteStore::openConnection() void RemoteStore::openConnection(bool reserveSpace)
{ {
if (initialised) return; if (initialised) return;
initialised = true; initialised = true;
@ -75,6 +75,8 @@ void RemoteStore::openConnection()
if (GET_PROTOCOL_MAJOR(daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION)) if (GET_PROTOCOL_MAJOR(daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION))
throw Error("Nix daemon protocol version not supported"); throw Error("Nix daemon protocol version not supported");
writeInt(PROTOCOL_VERSION, to); writeInt(PROTOCOL_VERSION, to);
if (GET_PROTOCOL_MINOR(daemonVersion) >= 11)
writeInt(reserveSpace, to);
processStderr(); processStderr();
} }
catch (Error & e) { catch (Error & e) {
@ -462,7 +464,7 @@ Roots RemoteStore::findRoots()
void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results) void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results)
{ {
openConnection(); openConnection(false);
writeInt(wopCollectGarbage, to); writeInt(wopCollectGarbage, to);
writeInt(options.action, to); writeInt(options.action, to);

View file

@ -86,7 +86,7 @@ private:
unsigned int daemonVersion; unsigned int daemonVersion;
bool initialised; bool initialised;
void openConnection(); void openConnection(bool reserveSpace = true);
void processStderr(Sink * sink = 0, Source * source = 0); void processStderr(Sink * sink = 0, Source * source = 0);

View file

@ -322,10 +322,10 @@ namespace nix {
boost::shared_ptr<StoreAPI> store; boost::shared_ptr<StoreAPI> store;
boost::shared_ptr<StoreAPI> openStore() boost::shared_ptr<StoreAPI> openStore(bool reserveSpace)
{ {
if (getEnv("NIX_REMOTE") == "") if (getEnv("NIX_REMOTE") == "")
return boost::shared_ptr<StoreAPI>(new LocalStore()); return boost::shared_ptr<StoreAPI>(new LocalStore(reserveSpace));
else else
return boost::shared_ptr<StoreAPI>(new RemoteStore()); return boost::shared_ptr<StoreAPI>(new RemoteStore());
} }

View file

@ -327,7 +327,7 @@ extern boost::shared_ptr<StoreAPI> store;
/* Factory method: open the Nix database, either through the local or /* Factory method: open the Nix database, either through the local or
remote implementation. */ remote implementation. */
boost::shared_ptr<StoreAPI> openStore(); boost::shared_ptr<StoreAPI> openStore(bool reserveSpace = true);
/* Display a set of paths in human-readable form (i.e., between quotes /* Display a set of paths in human-readable form (i.e., between quotes

View file

@ -8,7 +8,7 @@ namespace nix {
#define WORKER_MAGIC_1 0x6e697863 #define WORKER_MAGIC_1 0x6e697863
#define WORKER_MAGIC_2 0x6478696f #define WORKER_MAGIC_2 0x6478696f
#define PROTOCOL_VERSION 0x10a #define PROTOCOL_VERSION 0x10b
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00) #define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff) #define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)

View file

@ -843,7 +843,7 @@ void run(Strings args)
if (!op) throw UsageError("no operation specified"); if (!op) throw UsageError("no operation specified");
if (op != opDump && op != opRestore) /* !!! hack */ if (op != opDump && op != opRestore) /* !!! hack */
store = openStore(); store = openStore(op != opGC);
op(opFlags, opArgs); op(opFlags, opArgs);
} }

View file

@ -625,8 +625,12 @@ static void processConnection()
throw Error("if you run `nix-worker' as root, then you MUST set `build-users-group'!"); throw Error("if you run `nix-worker' as root, then you MUST set `build-users-group'!");
#endif #endif
bool reserveSpace = true;
if (GET_PROTOCOL_MINOR(clientVersion) >= 11)
reserveSpace = readInt(from) != 0;
/* Open the store. */ /* Open the store. */
store = boost::shared_ptr<StoreAPI>(new LocalStore()); store = boost::shared_ptr<StoreAPI>(new LocalStore(reserveSpace));
stopWork(); stopWork();
to.flush(); to.flush();