diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index f04436b7f..1ce62aeaf 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -195,7 +195,7 @@ void checkStoreNotSymlink() } -LocalStore::LocalStore() +LocalStore::LocalStore(bool reserveSpace) { substitutablePathsLoaded = false; @@ -221,6 +221,24 @@ LocalStore::LocalStore() 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 schema upgrade is in progress. */ try { diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 8e3cbe5ce..0ff3e7b39 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -91,7 +91,7 @@ public: /* Initialise the local store, upgrading the schema if necessary. */ - LocalStore(); + LocalStore(bool reserveSpace = true); ~LocalStore(); diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 0fd759b07..cbcf86054 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -43,7 +43,7 @@ RemoteStore::RemoteStore() } -void RemoteStore::openConnection() +void RemoteStore::openConnection(bool reserveSpace) { if (initialised) return; initialised = true; @@ -75,6 +75,8 @@ void RemoteStore::openConnection() if (GET_PROTOCOL_MAJOR(daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION)) throw Error("Nix daemon protocol version not supported"); writeInt(PROTOCOL_VERSION, to); + if (GET_PROTOCOL_MINOR(daemonVersion) >= 11) + writeInt(reserveSpace, to); processStderr(); } catch (Error & e) { @@ -462,7 +464,7 @@ Roots RemoteStore::findRoots() void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results) { - openConnection(); + openConnection(false); writeInt(wopCollectGarbage, to); writeInt(options.action, to); diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index c5853ef53..823e694dd 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -86,7 +86,7 @@ private: unsigned int daemonVersion; bool initialised; - void openConnection(); + void openConnection(bool reserveSpace = true); void processStderr(Sink * sink = 0, Source * source = 0); diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 19bc048ab..b64988268 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -322,10 +322,10 @@ namespace nix { boost::shared_ptr store; -boost::shared_ptr openStore() +boost::shared_ptr openStore(bool reserveSpace) { if (getEnv("NIX_REMOTE") == "") - return boost::shared_ptr(new LocalStore()); + return boost::shared_ptr(new LocalStore(reserveSpace)); else return boost::shared_ptr(new RemoteStore()); } diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index a62a64816..fa766d12e 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -327,7 +327,7 @@ extern boost::shared_ptr store; /* Factory method: open the Nix database, either through the local or remote implementation. */ -boost::shared_ptr openStore(); +boost::shared_ptr openStore(bool reserveSpace = true); /* Display a set of paths in human-readable form (i.e., between quotes diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh index 6e0aadad4..c1ea7f758 100644 --- a/src/libstore/worker-protocol.hh +++ b/src/libstore/worker-protocol.hh @@ -8,7 +8,7 @@ namespace nix { #define WORKER_MAGIC_1 0x6e697863 #define WORKER_MAGIC_2 0x6478696f -#define PROTOCOL_VERSION 0x10a +#define PROTOCOL_VERSION 0x10b #define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00) #define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff) diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index fa96725b1..36c041682 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -843,7 +843,7 @@ void run(Strings args) if (!op) throw UsageError("no operation specified"); if (op != opDump && op != opRestore) /* !!! hack */ - store = openStore(); + store = openStore(op != opGC); op(opFlags, opArgs); } diff --git a/src/nix-worker/nix-worker.cc b/src/nix-worker/nix-worker.cc index c69b9de50..b7bce20bd 100644 --- a/src/nix-worker/nix-worker.cc +++ b/src/nix-worker/nix-worker.cc @@ -625,8 +625,12 @@ static void processConnection() throw Error("if you run `nix-worker' as root, then you MUST set `build-users-group'!"); #endif + bool reserveSpace = true; + if (GET_PROTOCOL_MINOR(clientVersion) >= 11) + reserveSpace = readInt(from) != 0; + /* Open the store. */ - store = boost::shared_ptr(new LocalStore()); + store = boost::shared_ptr(new LocalStore(reserveSpace)); stopWork(); to.flush();