/** * Delete key from database. */ void dbmw_delete(dbmw_t *dw, gconstpointer key) { struct cached *entry; dbmw_check(dw); g_assert(key); dw->w_access++; entry = map_lookup(dw->values, key); if (entry) { if (entry->dirty) dw->w_hits++; if (!entry->absent) { dw->count_needs_sync = TRUE; /* Deferred delete */ fill_entry(dw, entry, NULL, 0); entry->absent = TRUE; } hash_list_moveto_tail(dw->keys, key); } else { dw->ioerr = FALSE; dbmap_remove(dw->dm, key); if (dbmap_has_ioerr(dw->dm)) { dw->ioerr = TRUE; dw->error = errno; g_warning("DBMW \"%s\" I/O error whilst deleting key: %s", dw->name, dbmap_strerror(dw->dm)); } /* * If the maximum value length of the DB is 0, then it is used as a * "search table" only, meaning there will be no read to get values, * only existence checks. * * Therefore, it makes sense to cache that the key is no longer valid. * Otherwise, possibly pushing a value out of the cache to record * a deletion is not worth it. */ if (0 == dw->value_size) { WALLOC0(entry); entry->absent = TRUE; (void) allocate_entry(dw, key, entry); } } }
/** * Write back cached value to disk. * @return TRUE on success */ static bool write_back(dbmw_t *dw, const void *key, struct cached *value) { dbmap_datum_t dval; bool ok; g_assert(value->dirty); if (value->absent) { /* Key not present, value is null item */ dval.data = NULL; dval.len = 0; } else { /* * Serialize value into our reused message block if a * serialization routine was provided. */ if (dw->pack) { pmsg_reset(dw->mb); (*dw->pack)(dw->mb, value->data); dval.data = pmsg_start(dw->mb); dval.len = pmsg_size(dw->mb); /* * We allocated the message block one byte larger than the * maximum size, in order to detect unexpected serialization * overflows. */ if (dval.len > dw->value_data_size) { /* Don't s_carp() as this is asynchronous wrt data change */ s_critical("DBMW \"%s\" serialization overflow in %s() " "whilst flushing dirty entry", dw->name, stacktrace_function_name(dw->pack)); return FALSE; } } else { dval.data = value->data; dval.len = value->len; } } /* * If cached entry is absent, delete the key. * Otherwise store the serialized value. * * Dirty bit is cleared on success. */ if ( dbg_ds_debugging(dw->dbg, 1, DBG_DSF_CACHING | DBG_DSF_UPDATE | DBG_DSF_INSERT | DBG_DSF_DELETE) ) { dbg_ds_log(dw->dbg, dw, "%s: %s dirty value (%zu byte%s) key=%s", G_STRFUNC, value->absent ? "deleting" : "flushing", dval.len, plural(dval.len), dbg_ds_keystr(dw->dbg, key, (size_t) -1)); } dw->ioerr = FALSE; ok = value->absent ? dbmap_remove(dw->dm, key) : dbmap_insert(dw->dm, key, dval); if (ok) { value->dirty = FALSE; } else if (dbmap_has_ioerr(dw->dm)) { dw->ioerr = TRUE; dw->error = errno; s_warning("DBMW \"%s\" I/O error whilst %s dirty entry: %s", dw->name, value->absent ? "deleting" : "flushing", dbmap_strerror(dw->dm)); } else { s_warning("DBMW \"%s\" error whilst %s dirty entry: %s", dw->name, value->absent ? "deleting" : "flushing", dbmap_strerror(dw->dm)); } return ok; }
/** * Write back cached value to disk. * @return TRUE on success */ static gboolean write_back(dbmw_t *dw, gconstpointer key, struct cached *value) { dbmap_datum_t dval; gboolean ok; g_assert(value->dirty); if (value->absent) { /* Key not present, value is null item */ dval.data = NULL; dval.len = 0; } else { /* * Serialize value into our reused message block if a * serialization routine was provided. */ if (dw->pack) { pmsg_reset(dw->mb); (*dw->pack)(dw->mb, value->data); dval.data = pmsg_start(dw->mb); dval.len = pmsg_size(dw->mb); /* * We allocated the message block one byte larger than the * maximum size, in order to detect unexpected serialization * overflows. */ if (dval.len > dw->value_data_size) { /* Don't g_carp() as this is asynchronous wrt data change */ g_warning("DBMW \"%s\" serialization overflow in %s() " "whilst %s dirty entry", dw->name, stacktrace_routine_name(func_to_pointer(dw->pack), FALSE), value->absent ? "deleting" : "flushing"); return FALSE; } } else { dval.data = value->data; dval.len = value->len; } } /* * If cached entry is absent, delete the key. * Otherwise store the serialized value. * * Dirty bit is cleared on success. */ if (common_dbg > 4) g_debug("DBMW \"%s\" %s dirty value (%lu byte%s)", dw->name, value->absent ? "deleting" : "flushing", (unsigned long) dval.len, 1 == dval.len ? "" : "s"); dw->ioerr = FALSE; ok = value->absent ? dbmap_remove(dw->dm, key) : dbmap_insert(dw->dm, key, dval); if (ok) { value->dirty = FALSE; } else if (dbmap_has_ioerr(dw->dm)) { dw->ioerr = TRUE; dw->error = errno; g_warning("DBMW \"%s\" I/O error whilst %s dirty entry: %s", dw->name, value->absent ? "deleting" : "flushing", dbmap_strerror(dw->dm)); } else { g_warning("DBMW \"%s\" error whilst %s dirty entry: %s", dw->name, value->absent ? "deleting" : "flushing", dbmap_strerror(dw->dm)); } return ok; }
/** * Delete key from database. */ void dbmw_delete(dbmw_t *dw, const void *key) { struct cached *entry; dbmw_check(dw); g_assert(key); dw->w_access++; entry = map_lookup(dw->values, key); if (entry) { if (dbg_ds_debugging(dw->dbg, 2, DBG_DSF_CACHING | DBG_DSF_DELETE)) { dbg_ds_log(dw->dbg, dw, "%s: %s key=%s%s", G_STRFUNC, entry->dirty ? "dirty" : "clean", dbg_ds_keystr(dw->dbg, key, (size_t) -1), entry->absent ? " (was absent)" : ""); } if (entry->dirty) dw->w_hits++; if (!entry->absent) { /* * Entry was present but is now deleted. * * If it was clean, then it was flushed to the database and we now * know that there is one less entry in the database than there is * physically present in the map. * * If it was dirty, then we do not know whether it exists in the * database or not, and therefore we cannot adjust the amount * of cached entries down. */ if (entry->dirty) dw->count_needs_sync = TRUE; /* Deferred delete */ else dw->cached--; /* One less entry in database */ fill_entry(dw, entry, NULL, 0); entry->absent = TRUE; } hash_list_moveto_tail(dw->keys, key); } else { if (dbg_ds_debugging(dw->dbg, 2, DBG_DSF_DELETE)) { dbg_ds_log(dw->dbg, dw, "%s: removing key=%s", G_STRFUNC, dbg_ds_keystr(dw->dbg, key, (size_t) -1)); } dw->ioerr = FALSE; dbmap_remove(dw->dm, key); if (dbmap_has_ioerr(dw->dm)) { dw->ioerr = TRUE; dw->error = errno; s_warning("DBMW \"%s\" I/O error whilst deleting key: %s", dw->name, dbmap_strerror(dw->dm)); } /* * If the maximum value length of the DB is 0, then it is used as a * "search table" only, meaning there will be no read to get values, * only existence checks. * * Therefore, it makes sense to cache that the key is no longer valid. * Otherwise, possibly pushing a value out of the cache to record * a deletion is not worth it. */ if (0 == dw->value_size) { WALLOC0(entry); entry->absent = TRUE; (void) allocate_entry(dw, key, entry); if (dbg_ds_debugging(dw->dbg, 2, DBG_DSF_CACHING)) { dbg_ds_log(dw->dbg, dw, "%s: cached absent key=%s", G_STRFUNC, dbg_ds_keystr(dw->dbg, key, (size_t) -1)); } } } }