core30Example_main.c

TPC-B comparing transaction safety settings. This example needs a compiled schema, core30Example.sdl.

/*
* Raima Database Manager
*
* Copyright (c) 2019 Raima Inc., All rights reserved.
*
* Use of this software, whether in source code format, or in executable,
* binary object code form, is governed by the Raima LICENSE which
* is fully described in the LICENSE.TXT file, included within this
* distribution of files.
*/
/* \file
* \brief Source code for the RDM core30 example
*/
#include <stdio.h>
#include <stdlib.h>
#include "example_fcns.h"
#include "rdm.h"
#include "rdmstartupapi.h"
/* Generated \c struct and \c typedef definitions to be used with the RDM APIs
*/
#include "core30Example_structs.h"
/* Generated catalog definition to be used with the RDM rdm_dbSetCatalog() API
*/
#include "core30Example_cat.h"
/* Number of records to insert */
#define RAMPUPTIME 15
#define TPS 5
#define NUM_BRANCHES 1
#define NUM_TELLERS 10
#define NUM_ACCOUNTS 100000L
#define STEADYSTATE 60
const char *const description = "Performance Test using TPC-B test "
"specifications. Compares durability settings.";
const RDM_CMDLINE_OPT opts[] = {{NULL, NULL, NULL, NULL}};
/*
* \mainpage Core30 Popcorn Example
*
* The Core30 example is based on the TPC-B Benchmark published by the
* Transaction Processing Performance Council (TPC) in 1994. The benchmark
* test uses a single, simple, update-intensive transaction to load the
* system.
*
* \par The Transaction Profile
*
* \code
* BEGIN TRANSACTION
* Update Account where Account_ID = Aid:
* Read Account_Balance from Account
* Set Account_Balance = Account_Balance + Delta
* Write Account_Balance to Account
* Write to History:
* Aid, Tid, Bid, Delta, Time_stamp
* Update Teller where Teller_ID = Tid:
* Set Teller_Balance = Teller_Balance + Delta
* Write Teller_Balance to Teller
* Update Branch where Branch_ID = Bid:
* Set Branch_Balance = Branch_Balance + Delta
* Write Branch_Balance to Branch
* COMMIT TRANSACTION Return
* Account_Balance to driver
* \endcode
*
* \par TPS configuration
*
* For each nominal transaction-per-second configured, the test uses:
* \li 1 Branch Row for each TPS
* \li 10 Teller Rows for each Branch
* \li 100,000 Account Rows for each Branch
*
* \par Table of Contents
*
* - \subpage hDB
* - \subpage hPGMfunc
*
* For additional information, please refer to the product documentation at
* http://docs.raima.com/.
*
* \page hDB Database Schema
*
* \par Database Schema Definition
*
* The components of the database are defined to consist of four separate and
* individual files/tables, Account, Branch, Teller, and History.
*
* \include core30.sdl
*
* The schema was compiled using the RDM rdm-compile utility with the -s and -a
* options to generate an embedded database dictionary.
*
* \code rdm-compile -s -a core30.sdl \endcode
*
* The schema catalog information (\c core30_cat.c) is embedded inside the
* application.
*
* \page hPGMfunc Program Functions
*
* For simplicity, this example does not check all return codes, but good
* programming practices would dictate that they are checked after each
* RDM call.
*
* \li populateDatabase() - \copybrief populateDatabase
* \li do_trans() - \copybrief do_trans
* \li delete_records() - \copybrief delete_records
* \li main() - \copybrief main
*/
/* \brief Initialize the RDM runtime library for use in the core30 example
*
* This function initializes the RDM Transactional File Server (TFS) to use
* the embedded implementation. It also creates an RDM database handle
* and opens the "core30" database in exclusive mode.
*
* @return Returns an RDM_RETCODE code (sOKAY if successful)
*/
RDM_RETCODE populateDatabase (
RDM_DB hDB) /*< [in] Pointer to the RDM database handle */
{
BRANCHES brnRec;
rdm_dbSetOption (hDB, "durability", "consistent");
printf ("Populate database with %d TPS configuration:\t", TPS);
for (brnRec.bid = 0; brnRec.bid < TPS; brnRec.bid++)
{
int ii;
rc = rdm_dbStartUpdate (hDB, RDM_LOCK_ALL, 0, NULL, 0, NULL);
if (rc == sOKAY)
{
brnRec.bbalance = 0.0;
brnRec._filler_has_value = RDM_FALSE;
hDB, TABLE_BRANCHES, &brnRec, sizeof (brnRec), NULL);
/* insert associated Tellers */
for (ii = 0; rc == sOKAY && ii < NUM_TELLERS; ii++)
{
TELLERS telRec;
telRec.bid = brnRec.bid;
telRec.tbalance = 0.0;
telRec.tid = (NUM_TELLERS * brnRec.bid) + ii;
telRec._filler_has_value = RDM_FALSE;
hDB, TABLE_TELLERS, &telRec, sizeof (telRec), NULL);
}
/* insert associated Accounts */
for (ii = 0; rc == sOKAY && ii < NUM_ACCOUNTS; ii++)
{
ACCOUNTS accRec;
accRec.bid = brnRec.bid;
accRec.abalance = 0.0;
accRec.aid = (NUM_ACCOUNTS * brnRec.bid) + ii;
accRec._filler_has_value = RDM_FALSE;
hDB, TABLE_ACCOUNTS, &accRec, sizeof (accRec), NULL);
}
rdm_dbEnd (hDB);
}
}
timeMeasureEnd (&timer);
printf ("%u milliseconds\n", timeMeasureDiff (&timer));
return rc;
}
RDM_RETCODE do_trans (
RDM_DB hDB, /*< [in] Pointer to the RDM database handle */
unsigned int *elapsedTime)
{
double bal;
RDM_TRANS hTrans;
RDM_CURSOR cursor = NULL;
RDM_CURSOR brnCursor = NULL;
HISTORY historyRow;
TELLERS_TELLERS_PK_KEY tellerKey;
ACCOUNTS_ACCOUNTS_PK_KEY acctKey;
/* Selct Delta value in the range of +/- 999999 */
historyRow.delta = (double) (rand () / (65536.0 * 65536.0));
/* first teller id = 0, last is Branches*10-1 */
historyRow.tid = (int) ((rand () % (TPS * NUM_TELLERS)));
/* assign this teller's branch id */
historyRow.bid = historyRow.tid / NUM_TELLERS;
historyRow.aid = (unsigned int) rand () % NUM_ACCOUNTS;
rdm_timestampNow (0, &historyRow.tim);
do
{
rc = rdm_dbStartUpdate (hDB, RDM_LOCK_ALL, 0, NULL, 0, &hTrans);
} while (rc == eUNAVAIL);
/* Update Account where Account_ID = Aid: */
acctKey.aid = historyRow.aid;
hDB, COL_ACCOUNTS_AID, &acctKey, sizeof (acctKey), &cursor);
cursor, COL_ACCOUNTS_ABALANCE, &bal, sizeof (bal), NULL);
bal += historyRow.delta;
cursor, COL_ACCOUNTS_ABALANCE, &bal, sizeof (bal));
/* Write to History */
hDB, TABLE_HISTORY, &historyRow, sizeof (historyRow), NULL);
/* Update Teller where Teller_ID = Tid */
tellerKey.tid = historyRow.tid;
hDB, COL_TELLERS_TID, &tellerKey, sizeof (tellerKey), &cursor);
cursor, COL_TELLERS_TBALANCE, &bal, sizeof (bal), NULL);
bal += historyRow.delta;
cursor, COL_TELLERS_TBALANCE, &bal, sizeof (bal));
/* Update Branch where Branch_ID = Bid: */
rc = rdm_cursorGetOwnerRow (cursor, REF_TELLERS_FK, &brnCursor);
brnCursor, COL_BRANCHES_BBALANCE, &bal, sizeof (bal), NULL);
bal += historyRow.delta;
brnCursor, COL_BRANCHES_BBALANCE, &bal, sizeof (bal));
rc = rdm_transEnd (hTrans);
timeMeasureEnd (&timer);
if (cursor)
rdm_cursorFree (cursor);
if (brnCursor)
rdm_cursorFree (brnCursor);
if (elapsedTime)
*elapsedTime = timeMeasureDiff (&timer);
return rc;
}
/* \brief Main function for core30 example
*
* The function initializes the RDM environment and runs the insert operations.
* over a number of transaction block sizes
*
* @return Returns the RDM_RETCODE on exit.
*/
int main_core30 (int argc, const char *const *argv)
{
RDM_DB hDB;
RDM_TFS hTFS;
srand (100);
rc = rdm_cmdlineInit (&cmd, argc, argv, description, opts);
if (rc != sCMD_USAGE)
if (rc == sOKAY)
{
/* Initialize the TFS, task, and open/initialize the database */
rc = exampleOpenEmptyDatabase (&hTFS, &hDB, "core30Example", core30Example_cat);
if (rc == sOKAY)
{
rc = populateDatabase (hDB);
if (rc == sOKAY)
{
int ii;
/* RAMP-UP transactions */
printf ("Perform RAMP-UP for %d seconds\n", RAMPUPTIME);
while (rc == sOKAY)
{
timeMeasureEnd (&timer);
if (timeMeasureDiff (&timer) > (RAMPUPTIME * 1000))
break;
rc = do_trans (hDB, NULL);
}
/* Run through the durability settings */
for (ii = 0; ii < 3; ii++)
{
const char *durability[3] = {"durability=durable",
"durability=consistent",
"durability=unsafe"};
rc = rdm_dbSetOptions (hDB, durability[ii]);
unsigned int minTrans = (unsigned int) -1;
unsigned int maxTrans = 0;
unsigned int numTrans = 0;
unsigned int totTrans = 0;
unsigned int thisTrans;
/* TPC-B measurement */
printf (
"\nPerform TPC-B test for %d seconds (%s)\n",
STEADYSTATE, durability[ii]);
while (rc == sOKAY)
{
timeMeasureEnd (&timer);
if (timeMeasureDiff (&timer) > (STEADYSTATE * 1000))
break;
rc = do_trans (hDB, &thisTrans);
/* update the statistics */
minTrans = RDM_MIN (minTrans, thisTrans);
maxTrans = RDM_MAX (maxTrans, thisTrans);
totTrans += thisTrans;
numTrans++;
}
/* prevent divide by 0 incase totTrans is 0 */
if (totTrans != 0)
{
printf (
"Steady state results: %.2f TPS-B\n\n",
(double) numTrans / (double) totTrans * 1000.0);
}
printf (
"\tMinimum transaction time: %u milliseconds\n",
minTrans);
printf (
"\tMaximum transaction time: %u milliseconds\n",
maxTrans);
printf (
"\tTotal number of transactions measured: %u\n",
numTrans);
}
}
exampleCleanup (hTFS, hDB);
}
}
return (int) rc;
}
RDM_RETCODE rdm_transEnd(RDM_TRANS trans)
End a transactional operation.
RDM_RETCODE rdm_dbSetOptions(RDM_DB db, const char *optString)
Set RDM options.
Header for the native RDM Runtime API.
#define exampleOpenEmptyDatabase(tfs, db, name, catalog)
Definition: example_fcns.h:39
void timeMeasureBegin(perfTimer_t *timer)
@ RDM_FALSE
Definition: psptypes.h:59
#define RDM_MAX(a, b)
Definition: psptypes.h:83
RDM_RETCODE rdm_dbEnd(RDM_DB db)
End a transactional operation.
@ sCMD_USAGE
Definition: rdmretcodetypes.h:72
RDM_RETCODE rdm_timestampNow(int16_t time_zone, RDM_PACKED_TIMESTAMP_T *ts)
Get the current local timestamp.
struct RDM_CURSOR_S * RDM_CURSOR
Definition: rdmtypes.h:306
void timeMeasureEnd(perfTimer_t *timer)
@ eUNAVAIL
Definition: rdmretcodetypes.h:192
The buffer used by the command line parser to hold state information.
Definition: rdmcmdlinetypes.h:85
#define RDM_MIN(a, b)
Definition: psptypes.h:81
#define exampleCleanup(tfs, db)
Definition: example_fcns.h:47
RDM_RETCODE rdm_cursorUpdateColumn(RDM_CURSOR cursor, RDM_COLUMN_ID columnId, const void *columnValue, size_t bytesIn)
Update a single column in a row.
#define print_error(rc)
Definition: example_fcns.h:34
@ sOKAY
Definition: rdmretcodetypes.h:96
RDM_RETCODE rdm_dbGetRowsByKeyAtKey(RDM_DB db, RDM_KEY_ID keyId, const void *keyValue, size_t len, RDM_CURSOR *pCursor)
Associate an RDM_CURSOR with a row set that is ordered by key value and is initially positioned at th...
struct RDM_TRANS_S * RDM_TRANS
Definition: rdmtypes.h:307
Generic usage function option record.
Definition: rdmcmdlinetypes.h:32
RDM_RETCODE rdm_cursorFree(RDM_CURSOR cursor)
Free an RDM_CURSOR.
RDM_RETCODE rdm_dbSetOption(RDM_DB db, const char *keyword, const char *strValue)
Set a single RDM option from a string.
#define RDM_STARTUP_EXAMPLE(name)
Definition: rdmstartuptypes.h:73
unsigned int timeMeasureDiff(perfTimer_t *timer)
RDM_RETCODE rdm_dbInsertRow(RDM_DB db, RDM_TABLE_ID tableId, const void *colValues, size_t bytesIn, RDM_CURSOR *pCursor)
Insert a new row into a table at the specified rowId.
RDM_RETCODE rdm_cursorReadColumn(RDM_CURSOR cursor, RDM_COLUMN_ID columnId, void *columnValue, size_t bytesIn, size_t *bytesOut)
Read a single column from a table row.
struct RDM_TFS_S * RDM_TFS
RDM TFS Handle.
Definition: rdmtfstypes.h:21
RDM_RETCODE rdm_cmdlineInit(RDM_CMDLINE *cmd, int32_t argc, const char *const argv[], const char *description, const RDM_CMDLINE_OPT *opts)
Initialize an RDM_CMDLINE buffer and validate the command line.
struct RDM_DB_S * RDM_DB
Definition: rdmtypes.h:305
RDM_RETCODE rdm_cursorGetOwnerRow(RDM_CURSOR memberCursor, RDM_REF_ID refId, RDM_CURSOR *pCursor)
Associate an RDM_CURSOR with a set owner.
#define RDM_LOCK_ALL
Definition: rdmtypes.h:170
RDM_RETCODE rdm_dbStartUpdate(RDM_DB db, const RDM_TABLE_ID *writeTableIds, uint32_t numWriteTableIds, const RDM_TABLE_ID *readTableIds, uint32_t numReadTableIds, RDM_TRANS *pTrans)
Get write locks.
Definition: example_fcns.h:54
Internal RDM Startup API used by startup macros.
RDM_RETCODE
RDM status and error return codes.
Definition: rdmretcodetypes.h:44