2016-04-20 15:12:38 +03:00
# include "nar-info-disk-cache.hh"
# include "sync.hh"
# include "sqlite.hh"
2018-04-02 14:45:18 +03:00
# include "globals.hh"
2016-04-20 15:12:38 +03:00
# include <sqlite3.h>
namespace nix {
static const char * schema = R " sql(
create table if not exists BinaryCaches (
id integer primary key autoincrement not null ,
url text unique not null ,
timestamp integer not null ,
storeDir text not null ,
wantMassQuery integer not null ,
priority integer not null
) ;
create table if not exists NARs (
cache integer not null ,
2016-04-21 18:53:47 +03:00
hashPart text not null ,
2016-06-20 18:39:05 +03:00
namePart text ,
2016-04-20 15:12:38 +03:00
url text ,
compression text ,
fileHash text ,
fileSize integer ,
narHash text ,
narSize integer ,
refs text ,
deriver text ,
sigs text ,
2018-12-12 02:04:34 +02:00
ca text ,
2016-04-20 15:12:38 +03:00
timestamp integer not null ,
2016-06-20 18:39:05 +03:00
present integer not null ,
2016-04-21 18:53:47 +03:00
primary key ( cache , hashPart ) ,
2016-04-20 15:12:38 +03:00
foreign key ( cache ) references BinaryCaches ( id ) on delete cascade
) ;
2017-01-27 16:19:33 +02:00
create table if not exists LastPurge (
dummy text primary key ,
value integer
) ;
2016-04-20 15:12:38 +03:00
) sql " ;
class NarInfoDiskCacheImpl : public NarInfoDiskCache
{
public :
2017-01-27 16:19:33 +02:00
/* How often to purge expired entries from the cache. */
const int purgeInterval = 24 * 3600 ;
2016-06-01 15:49:12 +03:00
struct Cache
{
int id ;
Path storeDir ;
2016-06-01 16:15:21 +03:00
bool wantMassQuery ;
int priority ;
2016-06-01 15:49:12 +03:00
} ;
2016-04-20 15:12:38 +03:00
struct State
{
SQLite db ;
2017-01-27 16:19:33 +02:00
SQLiteStmt insertCache , queryCache , insertNAR , insertMissingNAR , queryNAR , purgeCache ;
2016-06-01 15:49:12 +03:00
std : : map < std : : string , Cache > caches ;
2016-04-20 15:12:38 +03:00
} ;
Sync < State > _state ;
NarInfoDiskCacheImpl ( )
{
auto state ( _state . lock ( ) ) ;
2018-12-12 02:04:34 +02:00
Path dbPath = getCacheDir ( ) + " /nix/binary-cache-v6.sqlite " ;
2016-04-20 15:12:38 +03:00
createDirs ( dirOf ( dbPath ) ) ;
2016-08-09 15:27:30 +03:00
state - > db = SQLite ( dbPath ) ;
2016-04-20 15:12:38 +03:00
if ( sqlite3_busy_timeout ( state - > db , 60 * 60 * 1000 ) ! = SQLITE_OK )
throwSQLiteError ( state - > db , " setting timeout " ) ;
// We can always reproduce the cache.
2016-08-09 15:27:30 +03:00
state - > db . exec ( " pragma synchronous = off " ) ;
state - > db . exec ( " pragma main.journal_mode = truncate " ) ;
2016-04-20 15:12:38 +03:00
2016-08-09 15:27:30 +03:00
state - > db . exec ( schema ) ;
2016-04-20 15:12:38 +03:00
state - > insertCache . create ( state - > db ,
" insert or replace into BinaryCaches(url, timestamp, storeDir, wantMassQuery, priority) values (?, ?, ?, ?, ?) " ) ;
state - > queryCache . create ( state - > db ,
" select id, storeDir, wantMassQuery, priority from BinaryCaches where url = ? " ) ;
state - > insertNAR . create ( state - > db ,
2016-04-21 18:53:47 +03:00
" insert or replace into NARs(cache, hashPart, namePart, url, compression, fileHash, fileSize, narHash, "
2018-12-12 02:04:34 +02:00
" narSize, refs, deriver, sigs, ca, timestamp, present) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1) " ) ;
2016-06-20 18:39:05 +03:00
state - > insertMissingNAR . create ( state - > db ,
" insert or replace into NARs(cache, hashPart, timestamp, present) values (?, ?, ?, 0) " ) ;
2016-04-20 15:12:38 +03:00
state - > queryNAR . create ( state - > db ,
2018-12-12 18:12:41 +02:00
" select present, namePart, url, compression, fileHash, fileSize, narHash, narSize, refs, deriver, sigs, ca from NARs where cache = ? and hashPart = ? and ((present = 0 and timestamp > ?) or (present = 1 and timestamp > ?)) " ) ;
2017-01-27 16:19:33 +02:00
/* Periodically purge expired entries from the database. */
2017-02-28 14:44:11 +02:00
retrySQLite < void > ( [ & ] ( ) {
auto now = time ( 0 ) ;
SQLiteStmt queryLastPurge ( state - > db , " select value from LastPurge " ) ;
auto queryLastPurge_ ( queryLastPurge . use ( ) ) ;
if ( ! queryLastPurge_ . next ( ) | | queryLastPurge_ . getInt ( 0 ) < now - purgeInterval ) {
SQLiteStmt ( state - > db ,
" delete from NARs where ((present = 0 and timestamp < ?) or (present = 1 and timestamp < ?)) " )
. use ( )
2018-04-06 13:05:15 +03:00
( now - settings . ttlNegativeNarInfoCache )
( now - settings . ttlPositiveNarInfoCache )
2017-02-28 14:44:11 +02:00
. exec ( ) ;
debug ( " deleted %d entries from the NAR info disk cache " , sqlite3_changes ( state - > db ) ) ;
SQLiteStmt ( state - > db ,
" insert or replace into LastPurge(dummy, value) values ('', ?) " )
. use ( ) ( now ) . exec ( ) ;
}
} ) ;
2016-04-20 15:12:38 +03:00
}
2016-06-01 15:49:12 +03:00
Cache & getCache ( State & state , const std : : string & uri )
2016-04-20 15:12:38 +03:00
{
auto i = state . caches . find ( uri ) ;
if ( i = = state . caches . end ( ) ) abort ( ) ;
return i - > second ;
}
2016-06-01 15:49:12 +03:00
void createCache ( const std : : string & uri , const Path & storeDir , bool wantMassQuery , int priority ) override
2016-04-20 15:12:38 +03:00
{
2017-02-28 14:44:11 +02:00
retrySQLite < void > ( [ & ] ( ) {
auto state ( _state . lock ( ) ) ;
2016-04-20 15:12:38 +03:00
2017-02-28 14:44:11 +02:00
// FIXME: race
2016-04-20 15:12:38 +03:00
2017-02-28 14:44:11 +02:00
state - > insertCache . use ( ) ( uri ) ( time ( 0 ) ) ( storeDir ) ( wantMassQuery ) ( priority ) . exec ( ) ;
assert ( sqlite3_changes ( state - > db ) = = 1 ) ;
state - > caches [ uri ] = Cache { ( int ) sqlite3_last_insert_rowid ( state - > db ) , storeDir , wantMassQuery , priority } ;
} ) ;
2016-04-20 15:12:38 +03:00
}
2016-06-01 16:15:21 +03:00
bool cacheExists ( const std : : string & uri ,
bool & wantMassQuery , int & priority ) override
2016-04-20 15:12:38 +03:00
{
2017-02-28 14:44:11 +02:00
return retrySQLite < bool > ( [ & ] ( ) {
auto state ( _state . lock ( ) ) ;
2016-04-20 15:12:38 +03:00
2017-02-28 14:44:11 +02:00
auto i = state - > caches . find ( uri ) ;
if ( i = = state - > caches . end ( ) ) {
auto queryCache ( state - > queryCache . use ( ) ( uri ) ) ;
if ( ! queryCache . next ( ) ) return false ;
state - > caches . emplace ( uri ,
Cache { ( int ) queryCache . getInt ( 0 ) , queryCache . getStr ( 1 ) , queryCache . getInt ( 2 ) ! = 0 , ( int ) queryCache . getInt ( 3 ) } ) ;
}
2016-04-20 15:12:38 +03:00
2017-02-28 14:44:11 +02:00
auto & cache ( getCache ( * state , uri ) ) ;
2016-04-20 15:12:38 +03:00
2017-02-28 14:44:11 +02:00
wantMassQuery = cache . wantMassQuery ;
priority = cache . priority ;
2016-04-20 15:12:38 +03:00
2017-02-28 14:44:11 +02:00
return true ;
} ) ;
2016-04-20 15:12:38 +03:00
}
std : : pair < Outcome , std : : shared_ptr < NarInfo > > lookupNarInfo (
2016-04-21 18:53:47 +03:00
const std : : string & uri , const std : : string & hashPart ) override
2016-04-20 15:12:38 +03:00
{
2017-02-28 14:44:11 +02:00
return retrySQLite < std : : pair < Outcome , std : : shared_ptr < NarInfo > > > (
[ & ] ( ) - > std : : pair < Outcome , std : : shared_ptr < NarInfo > > {
auto state ( _state . lock ( ) ) ;
auto & cache ( getCache ( * state , uri ) ) ;
2016-04-20 15:12:38 +03:00
2017-02-28 14:44:11 +02:00
auto now = time ( 0 ) ;
auto queryNAR ( state - > queryNAR . use ( )
( cache . id )
( hashPart )
2018-04-06 13:05:15 +03:00
( now - settings . ttlNegativeNarInfoCache )
( now - settings . ttlPositiveNarInfoCache ) ) ;
2017-02-28 14:44:11 +02:00
if ( ! queryNAR . next ( ) )
return { oUnknown , 0 } ;
2018-12-12 18:12:41 +02:00
if ( ! queryNAR . getInt ( 0 ) )
2017-02-28 14:44:11 +02:00
return { oInvalid , 0 } ;
auto narInfo = make_ref < NarInfo > ( ) ;
2018-12-12 18:12:41 +02:00
auto namePart = queryNAR . getStr ( 1 ) ;
2017-02-28 14:44:11 +02:00
narInfo - > path = cache . storeDir + " / " +
hashPart + ( namePart . empty ( ) ? " " : " - " + namePart ) ;
2018-12-12 18:12:41 +02:00
narInfo - > url = queryNAR . getStr ( 2 ) ;
narInfo - > compression = queryNAR . getStr ( 3 ) ;
if ( ! queryNAR . isNull ( 4 ) )
narInfo - > fileHash = Hash ( queryNAR . getStr ( 4 ) ) ;
narInfo - > fileSize = queryNAR . getInt ( 5 ) ;
narInfo - > narHash = Hash ( queryNAR . getStr ( 6 ) ) ;
narInfo - > narSize = queryNAR . getInt ( 7 ) ;
for ( auto & r : tokenizeString < Strings > ( queryNAR . getStr ( 8 ) , " " ) )
2017-02-28 14:44:11 +02:00
narInfo - > references . insert ( cache . storeDir + " / " + r ) ;
2018-12-12 18:12:41 +02:00
if ( ! queryNAR . isNull ( 9 ) )
narInfo - > deriver = cache . storeDir + " / " + queryNAR . getStr ( 9 ) ;
for ( auto & sig : tokenizeString < Strings > ( queryNAR . getStr ( 10 ) , " " ) )
2017-02-28 14:44:11 +02:00
narInfo - > sigs . insert ( sig ) ;
2018-12-12 18:12:41 +02:00
narInfo - > ca = queryNAR . getStr ( 11 ) ;
2017-02-28 14:44:11 +02:00
return { oValid , narInfo } ;
} ) ;
2016-04-20 15:12:38 +03:00
}
void upsertNarInfo (
2016-04-21 18:53:47 +03:00
const std : : string & uri , const std : : string & hashPart ,
std : : shared_ptr < ValidPathInfo > info ) override
2016-04-20 15:12:38 +03:00
{
2017-02-28 14:44:11 +02:00
retrySQLite < void > ( [ & ] ( ) {
auto state ( _state . lock ( ) ) ;
auto & cache ( getCache ( * state , uri ) ) ;
if ( info ) {
auto narInfo = std : : dynamic_pointer_cast < NarInfo > ( info ) ;
assert ( hashPart = = storePathToHash ( info - > path ) ) ;
state - > insertNAR . use ( )
( cache . id )
( hashPart )
( storePathToName ( info - > path ) )
( narInfo ? narInfo - > url : " " , narInfo ! = 0 )
( narInfo ? narInfo - > compression : " " , narInfo ! = 0 )
( narInfo & & narInfo - > fileHash ? narInfo - > fileHash . to_string ( ) : " " , narInfo & & narInfo - > fileHash )
( narInfo ? narInfo - > fileSize : 0 , narInfo ! = 0 & & narInfo - > fileSize )
( info - > narHash . to_string ( ) )
( info - > narSize )
( concatStringsSep ( " " , info - > shortRefs ( ) ) )
( info - > deriver ! = " " ? baseNameOf ( info - > deriver ) : " " , info - > deriver ! = " " )
( concatStringsSep ( " " , info - > sigs ) )
2018-12-12 02:04:34 +02:00
( info - > ca )
2017-02-28 14:44:11 +02:00
( time ( 0 ) ) . exec ( ) ;
} else {
state - > insertMissingNAR . use ( )
( cache . id )
( hashPart )
( time ( 0 ) ) . exec ( ) ;
}
} ) ;
2016-04-20 15:12:38 +03:00
}
} ;
ref < NarInfoDiskCache > getNarInfoDiskCache ( )
{
2018-03-20 17:32:59 +02:00
static ref < NarInfoDiskCache > cache = make_ref < NarInfoDiskCacheImpl > ( ) ;
return cache ;
2016-04-20 15:12:38 +03:00
}
}