CernVM-FS  2.9.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
nfs_maps_sqlite.cc
Go to the documentation of this file.
1 
17 #include "nfs_maps_sqlite.h"
18 
19 #include <unistd.h>
20 
21 #include <cassert>
22 #include <cstddef>
23 #include <cstdlib>
24 
25 #include "logging.h"
26 #include "prng.h"
27 #include "smalloc.h"
28 #include "statistics.h"
29 #include "util/exception.h"
30 #include "util/pointer.h"
31 #include "util/posix.h"
32 #include "util/string.h"
33 #include "util_concurrency.h"
34 
35 using namespace std; // NOLINT
36 
37 
39  "CREATE TABLE IF NOT EXISTS inodes (path TEXT PRIMARY KEY);";
40 const char *NfsMapsSqlite::kSqlAddRoot =
41  "INSERT INTO inodes (rowid, path) VALUES (?, \"\");";
42 const char *NfsMapsSqlite::kSqlAddInode =
43  "INSERT INTO inodes VALUES (?);";
44 const char *NfsMapsSqlite::kSqlGetInode =
45  "SELECT rowid FROM inodes where path = ?;";
46 const char *NfsMapsSqlite::kSqlGetPath =
47  "SELECT path FROM inodes where rowid = ?;";
48 
49 
50 int NfsMapsSqlite::BusyHandler(void *data, int attempt) {
51  BusyHandlerInfo *handler_info = static_cast<BusyHandlerInfo *>(data);
52  // Reset the accumulated time if this is the start of a new request
53  if (attempt == 0)
54  handler_info->accumulated_ms = 0;
56  "busy handler, attempt %d, accumulated waiting time %u",
57  attempt, handler_info->accumulated_ms);
58  if (handler_info->accumulated_ms >= handler_info->kMaxWaitMs)
59  return 0;
60 
61  const unsigned backoff_range_ms = 1 << attempt;
62  unsigned backoff_ms = handler_info->prng.Next(backoff_range_ms);
63  if (handler_info->accumulated_ms + backoff_ms > handler_info->kMaxWaitMs) {
64  backoff_ms = handler_info->kMaxWaitMs - handler_info->accumulated_ms;
65  }
66  if (backoff_ms > handler_info->kMaxBackoffMs) {
67  backoff_ms = handler_info->kMaxBackoffMs;
68  }
69 
70  SafeSleepMs(backoff_ms);
71  handler_info->accumulated_ms += backoff_ms;
72  return 1;
73 }
74 
75 
77  const string &db_dir,
78  const uint64_t root_inode,
79  const bool rebuild,
80  perf::Statistics *statistics)
81 {
82  assert(root_inode > 0);
84  maps->n_db_added_ = statistics->Register(
85  "nfs.sqlite.n_added", "total number of issued inode");
86  maps->n_db_seq_ = statistics->Register(
87  "nfs.sqlite.n_seq", "last inode issued");
88  maps->n_db_path_found_ = statistics->Register(
89  "nfs.sqlite.n_path_hit", "inode --> path hits");
90  maps->n_db_inode_found_ = statistics->Register(
91  "nfs.sqlite.n_inode_hit", "path --> inode hits");
92 
93  string db_path = db_dir + "/inode_maps.db";
94 
95  sqlite3_stmt *stmt;
96  if (rebuild) {
98  "Ignoring rebuild flag as this may crash other cluster nodes.");
99  }
100  // We don't want the shared cache, we want minimal caching so sync is kept
101  int retval = sqlite3_enable_shared_cache(0);
102  assert(retval == SQLITE_OK);
103 
104  retval = sqlite3_open_v2(db_path.c_str(), &maps->db_,
105  SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_READWRITE
106  | SQLITE_OPEN_CREATE, NULL);
107  if (retval != SQLITE_OK) {
109  "Failed to create inode_maps file (%s)",
110  db_path.c_str());
111  return NULL;
112  }
113  // Be prepared to wait for up to 1 minute for transactions to complete
114  // Being stuck for a long time is far more favorable than failing
115  // TODO(jblomer): another busy handler. This one conflicts with SIGALRM
116  retval = sqlite3_busy_handler(
117  maps->db_, BusyHandler, &maps->busy_handler_info_);
118  assert(retval == SQLITE_OK);
119 
120  // Set-up the main inode table if it doesn't exist
121  retval = sqlite3_prepare_v2(
122  maps->db_, kSqlCreateTable, kMaxDBSqlLen, &stmt, NULL);
123  if (retval != SQLITE_OK) {
125  "Failed to prepare create table statement: %s",
126  sqlite3_errmsg(maps->db_));
127  return NULL;
128  }
129  if (sqlite3_step(stmt) != SQLITE_DONE) {
131  "Failed to create main inode table: %s",
132  sqlite3_errmsg(maps->db_));
133  sqlite3_finalize(stmt);
134  return NULL;
135  }
136  sqlite3_finalize(stmt);
137 
138  // Prepare lookup and add-inode statements
139  retval = sqlite3_prepare_v2(
140  maps->db_, kSqlGetPath, kMaxDBSqlLen, &maps->stmt_get_path_, NULL);
141  assert(retval == SQLITE_OK);
142  retval = sqlite3_prepare_v2(maps->db_, kSqlGetInode, kMaxDBSqlLen,
143  &maps->stmt_get_inode_, NULL);
144  assert(retval == SQLITE_OK);
145  retval = sqlite3_prepare_v2(maps->db_, kSqlAddInode, kMaxDBSqlLen,
146  &maps->stmt_add_, NULL);
147  assert(retval == SQLITE_OK);
148 
149  // Check the root inode exists, if not create it
150  PathString rootpath("", 0);
151  if (!maps->FindInode(rootpath)) {
152  retval = sqlite3_prepare_v2(
153  maps->db_, kSqlAddRoot, kMaxDBSqlLen, &stmt, NULL);
154  assert(retval == SQLITE_OK);
155  sqlite3_bind_int64(stmt, 1, root_inode);
156  assert(retval == SQLITE_OK);
157  if (sqlite3_step(stmt) != SQLITE_DONE) {
158  PANIC(kLogDebug | kLogSyslogErr, "Failed to execute CreateRoot: %s",
159  sqlite3_errmsg(maps->db_));
160  }
161  sqlite3_finalize(stmt);
162  }
163 
164  return maps.Release();
165 }
166 
167 
172 uint64_t NfsMapsSqlite::FindInode(const PathString &path) {
173  int sqlite_state;
174  uint64_t inode;
175  sqlite_state = sqlite3_bind_text(stmt_get_inode_, 1, path.GetChars(),
176  path.GetLength(), SQLITE_TRANSIENT);
177  assert(sqlite_state == SQLITE_OK);
178  sqlite_state = sqlite3_step(stmt_get_inode_);
179  if (sqlite_state == SQLITE_DONE) {
180  // Path not found in DB
181  sqlite3_reset(stmt_get_inode_);
182  return 0;
183  }
184  if (sqlite_state != SQLITE_ROW) {
185  LogCvmfs(kLogNfsMaps, kLogDebug, "Error finding inode (%s): %s",
186  path.c_str(), sqlite3_errmsg(db_));
187  sqlite3_reset(stmt_get_inode_);
188  return 0;
189  }
190  inode = sqlite3_column_int64(stmt_get_inode_, 0);
191  sqlite3_reset(stmt_get_inode_);
192  return inode;
193 }
194 
195 
200 uint64_t NfsMapsSqlite::IssueInode(const PathString &path) {
201  int sqlite_state;
202  uint64_t inode;
203  sqlite_state = sqlite3_prepare_v2(db_, kSqlAddInode, kMaxDBSqlLen,
204  &stmt_add_, NULL);
205  assert(sqlite_state == SQLITE_OK);
206  sqlite_state = sqlite3_bind_text(stmt_add_, 1, path.GetChars(),
207  path.GetLength(), SQLITE_TRANSIENT);
208  if (sqlite_state != SQLITE_OK) {
210  "Failed to bind path in IssueInode (%s)", path.c_str());
211  sqlite3_reset(stmt_add_);
212  return 0;
213  }
214  sqlite_state = sqlite3_step(stmt_add_);
215  if (sqlite_state != SQLITE_DONE) {
217  "Failed to execute SQL for IssueInode (%s): %s",
218  path.c_str(), sqlite3_errmsg(db_));
219  sqlite3_reset(stmt_add_);
220  return 0;
221  }
222  inode = sqlite3_last_insert_rowid(db_);
223  sqlite3_reset(stmt_add_);
224  n_db_seq_->Set(inode);
225  perf::Inc(n_db_added_);
226  return inode;
227 }
228 
229 
230 uint64_t NfsMapsSqlite::RetryGetInode(const PathString &path, int attempt) {
231  if (attempt > 2) {
232  // We have to give up eventually
233  LogCvmfs(kLogNfsMaps, kLogSyslogErr, "Failed to find & create path (%s)",
234  path.c_str());
235  return 0;
236  }
237 
238  uint64_t inode;
239  {
240  MutexLockGuard m(lock_);
241  inode = FindInode(path);
242  if (inode) {
243  perf::Inc(n_db_path_found_);
244  return inode;
245  }
246  // Inode not found, issue a new one
247  inode = IssueInode(path);
248  }
249 
250  if (!inode) {
251  inode = RetryGetInode(path, attempt + 1);
252  }
253  return inode;
254 }
255 
256 
260 uint64_t NfsMapsSqlite::GetInode(const PathString &path) {
261  return RetryGetInode(path, 0);
262 }
263 
264 
271 bool NfsMapsSqlite::GetPath(const uint64_t inode, PathString *path) {
272  int sqlite_state;
273  MutexLockGuard m(lock_);
274 
275  sqlite_state = sqlite3_bind_int64(stmt_get_path_, 1, inode);
276  assert(sqlite_state == SQLITE_OK);
277  sqlite_state = sqlite3_step(stmt_get_path_);
278  if (sqlite_state == SQLITE_DONE) {
279  // Success, but inode not found!
280  sqlite3_reset(stmt_get_path_);
281  return false;
282  }
283  if (sqlite_state != SQLITE_ROW) {
284  PANIC(kLogSyslogErr, "Failed to execute SQL for GetPath (%" PRIu64 "): %s",
285  inode, sqlite3_errmsg(db_));
286  }
287  const char *raw_path = (const char *)sqlite3_column_text(stmt_get_path_, 0);
288  path->Assign(raw_path, strlen(raw_path));
289  sqlite3_reset(stmt_get_path_);
290  perf::Inc(n_db_inode_found_);
291  return true;
292 }
293 
294 
296  : db_(NULL)
297  , stmt_get_path_(NULL)
298  , stmt_get_inode_(NULL)
299  , stmt_add_(NULL)
300  , lock_(NULL)
301  , n_db_seq_(NULL)
302  , n_db_added_(NULL)
303  , n_db_path_found_(NULL)
304  , n_db_inode_found_(NULL)
305 {
306  lock_ = reinterpret_cast<pthread_mutex_t *>(smalloc(sizeof(pthread_mutex_t)));
307  int retval = pthread_mutex_init(lock_, NULL);
308  assert(retval == 0);
309 }
310 
311 
313  if (stmt_add_) sqlite3_finalize(stmt_add_);
314  if (stmt_get_path_) sqlite3_finalize(stmt_get_path_);
315  if (stmt_get_inode_) sqlite3_finalize(stmt_get_inode_);
316  // Close the handles, it is explicitly OK to call close with NULL
317  sqlite3_close_v2(db_);
318  pthread_mutex_destroy(lock_);
319  free(lock_);
320 }
virtual ~NfsMapsSqlite()
#define LogCvmfs(source, mask,...)
Definition: logging.h:20
perf::Counter * n_db_added_
Counter * Register(const std::string &name, const std::string &desc)
Definition: statistics.cc:139
sqlite3_stmt * stmt_add_
static const char * kSqlGetPath
static NfsMapsSqlite * Create(const std::string &db_dir, const uint64_t root_inode, const bool rebuild, perf::Statistics *statistics_)
perf::Counter * n_db_seq_
#define PANIC(...)
Definition: exception.h:26
void Assign(const char *chars, const unsigned length)
Definition: shortstring.h:53
uint64_t FindInode(const PathString &path)
assert((mem||(size==0))&&"Out Of Memory")
BusyHandlerInfo busy_handler_info_
static const char * kSqlGetInode
pthread_mutex_t * lock_
static const unsigned kMaxWaitMs
virtual uint64_t GetInode(const PathString &path)
static int BusyHandler(void *data, int attempt)
static const char * kSqlCreateTable
static const unsigned kMaxBackoffMs
virtual bool GetPath(const uint64_t inode, PathString *path)
void Inc(class Counter *counter)
Definition: statistics.h:50
perf::Counter * n_db_inode_found_
sqlite3_stmt * stmt_get_inode_
uint64_t RetryGetInode(const PathString &path, int attempt)
uint64_t IssueInode(const PathString &path)
perf::Counter * n_db_path_found_
void SafeSleepMs(const unsigned ms)
Definition: posix.cc:1822
sqlite3_stmt * stmt_get_path_
unsigned GetLength() const
Definition: shortstring.h:103
const char * c_str() const
Definition: shortstring.h:117
const char * GetChars() const
Definition: shortstring.h:96
static const char * kSqlAddRoot
static const char * kSqlAddInode
uint32_t Next(const uint64_t boundary)
Definition: prng.h:45