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