Thursday, September 10, 2009

Persisting LR Data Using MySQL

In the beginning it was horrible: not knowing what kind of C compiler LoadRunner is using, I experience with several header files from cygwin and mingw, which I know, deep inside, won’t work.

But RTFM saved the day, by merging the header files to $LR/include MySQL C code can be compiled by only tweaking one 64 bits reference.

Below is the final code to commit unique users, I totally rely on MySQL’s ACID to do the right thing. Hopefully I put my belief at the right place.
int commit_user() {
int rc = 0;
char *exec;
int good = 0;
int available = 1;

db_connection = mysql_init(NULL);
if (db_connection == NULL) {
lr_error_message("Insufficient memory");
return -1;
}

// Connect to the database
if (mysql_real_connect(db_connection, server, user, password, database, port, NULL, 0) == NULL) {
lr_error_message("error on connect: %s", mysql_error(db_connection));
mysql_close(db_connection);
return -1;
}
lr_output_message("Connection to DB is established");

while (!good) {
available = 1;

while (available) {
exec = lr_eval_string("SELECT EXISTS (select login from LATLogin WHERE login = '{pUserName}');");
if (mysql_query(db_connection, exec)!= 0) {
lr_error_message("%s;error on query: %s", exec, mysql_error(db_connection));
mysql_close(db_connection);
return -1;
}
lr_output_message("exec: %s", exec);

if ((query_result = mysql_store_result(db_connection)) == NULL) {
lr_error_message("%s;error on store: %s", exec, mysql_error(db_connection));
mysql_close(db_connection);
return -1;
}

result_row = mysql_fetch_row(query_result);
available = atoi(result_row[0]);
lr_output_message("available=%d", available);

if (available) { //exists
lr_advance_param("pDataFileUserName");
lr_output_message("Advance pDataFileUserName = %s", lr_eval_string("{pDataFileUserName}"));
while (strcmp(lr_eval_string("{pDataFileUserName}"), lr_eval_string("{pUserName}")) == 0) {
lr_advance_param("pDataFileUserName");
lr_output_message("Advance pDataFileUserName = %s", lr_eval_string("{pDataFileUserName}"));
}
lr_save_string(lr_eval_string("{pDataFileUserName}"), "pUserName");
}
mysql_free_result(query_result);
}

exec = lr_eval_string("INSERT INTO LATLogin VALUES('{pUserName}');");
if (mysql_query(db_connection, exec)!= 0) {
rc = mysql_errno(db_connection);

if (rc == 1062) {
lr_output_message("%s is too late, duplicate exists", exec);

//when pUserName exists, probably some else took it already
lr_advance_param("pDataFileUserName");
lr_output_message("Advance pDataFileUserName = %s", lr_eval_string("{pDataFileUserName}"));
while (strcmp(lr_eval_string("{pDataFileUserName}"), lr_eval_string("{pUserName}")) == 0) {
lr_advance_param("pDataFileUserName");
lr_output_message("Advance pDataFileUserName = %s", lr_eval_string("{pDataFileUserName}"));
}
lr_save_string(lr_eval_string("{pDataFileUserName}"), "pUserName");
}
else {
lr_error_message("%s, error(%d): %s", exec, mysql_errno(db_connection), mysql_error(db_connection));
mysql_close(db_connection);
return -1;
}
}
else {
lr_output_message("exec: %s", exec);
good = 1;
user_removed = 0;
}
}
mysql_close(db_connection);
lr_output_message("Connection to DB is closed");
return 0;
}
The final result: running 90 virtual users (I ran out of login), each running 20 seconds transactions.
GetUniqueUserName
  • 75% of population: 0.028 +/- 0.02
  • 90Percentile: 0.043
The generator machine CPU consumption is hovering around 25% - I give this method of persisting LR data a stamp of approval.

No comments: