NCBI C++ ToolKit
gicache.c
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /*****************************************************************************
2 * $Id: gicache.c 90486 2020-06-18 19:17:03Z syncbot $
3 * ===========================================================================
4 *
5 * PUBLIC DOMAIN NOTICE
6 * National Center for Biotechnology Information
7 *
8 * This software/database is a "United States Government Work" under the
9 * terms of the United States Copyright Act. It was written as part of
10 * the author's official duties as a United States Government employee and
11 * thus cannot be copyrighted. This software/database is freely available
12 * to the public for use. The National Library of Medicine and the U.S.
13 * Government have not placed any restriction on its use or reproduction.
14 *
15 * Although all reasonable efforts have been taken to ensure the accuracy
16 * and reliability of the software and data, the NLM and the U.S.
17 * Government do not and cannot warrant the performance or results that
18 * may be obtained by using this software or data. The NLM and the U.S.
19 * Government disclaim all warranties, express or implied, including
20 * warranties of performance, merchantability or fitness for any particular
21 * purpose.
22 *
23 * Please cite the author in any work or product based on this material.
24 * Authors: Ilya Dondoshansky, Michael Kimelman
25 *
26 * ===========================================================================
27 *
28 * gicache.c
29 *
30 *****************************************************************************/
31 
32 #include <inttypes.h>
33 #include <sys/mman.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <assert.h>
40 #include <poll.h>
41 #include <errno.h>
42 #include <time.h>
43 #include <stdarg.h>
44 
45 #include <lmdb.h>
46 #include "gicache.h"
47 #include "ncbi_toolkit.h"
48 
49 #ifdef TEST_RUN
50 # ifdef NDEBUG
51 # undef NDEBUG
52 # endif
53 #endif
54 
55 #define MAX_ACCESSION_LENGTH 64
56 #define MAX_DBS 16
57 #define MAX_READERS 1024
58 #define MAP_SIZE_INIT (256L * 1024 * 1024 * 1024)
59 #define MAP_SIZE_DELTA (16L * 1024 * 1024 * 1024)
60 #define GI_DBI "#GI_DBI"
61 #define META_DBI "#META_DBI"
62 #define MAX_ROWS_PER_TRANSACTION 128
63 #define SYNC_PERIOD_SEC 5
64 
65 typedef struct {
67  char m_FileName[PATH_MAX];
73  time_t m_last_sync;
74 } SGiDataIndex;
75 
78 
79 static void (*__LogFunc)(char*) = NULL;
80 static void (*__LogFuncEx)(int severity, char* msg) = NULL;
81 
82 static void LOG(int severity, char* fmt, ...)
83 #ifdef __GNUC__
84 __attribute__ ((format (printf, 2, 3)))
85 #endif
86 ;
87 
88 #define SEV_NONE 0
89 #define SEV_INFO 1
90 #define SEV_WARNING 2
91 #define SEV_ERROR 3
92 #define SEV_REJECT 4
93 #define SEV_FATAL 5
94 
95 static void LOG(int severity, char* fmt, ...)
96 {
97  if (__LogFuncEx || __LogFunc) {
98  char msg[2048];
99  va_list args;
100  va_start(args, fmt);
101  int n = vsnprintf(msg, sizeof(msg), fmt, args);
102  if (n >= sizeof(msg) - 1)
103  n = sizeof(msg) - 1;
104  msg[n] = '\0';
105  va_end(args);
106  if (__LogFuncEx)
107  __LogFuncEx(severity, msg);
108  if (__LogFunc)
109  __LogFunc(msg);
110  }
111 }
112 
113 static int x_mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **txn) {
114  int rv;
115  rv = mdb_txn_begin(env, parent, flags, txn);
116  if (rv == MDB_READERS_FULL) {
118  rv = mdb_txn_begin(env, parent, flags, txn);
119  }
120  return rv;
121 }
122 
123 int x_Commit(SGiDataIndex* data_index, int force_sync) {
124  int rv = 0;
125  int rc;
126  if (data_index && data_index->m_txn) {
127  rc = mdb_txn_commit(data_index->m_txn);
128  data_index->m_txn = NULL;
129  data_index->m_txn_rowcount = 0;
130  if (rc) {
131  LOG(SEV_ERROR, "GI_CACHE: failed to commit transaction: %s\n", mdb_strerror(rc));
132  rv = -1;
133  }
134  }
135  if (data_index && rv == 0 && !data_index->m_ReadOnlyMode) {
136  time_t t = time(0);
137  if (force_sync || t > data_index->m_last_sync + SYNC_PERIOD_SEC) {
138  rc = mdb_env_sync(data_index->m_env, 1);
139  if (rc) {
140  LOG(SEV_ERROR, "GI_CACHE: failed to sync env: %s\n", mdb_strerror(rc));
141  rv = -1;
142  }
143  else
144  data_index->m_last_sync = t;
145  }
146  }
147  return rv;
148 }
149 
150 int GiDataIndex_Commit(int force_sync) {
151  return x_Commit(gi_cache, force_sync);
152 }
153 
154 /* Destructor */
155 static void GiDataIndex_Free(SGiDataIndex* data_index) {
156  if (data_index) {
157  if (data_index->m_txn)
158  x_Commit(data_index, 1);
159  if (data_index->m_env) {
160  if (data_index->m_gi_dbi) {
161  mdb_dbi_close(data_index->m_env, data_index->m_gi_dbi);
162  data_index->m_gi_dbi = 0;
163  }
164  if (data_index->m_meta_dbi) {
165  mdb_dbi_close(data_index->m_env, data_index->m_meta_dbi);
166  data_index->m_meta_dbi = 0;
167  }
168  mdb_env_close(data_index->m_env);
169  data_index->m_env = NULL;
170  }
171  free(data_index);
172  data_index = NULL;
173  }
174 }
175 
176 static int
177 xFindFile(SGiDataIndex* data_index, struct stat* fstat) {
178  int rv;
179  rv = stat(data_index->m_FileName, fstat);
180  return rv;
181 }
182 
183 /* Constructor */
184 static SGiDataIndex*
185 GiDataIndex_New(const char* prefix, Uint1 readonly) {
186  int rc;
187  char logmsg[4100];
188  SGiDataIndex* data_index;
189  struct stat fstat;
190  int fstat_err;
191  MDB_txn *txn = 0;
192  int64_t mapsize;
193 
195  data_index = (SGiDataIndex*) malloc(sizeof(SGiDataIndex));
196  memset(data_index, 0, sizeof(SGiDataIndex));
197  data_index->m_ReadOnlyMode = readonly;
198  assert(strlen(prefix) < PATH_MAX);
199  snprintf(data_index->m_FileName, sizeof(data_index->m_FileName), "%s.db", prefix);
200  fstat_err = xFindFile(data_index, &fstat);
201  if (fstat_err != 0 || fstat.st_size == 0) {
202  /* file does not exist */
203  if (readonly) {
204  rc = errno;
205  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to access file (%.256s): %s\n", data_index->m_FileName, strerror(rc));
206  goto ERROR;
207  }
208  }
209 
210  rc = mdb_env_create(&data_index->m_env);
211  if (rc) {
212  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to init LMDB environment: %s\n", mdb_strerror(rc));
213  goto ERROR;
214  }
215 
216  rc = mdb_env_set_maxdbs(data_index->m_env, MAX_DBS);
217  if (rc) {
218  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to update max dbs to %d: %s\n", MAX_DBS, mdb_strerror(rc));
219  goto ERROR;
220  }
221 
222  rc = mdb_env_set_maxreaders(data_index->m_env, MAX_READERS);
223  if (rc) {
224  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to update max readers to %d: %s\n", MAX_READERS, mdb_strerror(rc));
225  goto ERROR;
226  }
227 
228  mapsize = MAP_SIZE_INIT;
229  if (fstat_err == 0 && fstat.st_size + MAP_SIZE_DELTA > mapsize)
230  mapsize = fstat.st_size + MAP_SIZE_DELTA;
231 
232  rc = mdb_env_set_mapsize(data_index->m_env, mapsize);
233  if (rc) {
234  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to set map size of %" PRId64 ": %s\n", mapsize, mdb_strerror(rc));
235  goto ERROR;
236  }
237 
238  rc = mdb_env_open(data_index->m_env, data_index->m_FileName, MDB_NOSUBDIR | (readonly ? MDB_RDONLY : 0) | (MDB_NOSYNC | MDB_NOMETASYNC), 0644);
239  if (rc) {
240  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to open LMDB db (%.256s): %s\n", data_index->m_FileName, mdb_strerror(rc));
241  goto ERROR;
242  }
243 
244  rc = x_mdb_txn_begin(data_index->m_env, NULL, readonly ? MDB_RDONLY : 0, &txn);
245  if (rc) {
246  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to start transaction: %s\n", mdb_strerror(rc));
247  goto ERROR;
248  }
249 
250  rc = mdb_dbi_open(txn, GI_DBI, MDB_INTEGERKEY | (readonly ? 0 : MDB_CREATE), &data_index->m_gi_dbi);
251  if (rc) {
252  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to open dbi (%s): %s\n", GI_DBI, mdb_strerror(rc));
253  goto ERROR;
254  }
255  rc = mdb_dbi_open(txn, META_DBI, (readonly ? 0 : MDB_CREATE), &data_index->m_meta_dbi);
256  if (rc) {
257  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to open dbi (%s): %s\n", META_DBI, mdb_strerror(rc));
258  goto ERROR;
259  }
260 
261  rc = mdb_txn_commit(txn);
262  txn = NULL;
263  if (rc) {
264  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to commit transaction: %s\n", mdb_strerror(rc));
265  goto ERROR;
266  }
267 
268  rc = mdb_reader_check(data_index->m_env, NULL);
269  if (rc) {
270  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to check readers: %s\n", mdb_strerror(rc));
271  goto ERROR;
272  }
273 
274  return data_index;
275 
276 ERROR:
277  if (txn) {
278  mdb_txn_abort(txn);
279  txn = NULL;
280  }
281  GiDataIndex_Free(data_index);
282  data_index = NULL;
284  LOG(SEV_ERROR, "%s", logmsg);
285  return NULL;
286 }
287 
288 static int x_DataToGiData(int64_t gi, MDB_val *data, char *acc_buf, int acc_buf_len, int64_t* gi_len) {
289  char logmsg[256];
290  uint8_t flags = *(uint8_t*)data->mv_data;
291  int gi_len_bytes = flags & 0x7;
292  int gi_len_neg = flags & 0x8;
293  uint8_t acclen = *((uint8_t*)data->mv_data + gi_len_bytes + 1);
294  char* pacc = (char*)data->mv_data + gi_len_bytes + 2;
295 
296  if (acclen >= MAX_ACCESSION_LENGTH) {
297  LOG(SEV_ERROR, "GI_CACHE: accession length (%d) exceeded limit (%d) for gi: %" PRId64 "\n", acclen, MAX_ACCESSION_LENGTH, gi);
298  return 1;
299  }
300 
301  if (gi_len) {
302  int64_t val = 0;
303  memcpy(&val, ((uint8_t*)data->mv_data + 1), gi_len_bytes);
304  if (gi_len_neg) {
305  if (val == 0)
306  val = -1;
307  else
308  val = -val;
309  }
310  *gi_len = val;
311  }
312  if (acc_buf) {
313  if (acc_buf_len <= acclen) {
314  LOG(SEV_ERROR, "GI_CACHE: accession length buffer length (%d) is too small, actual accession length is %d for gi: %" PRId64 "\n", acc_buf_len, acclen, gi);
315  return 1;
316  }
317  memcpy(acc_buf, pacc, acclen);
318  acc_buf[acclen] = 0;
319  }
320  return 0;
321 }
322 
323 static int x_GetGiData(SGiDataIndex* data_index, int64_t gi, char *acc_buf, int acc_buf_len, int64_t* gi_len) {
324  char logmsg[256];
325  int rc;
326  MDB_txn *txn = NULL;
327  MDB_val key;
328  MDB_val data = {0};
329  int64_t gi64;
330 
331  logmsg[0] = 0;
332  if (gi_len)
333  *gi_len = 0;
334  if (acc_buf && acc_buf_len > 0)
335  acc_buf[0] = 0;
336 
337  if (!data_index || !data_index->m_env) {
338  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: DB is not open\n");
339  goto ERROR;
340  }
341 
342  rc = x_mdb_txn_begin(data_index->m_env, NULL, MDB_RDONLY, &txn);
343  if (rc) {
344  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to start transaction: %s\n", mdb_strerror(rc));
345  goto ERROR;
346  }
347 
348  gi64 = gi;
349  key.mv_size = sizeof(gi64);
350  key.mv_data = &gi64;
351  rc = mdb_get(txn, data_index->m_gi_dbi, &key, &data);
352  if (rc == MDB_NOTFOUND) { // silent error
353  LOG(SEV_INFO, "cache-miss for gi=%" PRId64, gi);
354  goto ERROR;
355  }
356  if (rc) {
357  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to get data for gi=%" PRId64 ": %s\n", gi, mdb_strerror(rc));
358  goto ERROR;
359  }
360 
361  rc = x_DataToGiData(gi64, &data, acc_buf, acc_buf_len, gi_len);
362  if (rc)
363  goto ERROR;
364 
365  rc = mdb_txn_commit(txn);
366  txn = NULL;
367  if (rc) {
368  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to close transaction: %s\n", mdb_strerror(rc));
369  goto ERROR;
370  }
371 
372  return 1;
373 ERROR:
374  if (txn) {
375  mdb_txn_abort(txn);
376  txn = NULL;
377  }
378  if (logmsg[0]) {
379  LOG(SEV_ERROR, "%s", logmsg);
380  }
381  return 0;
382 }
383 
384 static int64_t x_GetMaxGi(SGiDataIndex* data_index) {
385  char logmsg[256];
386  int rc;
387  MDB_txn *txn = NULL;
388  MDB_cursor *cur = NULL;
389  MDB_val key;
390  MDB_val data = {0};
391  int64_t gi64 = 0;
392 
393  rc = x_mdb_txn_begin(data_index->m_env, NULL, MDB_RDONLY, &txn);
394  if (rc) {
395  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to start transaction: %s\n", mdb_strerror(rc));
396  goto ERROR;
397  }
398 
399  rc = mdb_cursor_open(txn, data_index->m_gi_dbi, &cur);
400  if (rc) {
401  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to open cursor: %s\n", mdb_strerror(rc));
402  goto ERROR;
403  }
404 
405  key.mv_size = 0;
406  key.mv_data = NULL;
407  rc = mdb_cursor_get(cur, &key, &data, MDB_LAST);
408  if (rc) {
409  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to position cursor to last record: %s\n", mdb_strerror(rc));
410  goto ERROR;
411  }
412 
413  if (!key.mv_data || key.mv_size != sizeof(gi64)) {
414  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: last record contains no valid gi\n");
415  goto ERROR;
416  }
417  gi64 = *(int64_t*)key.mv_data;
418 
419  mdb_cursor_close(cur);
420  cur = NULL;
421 
422  rc = mdb_txn_commit(txn);
423  txn = NULL;
424  if (rc) {
425  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to close transaction: %s\n", mdb_strerror(rc));
426  goto ERROR;
427  }
428 
429  return gi64;
430 
431 ERROR:
432  if (cur) {
433  mdb_cursor_close(cur);
434  cur = NULL;
435  }
436  if (txn) {
437  mdb_txn_abort(txn);
438  txn = NULL;
439  }
440  LOG(SEV_ERROR, "%s", logmsg);
441  return -1;
442 }
443 
444 static int x_PutData(SGiDataIndex* data_index, int64_t gi, int64_t gi_len, const char *acc, int overwrite, int is_incremental) {
445  char logmsg[256];
446  int rc;
447  MDB_val key;
448  MDB_val data = {0};
449  int64_t gi64 = 0;
450  int acclen, datalen;
451  uint8_t *gidata = NULL;
452  int gi_len_bytes = 0;
453  int gi_len_neg;
454  uint8_t flags;
455 
456  logmsg[0] = 0;
457  acclen = strlen(acc);
458  if (acclen > MAX_ACCESSION_LENGTH) {
459  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to put, provided accession is too long: %d (max supported length is %d)\n", acclen, MAX_ACCESSION_LENGTH);
460  goto ERROR;
461  }
462 
463  if (is_incremental) {
464  char lacc[MAX_ACCESSION_LENGTH + 1];
465  int64_t lgi_len = 0;
466  rc = x_GetGiData(data_index, gi, lacc, sizeof(lacc), &lgi_len);
467  if (rc == 1) {
468  int changed_acc = 0;
469  int changed_len = 0;
470  changed_acc = strcmp(lacc, acc) != 0;
471  changed_len = lgi_len != gi_len;
472  if (!changed_acc && !changed_len)
473  return RC_ALREADY_EXISTS;
474  if (changed_acc)
475  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: gi %" PRId64 " changed accession from %s to %s\n", gi, lacc, acc);
476  if (changed_len)
477  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: gi %" PRId64 " changed len from %" PRId64 " to %" PRId64 "\n", gi, lgi_len, gi_len);
478  }
479  }
480 
481  if (!data_index->m_txn) {
482  rc = x_mdb_txn_begin(data_index->m_env, NULL, 0, &data_index->m_txn);
483  if (rc) {
484  data_index->m_txn = NULL;
485  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to start transaction: %s\n", mdb_strerror(rc));
486  goto ERROR;
487  }
488  }
489 
490  gi64 = gi;
491  key.mv_size = sizeof(gi64);
492  key.mv_data = &gi64;
493  gi_len_neg = gi_len < 0;
494  if (gi_len != 0 && gi_len != -1) {
495  if (gi_len_neg)
496  gi_len = -gi_len;
497  int64_t v = gi_len;
498  while (v != 0) {
499  gi_len_bytes++;
500  v >>= 8;
501  }
502  }
503 
504  flags = (gi_len_bytes & 0x7) | ((gi_len_neg ? 1 : 0) << 3);
505 
506  datalen = 1 + // flags
507  gi_len_bytes + // gi_len
508  1 + // acclen
509  acclen; // acc
510  gidata = (uint8_t*)malloc(datalen);
511  gidata[0] = flags;
512  memcpy(gidata + 1, &gi_len, gi_len_bytes);
513  gidata[1 + gi_len_bytes] = acclen;
514  memcpy(gidata + 1 + gi_len_bytes + 1, acc, acclen);
515  data.mv_size = datalen;
516  data.mv_data = gidata;
517  rc = mdb_put(data_index->m_txn, data_index->m_gi_dbi, &key, &data, overwrite ? 0 : MDB_NOOVERWRITE);
518  if (rc) {
519  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to start transaction: %s\n", mdb_strerror(rc));
520  goto ERROR;
521  }
522 
523  if (data_index->m_txn_rowcount++ > MAX_ROWS_PER_TRANSACTION) {
524  int rv = x_Commit(gi_cache, 0);
525  if (rv != 0)
526  goto ERROR;
527  }
528  free(gidata);
529  return 1;
530 
531 ERROR:
532  if (gidata) {
533  free(gidata);
534  gidata = NULL;
535  }
536  if (data_index->m_txn) {
537  mdb_txn_abort(data_index->m_txn);
538  data_index->m_txn = NULL;
539  }
540  if (logmsg[0]) {
541  LOG(SEV_ERROR, "%s", logmsg);
542  }
543  return 0;
544 }
545 
546 static int x_GICacheInit(const char* prefix, Uint1 readonly) {
547  char prefix_str[PATH_MAX];
548 
549  // First try local files
550  snprintf(prefix_str, sizeof(prefix_str), "%s", (prefix ? prefix : DEFAULT_GI_CACHE_PREFIX));
551 
552  /* When reading data, use readonly mode. */
553  if (gi_cache) {
555  gi_cache = NULL;
556  }
557  gi_cache = GiDataIndex_New(prefix_str, readonly);
558 
559  if (readonly) {
560  /* Check whether gi cache is available at this location, by trying to
561  * map it right away. If local cache isn't found, use default path and
562  * try again.
563  * NB: This is only done if nothing was provided in the input argument.
564  */
565  Uint1 cache_found = gi_cache != NULL;
566 
567  if (!cache_found && !prefix) {
568  snprintf(prefix_str, sizeof(prefix_str), "%s/%s", DEFAULT_GI_CACHE_PATH, DEFAULT_GI_CACHE_PREFIX);
569  gi_cache = GiDataIndex_New(prefix_str, readonly);
570  }
571  }
572 
573  return (gi_cache ? 0 : 1);
574 }
575 
576 int GICache_ReadData(const char *prefix) {
577  int rc;
578  rc = x_GICacheInit(prefix, 1);
579  return rc;
580 }
581 
582 /* Retrieves accession.version and gi length by gi. */
583 int GICache_GetAccessionLen(int64_t gi, char* acc, int buf_len, int64_t* gi_len) {
584  int retval = 0;
585  int rv;
586  if (acc && buf_len > 0)
587  acc[0] = NULLB;
588  if (!gi_cache) {
589  if (!data_index_notfound)
590  LOG(SEV_ERROR, "GICache_GetAccession: GI Cache is not initialized, call GICache_ReadData() first");
591  return 0;
592  }
593  rv = x_GetGiData(gi_cache, gi, acc, buf_len, gi_len);
594  if (rv)
595  retval = 1;
596  return retval;
597 }
598 
599 int GICache_GetAccession(int64_t gi, char* acc, int buf_len) {
600  return GICache_GetAccessionLen(gi, acc, buf_len, NULL);
601 }
602 
604  int rv;
605  int64_t gi_len = 0;
606  if(!gi_cache)
607  return 0;
608  rv = x_GetGiData(gi_cache, gi, NULL, 0, &gi_len);
609 
610  if(!rv)
611  return 0;
612 
613  return gi_len;
614 }
615 
617  if(!gi_cache) return 0;
618  return x_GetMaxGi(gi_cache);
619 }
620 
621 int GICache_LoadStart(const char* cache_prefix) {
622  int rc;
623  rc = x_GICacheInit(cache_prefix, 0);
624  return rc;
625 }
626 
628  if (gi_cache) {
630  gi_cache = NULL;
631  }
632 }
633 
634 int GICache_LoadAdd(int64_t gi, int64_t gi_len, const char* acc, int version, int is_incremental) {
635  int acc_len;
636 
637  static char accbuf[MAX_ACCESSION_LENGTH];
638  if(!gi_cache) return 0;
639 
640  if (version > 0)
641  snprintf(accbuf, sizeof(accbuf), "%s.%d", acc, version);
642  else
643  snprintf(accbuf, sizeof(accbuf), "%s", acc);
644 
645  return x_PutData(gi_cache, gi, gi_len, accbuf, 1, is_incremental);
646 }
647 
649  if (gi_cache) {
650  x_Commit(gi_cache, 1);
652  gi_cache = NULL;
653  }
654  return 0;
655 }
656 
657 void GICache_SetLog(void (*logfunc)(char*)) {
658  __LogFunc = logfunc;
659 }
660 
661 void GICache_SetLogEx(void (*logfunc)(int severity, char* msg)) {
662  __LogFuncEx = logfunc;
663 }
664 
665 void GICache_Dump(const char* cache_prefix, const char* filename, volatile int * quitting) {
666  FILE* f;
667  char logmsg[256];
668  int needopen;
669  int rc;
670  MDB_txn *txn = NULL;
671  MDB_cursor *cur = NULL;
672  MDB_val key;
673  MDB_val data = {0};
674 
675  needopen = gi_cache == NULL;
676  if (needopen) {
677  rc = x_GICacheInit(cache_prefix, 1);
678  if(!gi_cache) return;
679  }
680  f = fopen(filename, "w");
681  if (!f) {
682  snprintf(logmsg, sizeof(logmsg), "Failed to open file %s, error: %d", filename, errno);
683  goto ERROR;
684  }
685  setvbuf(f, NULL, _IOFBF, 128 * 1024);
686 
688  if (rc) {
689  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to start transaction: %s\n", mdb_strerror(rc));
690  goto ERROR;
691  }
692 
693  rc = mdb_cursor_open(txn, gi_cache->m_gi_dbi, &cur);
694  if (rc) {
695  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to open cursor: %s\n", mdb_strerror(rc));
696  goto ERROR;
697  }
698 
699  key.mv_size = 0;
700  key.mv_data = NULL;
701  while ((rc = mdb_cursor_get(cur, &key, &data, MDB_NEXT)) == MDB_SUCCESS) {
702  char acc_buf[MAX_ACCESSION_LENGTH];
703  int64_t gi_len = 0;
704  acc_buf[0] = 0;
705  int64_t gi64 = 0;
706 
707  if (!key.mv_data || key.mv_size != sizeof(gi64)) {
708  LOG(SEV_ERROR, "GI_CACHE: last record contains no valid gi\n");
709  continue;
710  }
711  gi64 = *(int64_t*)key.mv_data;
712  if (x_DataToGiData(gi64, &data, acc_buf, sizeof(acc_buf), &gi_len) == 0) {
713  char line[512];
714  snprintf(line, sizeof(line), "%" PRId64 " %s %" PRId64 "\n", gi64, acc_buf, gi_len);
715  fputs(line, f);
716  }
717  if (quitting && *quitting)
718  break;
719  }
720 
721  mdb_cursor_close(cur);
722  cur = NULL;
723 
724  rc = mdb_txn_commit(txn);
725  txn = NULL;
726  if (rc) {
727  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to close transaction: %s\n", mdb_strerror(rc));
728  goto ERROR;
729  }
730 
731  fclose(f);
732  if (needopen) {
733  GICache_ReadEnd();
734  }
735  return;
736 
737 ERROR:
738  if (f) {
739  fclose(f);
740  f = NULL;
741  }
742  if (cur) {
743  mdb_cursor_close(cur);
744  cur = NULL;
745  }
746  if (txn) {
747  mdb_txn_abort(txn);
748  txn = NULL;
749  }
750  LOG(SEV_ERROR, "%s", logmsg);
751  if (needopen) {
752  GICache_ReadEnd();
753  }
754 
755 }
756 
758  int rc;
759  char logmsg[256];
760  int transtarted = 0;
761 
762  if (!gi_cache || !gi_cache->m_env) {
763  snprintf(logmsg, sizeof(logmsg), "GICache_DropDb: failed to drop DB, database is not open");
764  goto ERROR;
765  }
766  if (gi_cache->m_ReadOnlyMode) {
767  snprintf(logmsg, sizeof(logmsg), "GICache_DropDb: failed to drop DB, database is open in readonly mode");
768  goto ERROR;
769  }
770  if (gi_cache->m_txn) {
771  snprintf(logmsg, sizeof(logmsg), "GICache_DropDb: failed to drop DB, database has an active transaction");
772  goto ERROR;
773  }
775  if (rc) {
776  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to start transaction: %s\n", mdb_strerror(rc));
777  goto ERROR;
778  }
779  transtarted = 1;
781  if (rc) {
782  snprintf(logmsg, sizeof(logmsg), "GICache_DropDb: failed to drop DB: %s\n", mdb_strerror(rc));
783  goto ERROR;
784  }
786  if (rc) {
787  snprintf(logmsg, sizeof(logmsg), "GICache_DropDb: failed to drop meta DB: %s\n", mdb_strerror(rc));
788  goto ERROR;
789  }
791  gi_cache->m_txn = NULL;
792  if (rc) {
793  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to close transaction: %s\n", mdb_strerror(rc));
794  goto ERROR;
795  }
796 
797  return 0;
798 ERROR:
799  LOG(SEV_ERROR, "%s", logmsg);
800  if (gi_cache && gi_cache->m_txn && transtarted) {
802  gi_cache->m_txn = NULL;
803  }
804  return 1;
805 }
806 
807 static int x_PutMeta(const char* Name, const char* Value) {
808  MDB_val key;
809  MDB_val data;
810  int rc;
811 
812  key.mv_data = (char*)Name;
813  key.mv_size = strlen(Name);
814  if (Value) {
815  data.mv_data = (char*)Value;
816  data.mv_size = strlen(Value);
817  rc = mdb_put(gi_cache->m_txn, gi_cache->m_meta_dbi, &key, &data, 0);
818  }
819  else {
821  if (rc == MDB_NOTFOUND)
822  rc = 0;
823  }
824  if (rc)
825  LOG(SEV_ERROR, "GICache_UpdateMeta: failed to update META: %s\n", mdb_strerror(rc));
826  return rc;
827 }
828 
829 int GICache_SetMeta(const char* Name, const char* Value) {
830  int rc;
831  char logmsg[256];
832  int transtarted = 0;
833  char meta[512], stime[128];
834 
835  logmsg[0] = 0;
836  if (!gi_cache || !gi_cache->m_env) {
837  snprintf(logmsg, sizeof(logmsg), "GICache_SetMeta: failed to update META, database is not open");
838  goto ERROR;
839  }
840  if (gi_cache->m_ReadOnlyMode) {
841  snprintf(logmsg, sizeof(logmsg), "GICache_SetMeta: failed to update META, database is open in readonly mode");
842  goto ERROR;
843  }
844  if (gi_cache->m_txn) {
845  snprintf(logmsg, sizeof(logmsg), "GICache_SetMeta: failed to update META, database has an active transaction");
846  goto ERROR;
847  }
849  if (rc) {
850  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to start transaction: %s\n", mdb_strerror(rc));
851  goto ERROR;
852  }
853  rc = x_PutMeta(Name, Value);
854  if (rc)
855  goto ERROR;
857  gi_cache->m_txn = NULL;
858  if (rc) {
859  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to commit transaction: %s\n", mdb_strerror(rc));
860  goto ERROR;
861  }
862 
863  return 0;
864 ERROR:
865  if (logmsg[0]) {
866  LOG(SEV_ERROR, "%s", logmsg);
867  }
868  if (gi_cache && gi_cache->m_txn && transtarted) {
870  gi_cache->m_txn = NULL;
871  }
872  return 1;
873 }
874 
875 int GICache_UpdateMeta(int is_incremental, const char* DB, time_t starttime) {
876  int rc;
877  char logmsg[256];
878  char meta[512], stime[128];
879 
880  logmsg[0] = 0;
881  if (gethostname(meta, sizeof(meta)) != 0)
882  meta[0] = 0;
883  rc = GICache_SetMeta(is_incremental ? META_INCREMENTAL_HOST : META_FULL_HOST, meta);
884  if (rc)
885  goto ERROR;
886  rc = GICache_SetMeta(is_incremental ? META_INCREMENTAL_DB : META_FULL_DB, DB);
887  if (rc)
888  goto ERROR;
889  snprintf(stime, sizeof(stime), "%" PRId64, (int64_t)starttime);
890  rc = GICache_SetMeta(is_incremental ? META_INCREMENTAL_TIME : META_FULL_TIME, stime);
891  if (rc)
892  goto ERROR;
893  return 0;
894 ERROR:
895  if (logmsg[0]) {
896  LOG(SEV_ERROR, "%s", logmsg);
897  }
898  return 1;
899 }
900 
901 int GICache_GetMeta(const char* Name, char* Value, size_t ValueSz) {
902  char logmsg[256];
903  int rc;
904  MDB_txn *txn = NULL;
905  MDB_val key;
906  MDB_val data;
907 
908  Value[0] = 0;
909  logmsg[0] = 0;
910  if (!gi_cache || !gi_cache->m_env) {
911  snprintf(logmsg, sizeof(logmsg), "GICache_GetMeta: failed to read META, database is not open");
912  goto ERROR;
913  }
915  if (rc) {
916  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to start transaction: %s\n", mdb_strerror(rc));
917  goto ERROR;
918  }
919  key.mv_size = strlen(Name);
920  key.mv_data = (char*)Name;
921  data.mv_size = 0;
922  data.mv_data = 0;
923  rc = mdb_get(txn, gi_cache->m_meta_dbi, &key, &data);
924  if (rc == MDB_NOTFOUND) // silent error
925  goto ERROR;
926  if (rc) {
927  snprintf(logmsg, sizeof(logmsg), "GICache_GetMeta: failed to read meta: %s\n", mdb_strerror(rc));
928  goto ERROR;
929  }
930  snprintf(Value, ValueSz, "%.*s", (int)data.mv_size, (const char*)data.mv_data);
931 
932  rc = mdb_txn_commit(txn);
933  txn = NULL;
934  if (rc) {
935  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to close transaction: %s\n", mdb_strerror(rc));
936  goto ERROR;
937  }
938  return 0;
939 
940 ERROR:
941  if (logmsg[0]) {
942  LOG(SEV_ERROR, "%s", logmsg);
943  }
944  if (gi_cache && txn) {
945  mdb_txn_abort(txn);
946  txn = NULL;
947  }
948  return 1;
949 }
950 
951 
952 int GICache_GetAccFreqTab(FreqTab* tab, const FreqTab* tablen) {
953  char logmsg[256];
954  int rc;
955  MDB_txn *txn = NULL;
956  MDB_cursor *cur = NULL;
957  MDB_val key;
958  MDB_val data = {0};
959  int64_t totlen = 0, totcomprlen = 0;
960 
961  memset(tab, 0, sizeof(*tab));
962  logmsg[0] = 0;
963  if (!gi_cache || !gi_cache->m_env) {
964  snprintf(logmsg, sizeof(logmsg), "GICache_GetAccFreqTab: failed to get frequency table, database is not open");
965  goto ERROR;
966  }
967 
969  if (rc) {
970  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to start transaction: %s\n", mdb_strerror(rc));
971  goto ERROR;
972  }
973 
974  rc = mdb_cursor_open(txn, gi_cache->m_gi_dbi, &cur);
975  if (rc) {
976  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to open cursor: %s\n", mdb_strerror(rc));
977  goto ERROR;
978  }
979 
980  key.mv_size = 0;
981  key.mv_data = NULL;
982  while ((rc = mdb_cursor_get(cur, &key, &data, MDB_NEXT)) == MDB_SUCCESS) {
983  char acc_buf[MAX_ACCESSION_LENGTH];
984  int64_t gi_len = 0;
985  acc_buf[0] = 0;
986  int64_t gi64 = 0;
987 
988  if (!key.mv_data || key.mv_size != sizeof(gi64)) {
989  LOG(SEV_ERROR, "GI_CACHE: record contains no valid gi\n");
990  continue;
991  }
992  gi64 = *(int64_t*)key.mv_data;
993  if (x_DataToGiData(gi64, &data, acc_buf, sizeof(acc_buf), NULL) == 0) {
994  char *p = acc_buf;
995  int no_compr = 0;
996  int compr = 0;
997  int len = 0;
998  while (*p) {
999  tab->Count[(unsigned int)(*p)]++;
1000  if (tablen) {
1001  int bits = tablen->Count[(unsigned int)(*p)];
1002  if (bits == 0)
1003  no_compr = 1;
1004  else
1005  compr += bits;
1006  }
1007  else
1008  no_compr = 1;
1009  p++;
1010  len++;
1011  }
1012  totlen += len;
1013  if (no_compr)
1014  totcomprlen += len;
1015  else
1016  totcomprlen += (compr + 7) / 8;
1017  }
1018  }
1019 
1020  mdb_cursor_close(cur);
1021  cur = NULL;
1022 
1023  rc = mdb_txn_commit(txn);
1024  txn = NULL;
1025  if (rc) {
1026  snprintf(logmsg, sizeof(logmsg), "GI_CACHE: failed to close transaction: %s\n", mdb_strerror(rc));
1027  goto ERROR;
1028  }
1029 
1030  if (totlen == 0)
1031  totlen = 1;
1032  return (totcomprlen * 100L) / totlen;
1033 
1034 ERROR:
1035  if (cur) {
1036  mdb_cursor_close(cur);
1037  cur = NULL;
1038  }
1039  if (txn) {
1040  mdb_txn_abort(txn);
1041  txn = NULL;
1042  }
1043  if (logmsg[0]) {
1044  LOG(SEV_ERROR, "%s", logmsg);
1045  }
1046  return -1;
1047 
1048 }
1049 
struct __db DB
Definition: bdb_types.hpp:52
static uch flags
#define PRId64
static HENV env
Definition: transaction2.c:38
char data[12]
Definition: iconv.c:80
Int8 int64_t
unsigned char uint8_t
int64_t GICache_GetLength(int64_t gi)
Definition: gicache.c:603
static int64_t x_GetMaxGi(SGiDataIndex *data_index)
Definition: gicache.c:384
#define MAX_DBS
Definition: gicache.c:56
static void(* __LogFuncEx)(int severity, char *msg)
Definition: gicache.c:80
#define SEV_INFO
Definition: gicache.c:89
static void LOG(int severity, char *fmt,...)
Definition: gicache.c:95
#define MAX_ACCESSION_LENGTH
Definition: gicache.c:55
#define MAX_READERS
Definition: gicache.c:57
static int x_PutData(SGiDataIndex *data_index, int64_t gi, int64_t gi_len, const char *acc, int overwrite, int is_incremental)
Definition: gicache.c:444
#define SYNC_PERIOD_SEC
Definition: gicache.c:63
static void(* __LogFunc)(char *)
Definition: gicache.c:79
#define MAP_SIZE_INIT
Definition: gicache.c:58
static int x_GetGiData(SGiDataIndex *data_index, int64_t gi, char *acc_buf, int acc_buf_len, int64_t *gi_len)
Definition: gicache.c:323
int x_Commit(SGiDataIndex *data_index, int force_sync)
Definition: gicache.c:123
static int x_DataToGiData(int64_t gi, MDB_val *data, char *acc_buf, int acc_buf_len, int64_t *gi_len)
Definition: gicache.c:288
void GICache_Dump(const char *cache_prefix, const char *filename, volatile int *quitting)
Definition: gicache.c:665
static int xFindFile(SGiDataIndex *data_index, struct stat *fstat)
Definition: gicache.c:177
#define GI_DBI
Definition: gicache.c:60
void GICache_ReadEnd()
Definition: gicache.c:627
static void GiDataIndex_Free(SGiDataIndex *data_index)
Definition: gicache.c:155
int GICache_ReadData(const char *prefix)
Definition: gicache.c:576
void GICache_SetLogEx(void(*logfunc)(int severity, char *msg))
Definition: gicache.c:661
static SGiDataIndex * gi_cache
Definition: gicache.c:76
int GICache_GetAccessionLen(int64_t gi, char *acc, int buf_len, int64_t *gi_len)
Definition: gicache.c:583
static SGiDataIndex * GiDataIndex_New(const char *prefix, Uint1 readonly)
Definition: gicache.c:185
static int x_mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **txn)
Definition: gicache.c:113
#define SEV_ERROR
Definition: gicache.c:91
int GICache_SetMeta(const char *Name, const char *Value)
Definition: gicache.c:829
static int x_GICacheInit(const char *prefix, Uint1 readonly)
Definition: gicache.c:546
static int x_PutMeta(const char *Name, const char *Value)
Definition: gicache.c:807
int GICache_GetAccession(int64_t gi, char *acc, int buf_len)
Definition: gicache.c:599
int GICache_GetAccFreqTab(FreqTab *tab, const FreqTab *tablen)
Definition: gicache.c:952
int GICache_UpdateMeta(int is_incremental, const char *DB, time_t starttime)
Definition: gicache.c:875
#define META_DBI
Definition: gicache.c:61
int GiDataIndex_Commit(int force_sync)
Definition: gicache.c:150
int GICache_GetMeta(const char *Name, char *Value, size_t ValueSz)
Definition: gicache.c:901
int GICache_LoadEnd()
Definition: gicache.c:648
int GICache_LoadStart(const char *cache_prefix)
Definition: gicache.c:621
int GICache_LoadAdd(int64_t gi, int64_t gi_len, const char *acc, int version, int is_incremental)
Definition: gicache.c:634
#define MAX_ROWS_PER_TRANSACTION
Definition: gicache.c:62
int GICache_DropDb()
Definition: gicache.c:757
#define MAP_SIZE_DELTA
Definition: gicache.c:59
static Nlm_Boolean data_index_notfound
Definition: gicache.c:77
void GICache_SetLog(void(*logfunc)(char *))
Definition: gicache.c:657
int64_t GICache_GetMaxGi(void)
Definition: gicache.c:616
#define META_INCREMENTAL_HOST
Definition: gicache.h:13
#define RC_ALREADY_EXISTS
Definition: gicache.h:20
#define META_INCREMENTAL_TIME
Definition: gicache.h:17
#define META_FULL_DB
Definition: gicache.h:16
#define META_FULL_HOST
Definition: gicache.h:14
#define DEFAULT_GI_CACHE_PATH
Definition: gicache.h:10
#define META_INCREMENTAL_DB
Definition: gicache.h:15
#define META_FULL_TIME
Definition: gicache.h:18
#define DEFAULT_GI_CACHE_PREFIX
Definition: gicache.h:11
#define NULL
Definition: ncbistd.hpp:225
#define PATH_MAX
Definition: ncbifile.hpp:106
uint8_t Uint1
1-byte (8-bit) unsigned integer
Definition: ncbitype.h:99
#define MDB_NOTFOUND
key/data pair not found (EOF)
Definition: lmdb.h:407
#define MDB_SUCCESS
Successful result.
Definition: lmdb.h:403
#define MDB_READERS_FULL
Environment maxreaders reached.
Definition: lmdb.h:423
unsigned int
A callback function used to compare two keys in a database.
Definition: types.hpp:1210
int mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode)
Open an environment handle.
Definition: mdb.c:4959
int mdb_reader_check(MDB_env *env, int *dead)
Check for stale entries in the reader lock table.
Definition: mdb.c:10163
void mdb_env_close(MDB_env *env)
Close the environment and release the memory map.
Definition: mdb.c:5156
int mdb_cursor_get(MDB_cursor *cursor, MDB_val *key, MDB_val *data, MDB_cursor_op op)
Retrieve by cursor.
Definition: mdb.c:6319
int mdb_put(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data, unsigned int flags)
Store items into a database.
Definition: mdb.c:9009
void mdb_dbi_close(MDB_env *env, MDB_dbi dbi)
Close a database handle.
Definition: mdb.c:9867
void mdb_txn_abort(MDB_txn *txn)
Abandon all the operations of the transaction instead of saving them.
Definition: mdb.c:3061
int mdb_txn_commit(MDB_txn *txn)
Commit all the operations of a transaction into the database.
Definition: mdb.c:3448
int mdb_env_sync(MDB_env *env, int force)
Flush the data buffers to disk.
Definition: mdb.c:2513
int mdb_get(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data)
Get items from a database.
Definition: mdb.c:5782
char * mdb_strerror(int err)
Return a string describing a given error code.
Definition: mdb.c:1479
int mdb_cursor_open(MDB_txn *txn, MDB_dbi dbi, MDB_cursor **cursor)
Create a cursor handle.
Definition: mdb.c:7634
int mdb_env_set_mapsize(MDB_env *env, size_t size)
Set the size of the memory map to use for this environment.
Definition: mdb.c:4060
int mdb_env_set_maxdbs(MDB_env *env, MDB_dbi dbs)
Set the maximum number of named databases for the environment.
Definition: mdb.c:4094
int mdb_env_create(MDB_env **env)
Create an LMDB environment handle.
Definition: mdb.c:3951
int mdb_del(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data)
Delete items from a database.
Definition: mdb.c:8523
int mdb_drop(MDB_txn *txn, MDB_dbi dbi, int del)
Empty or delete+close a database.
Definition: mdb.c:9991
int mdb_dbi_open(MDB_txn *txn, const char *name, unsigned int flags, MDB_dbi *dbi)
Open a database in the environment.
Definition: mdb.c:9730
void mdb_cursor_close(MDB_cursor *cursor)
Close a cursor handle.
Definition: mdb.c:7723
int mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **txn)
Create a transaction for use with the environment.
Definition: mdb.c:2829
int mdb_env_set_maxreaders(MDB_env *env, unsigned int readers)
Set the maximum number of threads/reader slots for the environment.
Definition: mdb.c:4103
@ MDB_LAST
Position at last key/data item.
Definition: lmdb.h:376
@ MDB_NEXT
Position at next data item.
Definition: lmdb.h:379
#define MDB_INTEGERKEY
numeric keys in native byte order: either unsigned int or size_t.
Definition: lmdb.h:317
#define MDB_CREATE
create DB if not already existing
Definition: lmdb.h:325
#define MDB_NOMETASYNC
don't fsync metapage after commit
Definition: lmdb.h:293
#define MDB_NOSYNC
don't fsync after commit
Definition: lmdb.h:289
#define MDB_NOSUBDIR
no environment directory
Definition: lmdb.h:287
#define MDB_RDONLY
read only
Definition: lmdb.h:291
#define MDB_NOOVERWRITE
For put: Don't write if the key already exists.
Definition: lmdb.h:332
unsigned int MDB_dbi
A handle for an individual database in the DB environment.
Definition: lmdb.h:241
if(yy_accept[yy_current_state])
yy_size_t n
int len
static int version
Definition: mdb_load.c:29
const struct ncbi::grid::netcache::search::fields::KEY key
GenericValue< UTF8<> > Value
GenericValue with UTF8 encoding.
Definition: document.h:2107
int strcmp(const char *str1, const char *str2)
Definition: odbc_utils.hpp:160
EIPRangeType t
Definition: ncbi_localip.c:101
Uint1 Boolean
bool replacment for C
Definition: ncbi_std.h:94
#define TRUE
bool replacment for C indicating true.
Definition: ncbi_std.h:97
#define FALSE
bool replacment for C indicating false.
Definition: ncbi_std.h:101
#define NULLB
terminating byte of a char* string.
Definition: ncbi_std.h:181
#define vsnprintf
Definition: ncbiconf_msvc.h:66
static Format format
Definition: njn_ioutil.cpp:53
double f(double x_, const double &y_)
Definition: njn_root.hpp:188
char * strerror(int n)
Definition: pcregrep.c:835
static const char * prefix[]
Definition: pcregrep.c:405
#define assert(x)
Definition: srv_diag.hpp:58
int64_t Count[256]
Definition: gicache.h:23
Cursors are used for all DB operations.
Definition: mdb.c:1184
The database environment.
Definition: mdb.c:1259
A database transaction.
Definition: mdb.c:1084
Generic structure used for passing keys and data in and out of the database.
Definition: lmdb.h:257
MDB_dbi m_gi_dbi
Definition: gicache.c:69
MDB_txn * m_txn
Definition: gicache.c:71
MDB_env * m_env
Definition: gicache.c:68
Uint1 m_ReadOnlyMode
Definition: gicache.c:66
int m_txn_rowcount
Definition: gicache.c:72
MDB_dbi m_meta_dbi
Definition: gicache.c:70
char m_FileName[PATH_MAX]
Definition: gicache.c:67
time_t m_last_sync
Definition: gicache.c:73
void free(voidpf ptr)
voidp malloc(uInt size)
Modified on Mon Apr 22 04:01:16 2024 by modify_doxy.py rev. 669887