/* write a record to a normal database */ int ctdb_ltdb_store(struct ctdb_db_context *ctdb_db, TDB_DATA key, struct ctdb_ltdb_header *header, TDB_DATA data) { struct ctdb_context *ctdb = ctdb_db->ctdb; TDB_DATA rec; int ret; bool seqnum_suppressed = false; if (ctdb_db->ctdb_ltdb_store_fn) { return ctdb_db->ctdb_ltdb_store_fn(ctdb_db, key, header, data); } if (ctdb->flags & CTDB_FLAG_TORTURE) { struct ctdb_ltdb_header *h2; rec = tdb_fetch(ctdb_db->ltdb->tdb, key); h2 = (struct ctdb_ltdb_header *)rec.dptr; if (rec.dptr && rec.dsize >= sizeof(h2) && h2->rsn > header->rsn) { DEBUG(DEBUG_CRIT,("RSN regression! %llu %llu\n", (unsigned long long)h2->rsn, (unsigned long long)header->rsn)); } if (rec.dptr) free(rec.dptr); } rec.dsize = sizeof(*header) + data.dsize; rec.dptr = talloc_size(ctdb, rec.dsize); CTDB_NO_MEMORY(ctdb, rec.dptr); memcpy(rec.dptr, header, sizeof(*header)); memcpy(rec.dptr + sizeof(*header), data.dptr, data.dsize); /* Databases with seqnum updates enabled only get their seqnum changes when/if we modify the data */ if (ctdb_db->seqnum_update != NULL) { TDB_DATA old; old = tdb_fetch(ctdb_db->ltdb->tdb, key); if ( (old.dsize == rec.dsize) && !memcmp(old.dptr+sizeof(struct ctdb_ltdb_header), rec.dptr+sizeof(struct ctdb_ltdb_header), rec.dsize-sizeof(struct ctdb_ltdb_header)) ) { tdb_remove_flags(ctdb_db->ltdb->tdb, TDB_SEQNUM); seqnum_suppressed = true; } if (old.dptr) free(old.dptr); } ret = tdb_store(ctdb_db->ltdb->tdb, key, rec, TDB_REPLACE); if (ret != 0) { DEBUG(DEBUG_ERR, (__location__ " Failed to store dynamic data\n")); } if (seqnum_suppressed) { tdb_add_flags(ctdb_db->ltdb->tdb, TDB_SEQNUM); } talloc_free(rec.dptr); return ret; }
static PyObject *obj_remove_flags(PyTdbObject *self, PyObject *args) { unsigned flags; PyErr_TDB_RAISE_IF_CLOSED(self); if (!PyArg_ParseTuple(args, "I", &flags)) return NULL; tdb_remove_flags(self->ctx, flags); Py_RETURN_NONE; }
/** * Cancel a transaction on database */ static int db_transaction_cancel_handler(struct ctdb_db_context *ctdb_db, void *private_data) { int ret; tdb_add_flags(ctdb_db->ltdb->tdb, TDB_NOLOCK); ret = tdb_transaction_cancel(ctdb_db->ltdb->tdb); if (ret != 0) { DEBUG(DEBUG_ERR, ("Failed to cancel transaction for db %s\n", ctdb_db->db_name)); } tdb_remove_flags(ctdb_db->ltdb->tdb, TDB_NOLOCK); return 0; }
/** * Commit a transaction on database */ static int db_transaction_commit_handler(struct ctdb_db_context *ctdb_db, void *private_data) { int healthy_nodes = *(int *)private_data; int ret; tdb_add_flags(ctdb_db->ltdb->tdb, TDB_NOLOCK); ret = tdb_transaction_commit(ctdb_db->ltdb->tdb); tdb_remove_flags(ctdb_db->ltdb->tdb, TDB_NOLOCK); if (ret != 0) { DEBUG(DEBUG_ERR, ("Failed to commit transaction for db %s\n", ctdb_db->db_name)); return -1; } ret = ctdb_update_persistent_health(ctdb_db->ctdb, ctdb_db, NULL, healthy_nodes); if (ret != 0) { DEBUG(DEBUG_ERR, ("Failed to update persistent health for db %s\n", ctdb_db->db_name)); } return ret; }
/** * Start a transaction on database */ static int db_transaction_start_handler(struct ctdb_db_context *ctdb_db, void *private_data) { bool freeze_transaction_started = *(bool *)private_data; int ret; tdb_add_flags(ctdb_db->ltdb->tdb, TDB_NOLOCK); if (freeze_transaction_started) { ret = tdb_transaction_cancel(ctdb_db->ltdb->tdb); if (ret != 0) { DEBUG(DEBUG_ERR, ("Failed to cancel transaction for db %s\n", ctdb_db->db_name)); } } ret = tdb_transaction_start(ctdb_db->ltdb->tdb); tdb_remove_flags(ctdb_db->ltdb->tdb, TDB_NOLOCK); if (ret != 0) { DEBUG(DEBUG_ERR, ("Failed to start transaction for db %s\n", ctdb_db->db_name)); return -1; } return 0; }
/** * write a record to a normal database * * This is the server-variant of the ctdb_ltdb_store function. * It contains logic to determine whether a record should be * stored or deleted. It also sends SCHEDULE_FOR_DELETION * controls to the local ctdb daemon if apporpriate. */ static int ctdb_ltdb_store_server(struct ctdb_db_context *ctdb_db, TDB_DATA key, struct ctdb_ltdb_header *header, TDB_DATA data) { struct ctdb_context *ctdb = ctdb_db->ctdb; TDB_DATA rec; int ret; bool seqnum_suppressed = false; bool keep = false; bool schedule_for_deletion = false; bool remove_from_delete_queue = false; uint32_t lmaster; if (ctdb->flags & CTDB_FLAG_TORTURE) { struct ctdb_ltdb_header *h2; rec = tdb_fetch(ctdb_db->ltdb->tdb, key); h2 = (struct ctdb_ltdb_header *)rec.dptr; if (rec.dptr && rec.dsize >= sizeof(h2) && h2->rsn > header->rsn) { DEBUG(DEBUG_CRIT,("RSN regression! %llu %llu\n", (unsigned long long)h2->rsn, (unsigned long long)header->rsn)); } if (rec.dptr) free(rec.dptr); } if (ctdb->vnn_map == NULL) { /* * Called from a client: always store the record * Also don't call ctdb_lmaster since it uses the vnn_map! */ keep = true; goto store; } lmaster = ctdb_lmaster(ctdb_db->ctdb, &key); /* * If we migrate an empty record off to another node * and the record has not been migrated with data, * delete the record instead of storing the empty record. */ if (data.dsize != 0) { keep = true; } else if (header->flags & CTDB_REC_RO_FLAGS) { keep = true; } else if (ctdb_db->persistent) { keep = true; } else if (header->flags & CTDB_REC_FLAG_AUTOMATIC) { /* * The record is not created by the client but * automatically by the ctdb_ltdb_fetch logic that * creates a record with an initial header in the * ltdb before trying to migrate the record from * the current lmaster. Keep it instead of trying * to delete the non-existing record... */ keep = true; schedule_for_deletion = true; } else if (header->flags & CTDB_REC_FLAG_MIGRATED_WITH_DATA) { keep = true; } else if (ctdb_db->ctdb->pnn == lmaster) { /* * If we are lmaster, then we usually keep the record. * But if we retrieve the dmaster role by a VACUUM_MIGRATE * and the record is empty and has never been migrated * with data, then we should delete it instead of storing it. * This is part of the vacuuming process. * * The reason that we usually need to store even empty records * on the lmaster is that a client operating directly on the * lmaster (== dmaster) expects the local copy of the record to * exist after successful ctdb migrate call. If the record does * not exist, the client goes into a migrate loop and eventually * fails. So storing the empty record makes sure that we do not * need to change the client code. */ if (!(header->flags & CTDB_REC_FLAG_VACUUM_MIGRATED)) { keep = true; } else if (ctdb_db->ctdb->pnn != header->dmaster) { keep = true; } } else if (ctdb_db->ctdb->pnn == header->dmaster) { keep = true; } if (keep) { if (!ctdb_db->persistent && (ctdb_db->ctdb->pnn == header->dmaster) && !(header->flags & CTDB_REC_RO_FLAGS)) { header->rsn++; if (data.dsize == 0) { schedule_for_deletion = true; } } remove_from_delete_queue = !schedule_for_deletion; } store: /* * The VACUUM_MIGRATED flag is only set temporarily for * the above logic when the record was retrieved by a * VACUUM_MIGRATE call and should not be stored in the * database. * * The VACUUM_MIGRATE call is triggered by a vacuum fetch, * and there are two cases in which the corresponding record * is stored in the local database: * 1. The record has been migrated with data in the past * (the MIGRATED_WITH_DATA record flag is set). * 2. The record has been filled with data again since it * had been submitted in the VACUUM_FETCH message to the * lmaster. * For such records it is important to not store the * VACUUM_MIGRATED flag in the database. */ header->flags &= ~CTDB_REC_FLAG_VACUUM_MIGRATED; /* * Similarly, clear the AUTOMATIC flag which should not enter * the local database copy since this would require client * modifications to clear the flag when the client stores * the record. */ header->flags &= ~CTDB_REC_FLAG_AUTOMATIC; rec.dsize = sizeof(*header) + data.dsize; rec.dptr = talloc_size(ctdb, rec.dsize); CTDB_NO_MEMORY(ctdb, rec.dptr); memcpy(rec.dptr, header, sizeof(*header)); memcpy(rec.dptr + sizeof(*header), data.dptr, data.dsize); /* Databases with seqnum updates enabled only get their seqnum changes when/if we modify the data */ if (ctdb_db->seqnum_update != NULL) { TDB_DATA old; old = tdb_fetch(ctdb_db->ltdb->tdb, key); if ( (old.dsize == rec.dsize) && !memcmp(old.dptr+sizeof(struct ctdb_ltdb_header), rec.dptr+sizeof(struct ctdb_ltdb_header), rec.dsize-sizeof(struct ctdb_ltdb_header)) ) { tdb_remove_flags(ctdb_db->ltdb->tdb, TDB_SEQNUM); seqnum_suppressed = true; } if (old.dptr) free(old.dptr); } DEBUG(DEBUG_DEBUG, (__location__ " db[%s]: %s record: hash[0x%08x]\n", ctdb_db->db_name, keep?"storing":"deleting", ctdb_hash(&key))); if (keep) { ret = tdb_store(ctdb_db->ltdb->tdb, key, rec, TDB_REPLACE); } else { ret = tdb_delete(ctdb_db->ltdb->tdb, key); } if (ret != 0) { int lvl = DEBUG_ERR; if (keep == false && tdb_error(ctdb_db->ltdb->tdb) == TDB_ERR_NOEXIST) { lvl = DEBUG_DEBUG; } DEBUG(lvl, (__location__ " db[%s]: Failed to %s record: " "%d - %s\n", ctdb_db->db_name, keep?"store":"delete", ret, tdb_errorstr(ctdb_db->ltdb->tdb))); schedule_for_deletion = false; remove_from_delete_queue = false; } if (seqnum_suppressed) { tdb_add_flags(ctdb_db->ltdb->tdb, TDB_SEQNUM); } talloc_free(rec.dptr); if (schedule_for_deletion) { int ret2; ret2 = ctdb_local_schedule_for_deletion(ctdb_db, header, key); if (ret2 != 0) { DEBUG(DEBUG_ERR, (__location__ " ctdb_local_schedule_for_deletion failed.\n")); } } if (remove_from_delete_queue) { ctdb_local_remove_from_delete_queue(ctdb_db, header, key); } return ret; }
/* write a record to a normal database */ int ctdb_ltdb_store(struct ctdb_db_context *ctdb_db, TDB_DATA key, struct ctdb_ltdb_header *header, TDB_DATA data) { struct ctdb_context *ctdb = ctdb_db->ctdb; TDB_DATA rec[2]; uint32_t hsize = sizeof(struct ctdb_ltdb_header); int ret; bool seqnum_suppressed = false; if (ctdb_db->ctdb_ltdb_store_fn) { return ctdb_db->ctdb_ltdb_store_fn(ctdb_db, key, header, data); } if (ctdb->flags & CTDB_FLAG_TORTURE) { TDB_DATA old; struct ctdb_ltdb_header *h2; old = tdb_fetch(ctdb_db->ltdb->tdb, key); h2 = (struct ctdb_ltdb_header *)old.dptr; if (old.dptr != NULL && old.dsize >= hsize && h2->rsn > header->rsn) { DEBUG(DEBUG_ERR, ("RSN regression! %"PRIu64" %"PRIu64"\n", h2->rsn, header->rsn)); } if (old.dptr != NULL) { free(old.dptr); } } rec[0].dsize = hsize; rec[0].dptr = (uint8_t *)header; rec[1].dsize = data.dsize; rec[1].dptr = data.dptr; /* Databases with seqnum updates enabled only get their seqnum changes when/if we modify the data */ if (ctdb_db->seqnum_update != NULL) { TDB_DATA old; old = tdb_fetch(ctdb_db->ltdb->tdb, key); if ((old.dsize == hsize + data.dsize) && memcmp(old.dptr+hsize, data.dptr, data.dsize) == 0) { tdb_remove_flags(ctdb_db->ltdb->tdb, TDB_SEQNUM); seqnum_suppressed = true; } if (old.dptr != NULL) { free(old.dptr); } } ret = tdb_storev(ctdb_db->ltdb->tdb, key, rec, 2, TDB_REPLACE); if (ret != 0) { DEBUG(DEBUG_ERR, (__location__ " Failed to store dynamic data\n")); } if (seqnum_suppressed) { tdb_add_flags(ctdb_db->ltdb->tdb, TDB_SEQNUM); } return ret; }