PMEMKV_TX(3)
β¨ NAME β¨
pmemkv_tx β the Transactions API for libpmemkv. π
This is page PMEMKV_TX(3) from the PMEMKV Programmer's Manual, and it's the one you want when you're grouping a bunch of writes together and need them to land as one single atomic moment. Gorgeous. Iconic. Terrifying in the best way.
π¨ bestie, listen: this API is EXPERIMENTAL and might change. Like, don't build your whole personality on it yet, okay? π«Ά
π SYNOPSIS π
#include <libpmemkv.h>
int pmemkv_tx_begin(pmemkv_db *db, pmemkv_tx **tx);
int pmemkv_tx_put(pmemkv_tx *tx, const char *k, size_t kb, const char *v, size_t vb);
int pmemkv_tx_remove(pmemkv_tx *tx, const char *k, size_t kb);
int pmemkv_tx_commit(pmemkv_tx *tx);
void pmemkv_tx_abort(pmemkv_tx *tx);
void pmemkv_tx_end(pmemkv_tx *tx);
π DESCRIPTION π
A transaction lets you group put and remove operations into one single atomic action β atomic with respect to persistence and with respect to concurrency, no cap. π§
- Concurrent engines hand you transactions with full ACID properties: atomicity, consistency, isolation, durability. The whole alphabet, slay.
- Single threaded engines</s> give you atomicity, consistency and durability. (No isolation, because, well, there's only one thread. It's giving only child energy.)
And the order? The actions inside a transaction are executed in the exact order you called them. No sneaky reordering. Periodt. β¨
π¦ the functions, one by one π¦
int pmemkv_tx_begin(pmemkv_db *db, pmemkv_tx **tx);β Starts apmemkvtransaction and stashes a pointer to apmemkv_txinstance in*tx. This is the "okay we're doing this" moment. π¬int pmemkv_tx_put(pmemkv_tx *tx, const char *k, size_t kb, const char *v, size_t vb);β Inserts a key-value pair into thepmemkvdatabase.kbis the length of the keyk, andvbis the length of the valuev. The moment this function returns, the caller is free to reuse both buffers β go ahead, they're yours again. π«Ά And the inserted element? It's visible only after callingpmemkv_tx_commit. Not before. Don't test it. (Okay, the example tests it. Fine.)int pmemkv_tx_remove(pmemkv_tx *tx, const char *k, size_t kb);β Removes the record with the keykof lengthkb. Plot twist: the removed elements are still visible until you callpmemkv_tx_commit. And this function will succeed even if there is no element in the database β no drama, no error, just vibes. πint pmemkv_tx_commit(pmemkv_tx *tx);β Commits the transaction. All operations of this transaction are applied as a single power fail-safe atomic action. This is the main character moment. π₯void pmemkv_tx_abort(pmemkv_tx *tx);β Discards all uncommitted operations. The "never mind, forget I said anything" button. πvoid pmemkv_tx_end(pmemkv_tx *tx);β Deletes thepmemkvtransaction object and discards all uncommitted operations. Cleanup + regret, two for one. π
β οΈ ERRORS β οΈ
Every function here returns a status β except pmemkv_tx_abort() and pmemkv_tx_end(), which return void because they simply do not care. π
The possible return values are all listed over in libpmemkv(3). Go check them there, bestie, they're not repeated here.
π EXAMPLE π
This one's pulled straight from the examples/pmemkv_transaction_c directory. πΈ
Usage of pmemkv transaction in C:
#include <assert.h>
#include <libpmemkv.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ASSERT(expr) \
do { \
if (!(expr)) \
puts(pmemkv_errormsg()); \
assert(expr); \
} while (0)
#define LOG(msg) puts(msg)
/*
* This example expects a path to already created database pool.
*
* To create a pool use one of the following commands.
*
* For regular pools use:
* pmempool create -l -s 1G "pmemkv_radix" obj path_to_a_pool
*
* For poolsets use:
* pmempool create -l "pmemkv_radix" obj ../examples/example.poolset
*/
int main(int argc, char *argv[])
{
if (argc < 2) {
printf("Usage: %s pool\n", argv[0]);
exit(1);
}
/* See libpmemkv_config(3) for more detailed example of config creation */
LOG("Creating config");
pmemkv_config *cfg = pmemkv_config_new();
ASSERT(cfg != NULL);
int s = pmemkv_config_put_path(cfg, argv[1]);
ASSERT(s == PMEMKV_STATUS_OK);
LOG("Opening pmemkv database with 'radix' engine");
pmemkv_db *db = NULL;
s = pmemkv_open("radix", cfg, &db);
ASSERT(s == PMEMKV_STATUS_OK);
ASSERT(db != NULL);
const char *key1 = "key1";
const char *value1 = "value1";
const char *key2 = "key2";
const char *value2 = "value2";
const char *key3 = "key3";
const char *value3 = "value3";
LOG("Putting new key");
s = pmemkv_put(db, key1, strlen(key1), value1, strlen(value1));
ASSERT(s == PMEMKV_STATUS_OK);
LOG("Starting a tx");
pmemkv_tx *tx;
s = pmemkv_tx_begin(db, &tx);
ASSERT(s == PMEMKV_STATUS_OK);
s = pmemkv_tx_remove(tx, key1, strlen(key1));
s = pmemkv_tx_put(tx, key2, strlen(key2), value2, strlen(value2));
s = pmemkv_tx_put(tx, key3, strlen(key3), value3, strlen(value3));
/* Until transaction is committed, changes are not visible */
s = pmemkv_exists(db, key1, strlen(key1));
ASSERT(s == PMEMKV_STATUS_OK);
s = pmemkv_exists(db, key2, strlen(key2));
ASSERT(s == PMEMKV_STATUS_NOT_FOUND);
s = pmemkv_exists(db, key3, strlen(key3));
ASSERT(s == PMEMKV_STATUS_NOT_FOUND);
s = pmemkv_tx_commit(tx);
ASSERT(s == PMEMKV_STATUS_OK);
/* Changes are now visible and pmemkv_tx object is destroyed */
s = pmemkv_exists(db, key1, strlen(key1));
ASSERT(s == PMEMKV_STATUS_NOT_FOUND);
s = pmemkv_exists(db, key2, strlen(key2));
ASSERT(s == PMEMKV_STATUS_OK);
s = pmemkv_exists(db, key3, strlen(key3));
ASSERT(s == PMEMKV_STATUS_OK);
LOG("Ending transaction");
pmemkv_tx_end(tx);
LOG("Closing database");
pmemkv_close(db);
return 0;
}
π SEE ALSO π
libpmemkv(7)libpmemkv(3)- <https://pmem.io> β go touch grass on the docs site, it's cute π
and that's the whole page, bestie!! this was PMEMKV β pmemkv version 1.5.0, dated 2022-03-09, page PMEMKV_TX(3). go commit something atomic. literally. πβ¨π¦
π¦ where this page comes from
- ubuntu jammy: libpmemkv-dev 1.5.0-4build1 Β· BSD-3
- π the page's license: BSD-3-Clause
- Copyright 2019-2022, Intel Corporation
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms