Add proper per-file copyright notices/licenses and top-level license.
[bluesky.git] / kvstore / backend.cc
1 #include "backend.h"
2 #include "util.h"
3
4 #include <iostream>
5 #include <boost/functional/hash.hpp>
6
7 /* For bdb */
8 extern "C" {
9 #include <sys/types.h>
10 #include <fcntl.h>
11 #include <limits.h>
12 }
13
14
15 namespace kvstore
16 {
17
18 MemoryBackend::~MemoryBackend()
19 {
20 }
21
22 bool MemoryBackend::Put(const string &key,
23                         const string &value)
24 {
25     _map[key] = value;
26     return true;
27 }
28
29 bool MemoryBackend::Get(const string &key,
30                         string *value)
31 {
32     map_t::iterator it = _map.find(key);
33
34     if (it == _map.end())
35     {
36         return false;
37     }
38     else
39     {
40         *value = it->second;
41         return true;
42     }
43 }
44
45 void set_DBT(DBT *thing, const string &str)
46 {
47     bzero(thing, sizeof(DBT));
48     thing->data = (void*)str.data();
49     thing->size = str.size();
50     thing->ulen = str.size();
51 }
52
53 class BDBBackend::BDBDatabase
54 {
55 public:
56     BDBDatabase(const string &path,
57                bool flush,
58                bool log_in_memory,
59                size_t num_dbs)
60         :_path(path)
61     {
62         int res = db_env_create(&_dbenv, 0);
63
64         if (res != 0)
65         {
66             cerr << db_strerror(res) << endl;
67             throw std::runtime_error("db_env_create fail");
68         }
69
70         /* Set Cache Size To Total Memory */
71         if (true)
72         {
73             double use_fraction = 0.1;
74             uint64_t pages = sysconf(_SC_PHYS_PAGES);
75             uint64_t page_size = sysconf(_SC_PAGE_SIZE);
76
77             uint64_t bytes = pages * page_size * use_fraction / num_dbs;
78
79             uint32_t gbytes = bytes / (1024uLL * 1024uLL * 1024uLL);
80             uint32_t nbytes = bytes % (1024uLL * 1024uLL * 1024uLL);
81             uint32_t ncache = bytes / (1024uLL * 1024uLL * 1024uLL * 4uLL) + 1;
82
83             res = _dbenv->set_cachesize(_dbenv, gbytes, nbytes, ncache);
84
85             if (res != 0)
86             {
87                 cerr << db_strerror(res) << endl;
88                 throw std::runtime_error("set_cachesize");
89             }
90         }
91
92         if (log_in_memory)
93         {
94             res = _dbenv->set_flags(_dbenv, DB_LOG_IN_MEMORY, 1);
95
96             if (res != 0)
97             {
98                 cerr << db_strerror(res) << endl;
99                 throw std::runtime_error("BDB ENV DB_LOG_IN_MEMORY");
100             }
101
102         }
103
104         res = _dbenv->set_flags(_dbenv, DB_LOG_AUTO_REMOVE, 1);
105
106         if (res != 0)
107         {
108             cerr << db_strerror(res) << endl;
109             throw std::runtime_error("BDB ENV DB_LOG_AUTO_REMOVE");
110         }
111
112         res = _dbenv->open(_dbenv,
113                            _path.c_str(),
114                            DB_INIT_CDB
115                            | DB_INIT_MPOOL
116                            | DB_CREATE
117                            | DB_THREAD,
118                            0644);
119
120
121         if (res != 0)
122         {
123             cerr << db_strerror(res) << endl;
124             throw std::runtime_error("BDB ENV Open Fail");
125         }
126
127         string dbfilename = _path + "/test.db";
128
129         /* Flush */
130         if (flush)
131         {
132             res = _dbenv->dbremove(_dbenv, NULL, dbfilename.c_str(), "test", 0);
133
134             if (res != 0 && res != ENOENT)
135             {
136                 cerr << db_strerror(res) << endl;
137                 throw std::runtime_error("db remove failed");
138             }
139         }
140
141         res = db_create(&_db, _dbenv, 0);
142
143         if (res != 0)
144         {
145             cerr << db_strerror(res) << endl;
146             throw std::runtime_error("db_create fail");
147         }
148
149         uint32_t flags = DB_CREATE | DB_THREAD;
150
151         res = _db->open(_db,
152                        NULL, /* TXN */
153                        dbfilename.c_str(),
154                        "test",
155                        DB_BTREE,
156                        flags,
157                        0644);
158
159         if (res != 0)
160         {
161             cerr << db_strerror(res) << endl;
162             throw std::runtime_error("BDB Open Fail");
163         }
164
165
166     }
167
168     ~BDBDatabase()
169     {
170         int res;
171
172         if (_db)
173         {
174             if ((res = _db->close(_db, 0)) < 0)
175             {
176                 cerr << db_strerror(res) << endl;
177             }
178         }
179
180         if (_dbenv)
181         {
182             if ((res = _dbenv->close(_dbenv, 0)) < 0)
183             {
184                 cerr << db_strerror(res) << endl;
185             }
186         }
187     }
188
189     bool Put(const string &key,
190                      const string &value)
191     {
192         DBT bdb_key, bdb_value;
193
194         set_DBT(&bdb_key, key);
195         set_DBT(&bdb_value, value);
196
197         if (_db->put(_db, NULL, &bdb_key, &bdb_value, 0) != 0)
198         {
199             perror("bdb put");
200             return false;
201         }
202         else
203         {
204             return true;
205         }
206     }
207
208     bool Get(const string &key,
209              string *value)
210     {
211         DBT bdb_key, bdb_value;
212
213         set_DBT(&bdb_key, key);
214
215         bzero(&bdb_value, sizeof(DBT));
216         bdb_value.flags = DB_DBT_MALLOC;
217
218         int res = _db->get(_db, NULL, &bdb_key, &bdb_value, 0);
219
220         if (res == 0)
221         {
222             *value = string((char*)bdb_value.data, bdb_value.size);
223             free(bdb_value.data);
224             bdb_value.data = NULL;
225             return true;
226         }
227         else if (res == DB_NOTFOUND || res == DB_KEYEMPTY)
228         {
229             return false;
230         }
231         else
232         {
233             /* ERR */
234             cerr << db_strerror(res) << endl;
235             return false;
236         }
237     }
238
239 private:
240     DB *_db;
241     DB_ENV *_dbenv;
242     const string _path;
243 };
244
245 BDBBackend::BDBBackend(const vector<string> &paths,
246                        bool flush,
247                        bool log_in_memory)
248 {
249     if (paths.size() < 1)
250     {
251         cerr << "Insufficient BDB Paths supplied (need at least 1)";
252         throw std::runtime_error("not enough paths");
253     }
254
255
256     for (size_t i = 0; i < paths.size(); ++i)
257     {
258         cerr << "db for " << paths[i] << endl;
259         _dbs.push_back(shared_ptr<BDBDatabase>(new BDBDatabase(paths[i],
260                                                                flush,
261                                                                log_in_memory,
262                                                                paths.size())));
263     }
264 }
265
266 BDBBackend::~BDBBackend()
267 {
268 }
269
270
271 static boost::hash<string> hasher;
272
273 inline size_t BDBBackend::LookupKeyDB(const string &key)
274 {
275     uint32_t hash = (uint32_t)hasher(key);
276     return hash % _dbs.size();
277 }
278
279 bool BDBBackend::Put(const string &key,
280                  const string &value)
281 {
282     size_t i = LookupKeyDB(key);
283
284     return _dbs[i]->Put(key, value);
285 }
286
287 bool BDBBackend::Get(const string &key,
288                  string *value)
289 {
290     size_t i = LookupKeyDB(key);
291     return _dbs[i]->Get(key, value);
292 }
293
294
295 } // namespace kvstore