aboutsummaryrefslogtreecommitdiff
path: root/src/libfetchers/cache.cc
blob: e1c7f3dee32a1fef70a6948fbcd7baa700cb0e64 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include "cache.hh"
#include "sqlite.hh"
#include "sync.hh"
#include "store-api.hh"

#include <nlohmann/json.hpp>

namespace nix::fetchers {

static const char * schema = R"sql(

create table if not exists Cache (
    input     text not null,
    info      text not null,
    path      text not null,
    immutable integer not null,
    timestamp integer not null,
    primary key (input)
);
)sql";

struct CacheImpl : Cache
{
    struct State
    {
        SQLite db;
        SQLiteStmt add, lookup;
    };

    Sync<State> _state;

    CacheImpl()
    {
        auto state(_state.lock());

        auto dbPath = getCacheDir() + "/nix/fetcher-cache-v1.sqlite";
        createDirs(dirOf(dbPath));

        state->db = SQLite(dbPath);
        state->db.isCache();
        state->db.exec(schema);

        state->add.create(state->db,
            "insert or replace into Cache(input, info, path, immutable, timestamp) values (?, ?, ?, ?, ?)");

        state->lookup.create(state->db,
            "select info, path, immutable, timestamp from Cache where input = ?");
    }

    void add(
        ref<Store> store,
        const Attrs & inAttrs,
        const Attrs & infoAttrs,
        const StorePath & storePath,
        bool immutable) override
    {
        _state.lock()->add.use()
            (attrsToJson(inAttrs).dump())
            (attrsToJson(infoAttrs).dump())
            (store->printStorePath(storePath))
            (immutable)
            (time(0)).exec();
    }

    std::optional<std::pair<Attrs, StorePath>> lookup(
        ref<Store> store,
        const Attrs & inAttrs) override
    {
        if (auto res = lookupExpired(store, inAttrs)) {
            if (!res->expired)
                return std::make_pair(std::move(res->infoAttrs), std::move(res->storePath));
            debug("ignoring expired cache entry '%s'",
                attrsToJson(inAttrs).dump());
        }
        return {};
    }

    std::optional<Result> lookupExpired(
        ref<Store> store,
        const Attrs & inAttrs) override
    {
        auto state(_state.lock());

        auto inAttrsJson = attrsToJson(inAttrs).dump();

        auto stmt(state->lookup.use()(inAttrsJson));
        if (!stmt.next()) {
            debug("did not find cache entry for '%s'", inAttrsJson);
            return {};
        }

        auto infoJson = stmt.getStr(0);
        auto storePath = store->parseStorePath(stmt.getStr(1));
        auto immutable = stmt.getInt(2) != 0;
        auto timestamp = stmt.getInt(3);

        store->addTempRoot(storePath);
        if (!store->isValidPath(storePath)) {
            // FIXME: we could try to substitute 'storePath'.
            debug("ignoring disappeared cache entry '%s'", inAttrsJson);
            return {};
        }

        debug("using cache entry '%s' -> '%s', '%s'",
            inAttrsJson, infoJson, store->printStorePath(storePath));

        return Result {
            .expired = !immutable && (settings.tarballTtl.get() == 0 || timestamp + settings.tarballTtl < time(0)),
            .infoAttrs = jsonToAttrs(nlohmann::json::parse(infoJson)),
            .storePath = std::move(storePath)
        };
    }
};

ref<Cache> getCache()
{
    static auto cache = std::make_shared<CacheImpl>();
    return ref<Cache>(cache);
}

}