522 lines
16 KiB
C++
522 lines
16 KiB
C++
/*
|
|
* TPCC.actor.cpp
|
|
*
|
|
* This source file is part of the FoundationDB open source project
|
|
*
|
|
* Copyright 2013-2020 Apple Inc. and the FoundationDB project authors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "flow/Arena.h"
|
|
#include "fdbserver/workloads/workloads.actor.h"
|
|
#include "fdbserver/QuietDatabase.h"
|
|
#include "fdbserver/workloads/TPCCWorkload.h"
|
|
#include "fdbserver/ServerDBInfo.h"
|
|
|
|
#include "fdbclient/ReadYourWrites.h"
|
|
#include "flow/actorcompiler.h" // needs to be last include
|
|
|
|
#undef FLOW_ACOMPILER_STATE
|
|
#define FLOW_ACOMPILER_STATE 1
|
|
|
|
using namespace TPCCWorkload;
|
|
|
|
namespace {
|
|
|
|
constexpr char alphaNumerics[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
|
|
'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F',
|
|
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
|
|
'W', 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' };
|
|
constexpr char numerics[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
|
|
|
|
constexpr const char* originalString = "ORIGINAL";
|
|
|
|
struct PopulateTPCC : TestWorkload {
|
|
static constexpr const char* DESCRIPTION = "PopulateTPCC";
|
|
|
|
int actorsPerClient;
|
|
int warehousesPerActor;
|
|
int clientsUsed;
|
|
|
|
GlobalState gState;
|
|
|
|
PopulateTPCC(WorkloadContext const& ctx) : TestWorkload(ctx) {
|
|
std::string workloadName = DESCRIPTION;
|
|
actorsPerClient = getOption(options, LiteralStringRef("actorsPerClient"), 10);
|
|
warehousesPerActor = getOption(options, LiteralStringRef("warehousesPerActor"), 30);
|
|
clientsUsed = getOption(options, LiteralStringRef("clientsUsed"), 2);
|
|
}
|
|
|
|
int NURand(int C, int A, int x, int y) {
|
|
return (((deterministicRandom()->randomInt(0, A + 1) | deterministicRandom()->randomInt(x, y + 1)) + C) %
|
|
(y - x + 1)) +
|
|
x;
|
|
}
|
|
|
|
StringRef aString(Arena& arena, int x, int y) {
|
|
int length = deterministicRandom()->randomInt(x, y + 1);
|
|
char* res = new (arena) char[length];
|
|
for (int i = 0; i < length; ++i) {
|
|
res[i] = alphaNumerics[deterministicRandom()->randomInt(0, sizeof(alphaNumerics))];
|
|
}
|
|
return StringRef(reinterpret_cast<uint8_t*>(res), length);
|
|
}
|
|
|
|
StringRef nString(Arena& arena, int x, int y) {
|
|
int length = deterministicRandom()->randomInt(x, y + 1);
|
|
char* res = new (arena) char[length];
|
|
for (int i = 0; i < length; ++i) {
|
|
res[i] = numerics[deterministicRandom()->randomInt(0, sizeof(numerics))];
|
|
}
|
|
return StringRef(reinterpret_cast<uint8_t*>(res), length);
|
|
}
|
|
|
|
StringRef genCLast(Arena& arena, int x) {
|
|
int l = x % 10;
|
|
x /= 10;
|
|
int m = x % 10;
|
|
x /= 10;
|
|
int f = x % 10;
|
|
std::stringstream ss;
|
|
ss << syllables[f] << syllables[m] << syllables[l];
|
|
return StringRef(arena, ss.str());
|
|
}
|
|
|
|
StringRef rndZip(Arena& arena) {
|
|
char* result = new (arena) char[9];
|
|
for (int i = 0; i < 4; ++i) {
|
|
result[i] = numerics[deterministicRandom()->randomInt(0, sizeof(numerics))];
|
|
}
|
|
for (int i = 4; i < 9; ++i) {
|
|
result[i] = '1';
|
|
}
|
|
return StringRef(reinterpret_cast<uint8_t*>(result), 9);
|
|
}
|
|
|
|
StringRef dataString(Arena& arena) {
|
|
if (deterministicRandom()->random01() < 0.1) {
|
|
auto str = aString(arena, 26, 51 - strlen(originalString));
|
|
char* r = new (arena) char[str.size() + strlen(originalString)];
|
|
int pos = deterministicRandom()->randomInt(0, str.size());
|
|
std::copy(originalString, originalString + strlen(originalString), r + pos);
|
|
auto res = reinterpret_cast<uint8_t*>(r);
|
|
std::copy(str.begin(), str.begin() + pos, res);
|
|
std::copy(str.begin() + pos, str.end(), res + pos + strlen(originalString));
|
|
return StringRef(res, str.size() + strlen(originalString));
|
|
} else {
|
|
return aString(arena, 26, 51);
|
|
}
|
|
}
|
|
|
|
ACTOR static Future<Void> writeGlobalState(PopulateTPCC* self, Database cx) {
|
|
state ReadYourWritesTransaction tr(cx);
|
|
loop {
|
|
tr.reset();
|
|
try {
|
|
BinaryWriter writer(IncludeVersion());
|
|
serializer(writer, self->gState);
|
|
tr.set(self->gState.key(), writer.toValue());
|
|
wait(tr.commit());
|
|
return Void();
|
|
} catch (Error& e) {
|
|
wait(tr.onError(e));
|
|
}
|
|
}
|
|
}
|
|
|
|
ACTOR static Future<Void> readGlobalState(PopulateTPCC* self, Database cx) {
|
|
state ReadYourWritesTransaction tr(cx);
|
|
loop {
|
|
tr.reset();
|
|
try {
|
|
Optional<Value> val = wait(tr.get(self->gState.key()));
|
|
if (val.present()) {
|
|
BinaryReader reader(val.get(), IncludeVersion());
|
|
serializer(reader, self->gState);
|
|
} else {
|
|
wait(delay(1.0));
|
|
}
|
|
return Void();
|
|
} catch (Error& e) {
|
|
wait(tr.onError(e));
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string description() const override { return DESCRIPTION; }
|
|
|
|
ACTOR static Future<Void> populateItems(PopulateTPCC* self, Database cx) {
|
|
state Transaction tr(cx);
|
|
state int itemStart = 0;
|
|
state int i_id;
|
|
for (; itemStart < 100000; itemStart += 100) {
|
|
TraceEvent("PopulateItems").detail("Status", itemStart);
|
|
loop {
|
|
try {
|
|
tr.reset();
|
|
for (i_id = itemStart; i_id < itemStart + 100; ++i_id) {
|
|
Item item;
|
|
item.i_id = i_id;
|
|
item.i_im_id = deterministicRandom()->randomInt(1, 10001);
|
|
item.i_name = self->aString(item.arena, 14, 25);
|
|
item.i_price = deterministicRandom()->randomInt64(1.0, 100.0);
|
|
item.i_data = self->dataString(item.arena);
|
|
BinaryWriter w(IncludeVersion());
|
|
serializer(w, item);
|
|
tr.set(item.key(), w.toValue(), false);
|
|
}
|
|
wait(tr.commit());
|
|
break;
|
|
} catch (Error& e) {
|
|
TraceEvent("PopulateItemsHandleError").error(e);
|
|
wait(tr.onError(e));
|
|
}
|
|
}
|
|
}
|
|
TraceEvent("PopulateItemsDone");
|
|
return Void();
|
|
}
|
|
|
|
ACTOR static Future<Void> populateCustomers(PopulateTPCC* self, Database cx, int w_id, int d_id) {
|
|
state Transaction tr(cx);
|
|
state int cStart;
|
|
state int c_id;
|
|
for (cStart = 0; cStart < 3000; cStart += 100) {
|
|
TraceEvent("PopulateCustomers")
|
|
.detail("Warehouse", w_id)
|
|
.detail("District", d_id)
|
|
.detail("Customer", cStart);
|
|
loop {
|
|
for (c_id = cStart; c_id < cStart + 100; ++c_id) {
|
|
Customer c;
|
|
History h;
|
|
c.c_id = c_id;
|
|
c.c_d_id = d_id;
|
|
c.c_w_id = w_id;
|
|
if (c_id < 1000) {
|
|
c.c_last = self->genCLast(c.arena, c_id);
|
|
} else {
|
|
c.c_last = self->genCLast(c.arena, self->NURand(self->gState.CLoad, 255, 0, 999));
|
|
}
|
|
c.c_middle = LiteralStringRef("OE");
|
|
c.c_first = self->aString(c.arena, 8, 16);
|
|
c.c_street_1 = self->aString(c.arena, 10, 20);
|
|
c.c_street_2 = self->aString(c.arena, 10, 20);
|
|
c.c_city = self->aString(c.arena, 10, 20);
|
|
c.c_state = self->aString(c.arena, 2, 2);
|
|
c.c_zip = self->rndZip(c.arena);
|
|
c.c_phone = self->nString(c.arena, 16, 16);
|
|
c.c_since = g_network->now();
|
|
if (deterministicRandom()->random01() < 0.1) {
|
|
c.c_credit = LiteralStringRef("BC");
|
|
} else {
|
|
c.c_credit = LiteralStringRef("GC");
|
|
}
|
|
c.c_credit_lim = 50000;
|
|
c.c_discount = deterministicRandom()->random01() / 2.0;
|
|
c.c_balance = -10.0;
|
|
c.c_ytd_payment = 10.0;
|
|
c.c_payment_cnt = 1;
|
|
c.c_delivery_count = 0;
|
|
c.c_data = self->aString(c.arena, 300, 500);
|
|
|
|
h.h_c_id = c_id;
|
|
h.h_c_d_id = d_id;
|
|
h.h_d_id = d_id;
|
|
h.h_w_id = w_id;
|
|
h.h_c_w_id = w_id;
|
|
h.h_date = g_network->now();
|
|
h.h_amount = 10.0;
|
|
h.h_data = self->aString(c.arena, 12, 24);
|
|
{
|
|
BinaryWriter w(IncludeVersion());
|
|
serializer(w, c);
|
|
tr.set(c.key(), w.toValue(), false);
|
|
}
|
|
{
|
|
// Write index
|
|
tr.set(c.indexLastKey(), c.key(), false);
|
|
}
|
|
{
|
|
BinaryWriter w(IncludeVersion());
|
|
serializer(w, h);
|
|
UID k = deterministicRandom()->randomUniqueID();
|
|
BinaryWriter kW(Unversioned());
|
|
serializer(kW, k);
|
|
auto key = kW.toValue().withPrefix(LiteralStringRef("History/"));
|
|
tr.set(key, w.toValue(), false);
|
|
}
|
|
}
|
|
try {
|
|
wait(tr.commit());
|
|
break;
|
|
} catch (Error& e) {
|
|
TraceEvent("PopulateCustomerHandleError").error(e);
|
|
wait(tr.onError(e));
|
|
}
|
|
}
|
|
}
|
|
TraceEvent("PopulateCustomersDone").detail("Warehouse", w_id).detail("District", d_id);
|
|
return Void();
|
|
}
|
|
|
|
ACTOR static Future<Void> populateOrders(PopulateTPCC* self, Database cx, int w_id, int d_id) {
|
|
state Transaction tr(cx);
|
|
state std::vector<int> customerIds;
|
|
state int idStart;
|
|
state int o_id;
|
|
customerIds.reserve(3000);
|
|
for (int i = 0; i < 3000; ++i) {
|
|
customerIds.push_back(i);
|
|
}
|
|
deterministicRandom()->randomShuffle(customerIds);
|
|
for (idStart = 0; idStart < 3000; idStart += 100) {
|
|
TraceEvent("PopulateOrders").detail("Warehouse", w_id).detail("District", d_id).detail("Order", idStart);
|
|
loop {
|
|
tr.reset();
|
|
for (o_id = idStart; o_id < idStart + 100; ++o_id) {
|
|
Order o;
|
|
o.o_id = o_id;
|
|
o.o_c_id = customerIds[o_id];
|
|
o.o_d_id = d_id;
|
|
o.o_w_id = w_id;
|
|
o.o_entry_d = g_network->now();
|
|
if (o_id < 2100) {
|
|
o.o_carrier_id = deterministicRandom()->randomInt(1, 11);
|
|
}
|
|
o.o_ol_cnt = deterministicRandom()->randomInt(5, 16);
|
|
o.o_all_local = true;
|
|
for (int ol_number = 0; ol_number < o.o_ol_cnt; ++ol_number) {
|
|
OrderLine ol;
|
|
ol.ol_o_id = o_id;
|
|
ol.ol_d_id = d_id;
|
|
ol.ol_w_id = w_id;
|
|
ol.ol_number = ol_number;
|
|
ol.ol_i_id = deterministicRandom()->randomInt(0, 100000);
|
|
ol.ol_supply_w_id = w_id;
|
|
if (o_id < 2100) {
|
|
ol.ol_delivery_d = g_network->now();
|
|
ol.ol_amount = 0.0;
|
|
} else {
|
|
ol.ol_amount = deterministicRandom()->random01() * 10000.0;
|
|
}
|
|
ol.ol_quantity = 5;
|
|
ol.ol_dist_info = self->aString(ol.arena, 24, 24);
|
|
BinaryWriter w(IncludeVersion());
|
|
serializer(w, ol);
|
|
tr.set(ol.key(), w.toValue(), false);
|
|
}
|
|
BinaryWriter w(IncludeVersion());
|
|
serializer(w, o);
|
|
tr.set(o.key(), w.toValue(), false);
|
|
}
|
|
try {
|
|
wait(tr.commit());
|
|
break;
|
|
} catch (Error& e) {
|
|
TraceEvent("PopulateOrderHandleError").error(e);
|
|
wait(tr.onError(e));
|
|
}
|
|
}
|
|
}
|
|
TraceEvent("PopulateOrdersDone").detail("Warehouse", w_id).detail("District", d_id);
|
|
return Void();
|
|
}
|
|
|
|
ACTOR static Future<Void> populateNewOrders(PopulateTPCC* self, Database cx, int w_id, int d_id) {
|
|
state Transaction tr(cx);
|
|
TraceEvent("PopulateNewOrders").detail("Warehouse", w_id).detail("District", d_id);
|
|
loop {
|
|
tr.reset();
|
|
for (int i = 2100; i < 3000; ++i) {
|
|
NewOrder no;
|
|
no.no_o_id = i;
|
|
no.no_d_id = d_id;
|
|
no.no_w_id = w_id;
|
|
BinaryWriter w(IncludeVersion());
|
|
serializer(w, no);
|
|
tr.set(no.key(), w.toValue(), false);
|
|
}
|
|
try {
|
|
wait(tr.commit());
|
|
break;
|
|
} catch (Error& e) {
|
|
TraceEvent("PopulateNewOrderHandleError").error(e);
|
|
wait(tr.onError(e));
|
|
}
|
|
}
|
|
TraceEvent("PopulateNewOrdersDone").detail("Warehouse", w_id).detail("District", d_id);
|
|
return Void();
|
|
}
|
|
|
|
ACTOR static Future<Void> populateDistricts(PopulateTPCC* self, Database cx, int w_id) {
|
|
state Transaction tr(cx);
|
|
state int d_id;
|
|
for (d_id = 0; d_id < 10; ++d_id) {
|
|
TraceEvent("PopulateDistricts").detail("Warehouse", w_id).detail("District", d_id);
|
|
loop {
|
|
tr.reset();
|
|
District d;
|
|
d.d_id = d_id;
|
|
d.d_w_id = w_id;
|
|
d.d_name = self->aString(d.arena, 6, 10);
|
|
d.d_street_1 = self->aString(d.arena, 10, 20);
|
|
d.d_street_2 = self->aString(d.arena, 10, 20);
|
|
d.d_city = self->aString(d.arena, 10, 20);
|
|
d.d_state = self->aString(d.arena, 2, 2);
|
|
d.d_zip = self->rndZip(d.arena);
|
|
d.d_tax = deterministicRandom()->random01() * 0.2;
|
|
d.d_ytd = 30000;
|
|
d.d_next_o_id = 3000;
|
|
BinaryWriter w(IncludeVersion());
|
|
serializer(w, d);
|
|
tr.set(d.key(), w.toValue(), false);
|
|
try {
|
|
wait(tr.commit());
|
|
wait(populateCustomers(self, cx, w_id, d_id));
|
|
wait(populateOrders(self, cx, w_id, d_id));
|
|
wait(populateNewOrders(self, cx, w_id, d_id));
|
|
break;
|
|
} catch (Error& e) {
|
|
TraceEvent("PopulateDistrictHandleError").error(e);
|
|
wait(tr.onError(e));
|
|
}
|
|
}
|
|
}
|
|
TraceEvent("PopulateDistrictsDone").detail("Warehouse", w_id);
|
|
return Void();
|
|
}
|
|
|
|
ACTOR static Future<Void> populateStock(PopulateTPCC* self, Database cx, int w_id) {
|
|
state Transaction tr(cx);
|
|
state int idStart;
|
|
for (idStart = 0; idStart < 100000; idStart += 100) {
|
|
TraceEvent("PopulateStock").detail("Warehouse", w_id).detail("i_id", idStart);
|
|
loop {
|
|
tr.reset();
|
|
for (int i = idStart; i < idStart + 100; ++i) {
|
|
Stock s;
|
|
s.s_i_id = i;
|
|
s.s_w_id = w_id;
|
|
s.s_quantity = deterministicRandom()->randomInt(1, 101);
|
|
s.s_dist_01 = self->aString(s.arena, 24, 25);
|
|
s.s_dist_02 = self->aString(s.arena, 24, 25);
|
|
s.s_dist_03 = self->aString(s.arena, 24, 25);
|
|
s.s_dist_04 = self->aString(s.arena, 24, 25);
|
|
s.s_dist_05 = self->aString(s.arena, 24, 25);
|
|
s.s_dist_06 = self->aString(s.arena, 24, 25);
|
|
s.s_dist_07 = self->aString(s.arena, 24, 25);
|
|
s.s_dist_08 = self->aString(s.arena, 24, 25);
|
|
s.s_dist_09 = self->aString(s.arena, 24, 25);
|
|
s.s_dist_10 = self->aString(s.arena, 24, 25);
|
|
s.s_ytd = 0;
|
|
s.s_order_cnt = 0;
|
|
s.s_remote_cnt = 0;
|
|
s.s_data = self->dataString(s.arena);
|
|
BinaryWriter w(IncludeVersion());
|
|
serializer(w, s);
|
|
tr.set(s.key(), w.toValue(), false);
|
|
}
|
|
try {
|
|
wait(tr.commit());
|
|
break;
|
|
} catch (Error& e) {
|
|
TraceEvent("PopulateStockHandleError").error(e).detail("Warehouse", w_id);
|
|
wait(tr.onError(e));
|
|
}
|
|
}
|
|
}
|
|
TraceEvent("PopulateStockDone").detail("Warehouse", w_id);
|
|
return Void();
|
|
}
|
|
|
|
ACTOR static Future<Void> populateWarehouse(PopulateTPCC* self, Database cx, int w_id) {
|
|
state Transaction tr(cx);
|
|
TraceEvent("PopulateWarehouse").detail("W_ID", w_id);
|
|
loop {
|
|
tr.reset();
|
|
try {
|
|
Warehouse w;
|
|
w.w_id = w_id;
|
|
w.w_name = self->aString(w.arena, 6, 11);
|
|
w.w_street_1 = self->aString(w.arena, 10, 21);
|
|
w.w_street_2 = self->aString(w.arena, 10, 21);
|
|
w.w_city = self->aString(w.arena, 10, 21);
|
|
w.w_state = self->aString(w.arena, 2, 3);
|
|
w.w_tax = deterministicRandom()->random01() * 0.2;
|
|
w.w_ytd = 300000;
|
|
BinaryWriter writer(IncludeVersion());
|
|
serializer(writer, w);
|
|
tr.set(w.key(), writer.toValue(), false);
|
|
wait(tr.commit());
|
|
break;
|
|
} catch (Error& e) {
|
|
TraceEvent("PopulateWarehouseHandleError").error(e).detail("Warehouse", w_id);
|
|
wait(tr.onError(e));
|
|
}
|
|
}
|
|
wait(populateStock(self, cx, w_id));
|
|
wait(populateDistricts(self, cx, w_id));
|
|
TraceEvent("PopulateWarehouseDone").detail("W_ID", w_id);
|
|
return Void();
|
|
}
|
|
|
|
ACTOR static Future<Void> populateActor(PopulateTPCC* self, Database cx, int actorId) {
|
|
state int startWID =
|
|
self->clientId * self->actorsPerClient * self->warehousesPerActor + actorId * self->warehousesPerActor;
|
|
state int endWID = startWID + self->warehousesPerActor;
|
|
state int wid;
|
|
for (wid = startWID; wid < endWID; ++wid) {
|
|
wait(populateWarehouse(self, cx, wid));
|
|
}
|
|
return Void();
|
|
}
|
|
|
|
ACTOR static Future<Void> populate(PopulateTPCC* self, Database cx) {
|
|
if (self->clientId == 0) {
|
|
wait(writeGlobalState(self, cx));
|
|
} else {
|
|
wait(readGlobalState(self, cx));
|
|
}
|
|
if (self->clientId == 0) {
|
|
wait(populateItems(self, cx));
|
|
}
|
|
|
|
state vector<Future<Void>> populateActors;
|
|
state int actorId;
|
|
for (actorId = 0; actorId < self->actorsPerClient; ++actorId) {
|
|
populateActors.push_back(populateActor(self, cx, actorId));
|
|
}
|
|
wait(waitForAll(populateActors));
|
|
wait(quietDatabase(cx, self->dbInfo, "PopulateTPCC"));
|
|
return Void();
|
|
}
|
|
|
|
Future<Void> setup(Database const& cx) override {
|
|
if (clientId >= clientsUsed)
|
|
return Void();
|
|
return populate(this, cx);
|
|
}
|
|
|
|
Future<Void> start(Database const& cx) override { return Void(); }
|
|
|
|
Future<bool> check(Database const& cx) override { return true; }
|
|
|
|
void getMetrics(vector<PerfMetric>& m) override {}
|
|
};
|
|
|
|
} // namespace
|
|
|
|
WorkloadFactory<PopulateTPCC> PopulateTPCCWorkloadFactory(PopulateTPCC::DESCRIPTION);
|