Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 35 additions & 22 deletions src/benchmark/cbdc_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,44 +250,52 @@ void CascadeCBDC::ClientThread::main_loop(){
if(!running) return;

// thread main loop
std::unordered_map<uint32_t,queued_request_t*> to_persist;
// std::unordered_map<uint32_t,queued_request_t*> to_persist;
std::unordered_map<uint32_t,std::vector<queued_request_t>> to_persist;
std::unordered_map<uint32_t,std::chrono::steady_clock::time_point> wait_time;
auto batch_time = std::chrono::microseconds(batch_time_us);
while(true){
// size_t curr_batch_size = 0;
while(running){
std::unique_lock<std::mutex> lock(thread_mtx);
bool empty = true;
for(auto& item : request_queues){
for(auto& item : request_queues) {
empty = empty && item.second.empty();
}

if(empty){
thread_signal.wait_for(lock,batch_time);

if(empty) {
thread_signal.wait_for(lock, batch_time);
if(!running) break;
}

if(!running) break;

std::unordered_map<uint32_t,uint64_t> persist_count;
auto now = std::chrono::steady_clock::now();
for(auto& item : request_queues){

// Process one request per shard per iteration
for(auto& item : request_queues) {
auto& shard = item.first;
auto& queue = item.second;

if(to_persist.count(shard) == 0){
to_persist[shard] = new queued_request_t[batch_max_size];
if(to_persist.count(shard) == 0) {
to_persist[shard] = std::vector<queued_request_t>();
to_persist[shard].reserve(batch_max_size);
wait_time[shard] = now;
}

uint64_t queued_count = queue.size();
if((queued_count >= batch_min_size) || ((now-wait_time[shard]) >= batch_time)){
persist_count[shard] = std::min(queued_count,batch_max_size);
wait_time[shard] = now;

// copy out wallets
for(uint64_t i=0;i<persist_count[shard];i++){
to_persist[shard][i] = queue.front();
queue.pop();
// Process if we hit minimum batch size or timeout
if(!queue.empty() &&
(queue.size() >= batch_min_size ||
(now - wait_time[shard]) >= batch_time)) {

// Only take one request per iteration for fairness
to_persist[shard].push_back(queue.front());
persist_count[shard] = to_persist[shard].size();
queue.pop();

// Reset wait time if we've hit batch size
if(persist_count[shard] >= batch_max_size) {
wait_time[shard] = now;
}
}
}
}

lock.unlock();
Expand All @@ -301,7 +309,7 @@ void CascadeCBDC::ClientThread::main_loop(){
continue;
}

auto requests = to_persist[shard];
auto& requests = to_persist[shard];

std::vector<ObjectWithStringKey> objects;
objects.reserve(count);
Expand Down Expand Up @@ -346,7 +354,12 @@ void CascadeCBDC::ClientThread::main_loop(){
for(auto& obj : objects){
TimestampLogger::log(CBDC_TAG_CLIENT_TRANSFER_SENT,node_id,obj.message_id,0);
}

// Clear processed requests
requests.clear();
}
// Reset for next batch
persist_count.clear();
}
}

120 changes: 82 additions & 38 deletions src/core/cbdc_udl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -937,10 +937,13 @@ void CascadeCBDC::WalletPersistenceThread::main_loop(){
if(!running) return;

// thread main loop
queued_wallet_t to_persist[udl->config.wallet_persistence_batch_max_size];
// queued_wallet_t to_persist[udl->config.wallet_persistence_batch_max_size];
std::vector<queued_wallet_t> to_persist;
auto wait_start = std::chrono::steady_clock::now();
auto batch_time = std::chrono::microseconds(udl->config.wallet_persistence_batch_time_us);
while(true){
size_t curr_batch_size = 0;

while(running){
std::unique_lock<std::mutex> lock(thread_mtx);
if(wallet_queue.empty()){
thread_signal.wait_for(lock,batch_time);
Expand All @@ -949,13 +952,19 @@ void CascadeCBDC::WalletPersistenceThread::main_loop(){
if(!running) break;

uint64_t persist_count = 0;
uint64_t queued_count = wallet_queue.size();
// uint64_t queued_count = wallet_queue.size();
auto now = std::chrono::steady_clock::now();

if((queued_count >= udl->config.wallet_persistence_batch_min_size) || ((now-wait_start) >= batch_time)){
persist_count = std::min(queued_count,udl->config.wallet_persistence_batch_max_size);
wait_start = now;

if(!wallet_queue.empty() &&
(curr_batch_size < udl->config.wallet_persistence_batch_min_size) ||

@etremel etremel Dec 11, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a logic error because if curr_batch_size and batch_min_size are both 0, the expression will never be true. Since the body of the if statement adds another wallet to the batch, it seems like the head of the if statement should either check if curr_batch_size is greater than the minimum, or less than the maximum (e.g. batch_max_size).

((now-wait_start) >= batch_time)) {
std::size_t wallet_size = mutils::bytes_size(std::get<1>(wallet_queue.front()));
if (curr_batch_size + wallet_size < udl->config.wallet_persistence_batch_min_size){

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a logic error: This if statement will only add wallet_queue.front() to the current batch if the new batch size (curr_batch_size + wallet_size) would be less than the minimum batch size, which is (almost) never true. It should either test if the new batch size would be greater than the minimum, or test if the new batch size would be less than the maximum. I'm not sure which is intended, but I'm guessing it's "greater than the minimum"

to_persist.push_back(wallet_queue.front());
curr_batch_size += wallet_size;
persist_count = to_persist.size();
wallet_queue.pop();
}
// copy out wallets
for(uint64_t i=0;i<persist_count;i++){
to_persist[i] = wallet_queue.front();
Expand Down Expand Up @@ -987,6 +996,10 @@ void CascadeCBDC::WalletPersistenceThread::main_loop(){
TimestampLogger::log(CBDC_TAG_UDL_WALLET_BATCHING,node_id,objects.size(),0);
capi.put_objects_and_forget(objects);
}

// Reset for next batch
curr_batch_size = 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this reset on every loop iteration? Shouldn't it only be reset if the batch was actually sent via put_objects (i.e. inside the above if statement?)

to_persist.clear();
}
}

Expand Down Expand Up @@ -1017,10 +1030,13 @@ void CascadeCBDC::ChainingThread::main_loop(){
if(!running) return;

// thread main loop
std::unordered_map<uint32_t,queued_chain_t*> to_persist;
// std::unordered_map<uint32_t,queued_chain_t*> to_persist;
std::unordered_map<uint32_t,std::vector<queued_chain_t*>> to_persist;
std::unordered_map<uint32_t,std::chrono::steady_clock::time_point> wait_time;
auto batch_time = std::chrono::microseconds(udl->config.chaining_batch_time_us);
while(true){
size_t curr_batch_size = 0;
// while(true){
while(running){
std::unique_lock<std::mutex> lock(thread_mtx);
bool empty = true;
for(auto& item : chain_queues){
Expand All @@ -1039,21 +1055,31 @@ void CascadeCBDC::ChainingThread::main_loop(){
auto& shard = item.first;
auto& queue = item.second;

if (queue.empty()) continue;

if(to_persist.count(shard) == 0){
to_persist[shard] = new queued_chain_t[udl->config.chaining_batch_max_size];
// to_persist[shard] = new queued_chain_t[udl->config.chaining_batch_max_size];
to_persist[shard] = std::vector<queued_chain_t*>();
wait_time[shard] = now;
}

uint64_t queued_count = queue.size();
if((queued_count >= udl->config.chaining_batch_min_size) || ((now-wait_time[shard]) >= batch_time)){
persist_count[shard] = std::min(queued_count,udl->config.chaining_batch_max_size);
wait_time[shard] = now;

// copy out wallets
for(uint64_t i=0;i<persist_count[shard];i++){
to_persist[shard][i] = queue.front();
queue.pop();
}
// if((queued_count >= udl->config.chaining_batch_min_size) || ((now-wait_time[shard]) >= batch_time)){
if(!queue.empty() &&
curr_batch_size < udl->config.chaining_batch_max_size ||
(now - wait_time[shard] >= batch_time)) {

std::size_t tx_size = mutils::bytes_size(queue.front());

if(curr_batch_size + tx_size >= udl->config.tx_persistence_batch_max_size)
break;

// to_persist[shard].push_back(*queue.front());
queued_chain_t* chain_ptr = new queued_chain_t(queue.front());
to_persist[shard].push_back(chain_ptr);
curr_batch_size += tx_size;
persist_count[shard] = to_persist[shard].size();
queue.pop();
}
}

Expand All @@ -1068,24 +1094,24 @@ void CascadeCBDC::ChainingThread::main_loop(){
continue;
}

auto chains = to_persist[shard];
auto& chains = to_persist[shard];

std::vector<ObjectWithStringKey> objects;
objects.reserve(count);

for(uint64_t i=0;i<count;i++){
auto& queued_chain = chains[i];
auto& operation = std::get<0>(queued_chain);
auto& wallet_id = std::get<1>(queued_chain);
auto request = std::get<2>(queued_chain);
auto& operation = std::get<0>(*queued_chain);
auto& wallet_id = std::get<1>(*queued_chain);
auto request = std::get<2>(*queued_chain);
auto& txid = std::get<0>(*request);

if(operation == operation_type_t::FORWARD){ // forward
std::size_t sz = mutils::bytes_size(*request);
uint8_t* buffer = new uint8_t[sz];
mutils::to_bytes(*request, buffer);
objects.emplace_back(CBDC_BUILD_FORWARD_KEY(wallet_id),Blob(buffer,sz));
} else {
} else {
cbdc_request_t dummy_request(txid,{},{},{});
std::size_t sz = mutils::bytes_size(dummy_request);
uint8_t* buffer = new uint8_t[sz];
Expand All @@ -1103,7 +1129,10 @@ void CascadeCBDC::ChainingThread::main_loop(){

TimestampLogger::log(CBDC_TAG_UDL_CHAIN_BATCHING,node_id,objects.size(),shard);
capi.put_objects_and_forget<CBDC_OBJECT_POOL_TYPE>(objects,CBDC_OBJECT_POOL_SUBGROUP,shard,true);
chains.clear();
}
curr_batch_size = 0;
persist_count.clear();
}
}

Expand Down Expand Up @@ -1133,10 +1162,12 @@ void CascadeCBDC::TXPersistenceThread::main_loop(){
if(!running) return;

// thread main loop
std::unordered_map<uint32_t,internal_transaction_t**> to_persist;
std::unordered_map<uint32_t, std::vector<internal_transaction_t*>> to_persist;
std::unordered_map<uint32_t,std::chrono::steady_clock::time_point> wait_time;
auto batch_time = std::chrono::microseconds(udl->config.tx_persistence_batch_time_us);
while(true){

size_t curr_batch_size = 0;
while(running){
std::unique_lock<std::mutex> lock(thread_mtx);
bool empty = true;
for(auto& item : tx_queues){
Expand All @@ -1151,25 +1182,31 @@ void CascadeCBDC::TXPersistenceThread::main_loop(){

std::unordered_map<uint32_t,uint64_t> persist_count;
auto now = std::chrono::steady_clock::now();

// while (true) {
for(auto& item : tx_queues){
auto& shard = item.first;
auto& queue = item.second;

if(queue.empty()) continue;
if(to_persist.count(shard) == 0){
to_persist[shard] = new internal_transaction_t*[udl->config.tx_persistence_batch_max_size];
to_persist[shard] = std::vector<internal_transaction_t*>();
wait_time[shard] = now;
}

uint64_t queued_count = queue.size();
if((queued_count >= udl->config.tx_persistence_batch_min_size) || ((now-wait_time[shard]) >= batch_time)){
persist_count[shard] = std::min(queued_count,udl->config.tx_persistence_batch_max_size);
wait_time[shard] = now;

// copy out wallets
for(uint64_t i=0;i<persist_count[shard];i++){
to_persist[shard][i] = queue.front();
queue.pop();

if(!queue.empty() &&
curr_batch_size < udl->config.tx_persistence_batch_max_size ||
(now - wait_time[shard] >= batch_time)) {

std::size_t tx_size = mutils::bytes_size(queue.front());

if(curr_batch_size + tx_size >= udl->config.tx_persistence_batch_max_size){
break;
}
to_persist[shard].push_back(queue.front());
curr_batch_size += tx_size;
persist_count[shard] = to_persist[shard].size();
queue.pop();
}
}

Expand All @@ -1184,7 +1221,7 @@ void CascadeCBDC::TXPersistenceThread::main_loop(){
continue;
}

auto txs = to_persist[shard];
auto& txs = to_persist[shard];

std::vector<ObjectWithStringKey> objects;
objects.reserve(count);
Expand All @@ -1204,7 +1241,14 @@ void CascadeCBDC::TXPersistenceThread::main_loop(){

TimestampLogger::log(CBDC_TAG_UDL_TX_BATCHING,node_id,objects.size(),shard);
capi.put_objects_and_forget<CBDC_OBJECT_POOL_TYPE>(objects,CBDC_OBJECT_POOL_SUBGROUP,shard);

// Clear the vector for next batch
txs.clear();
}

// Reset batch size after processing
curr_batch_size = 0;
persist_count.clear();
}
}

Expand Down