Goddess of Justice DB, the database used for storage on IzaroDFS
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

387 lines
9.7 KiB

#pragma once
#include "fsized_map.h"
#include <chrono>
#include <random>
struct metadata_t{
bitops::regulated<uint64_t> record_cnt;
bitops::regulated<uint64_t> page_cnt;
bitops::regulated<uint64_t> delete_cnt;
bitops::regulated<uint64_t> last_page;
bitops::regulated<uint64_t> last_delete;
};
class database {
database(){}
public:
mmap_array<std::pair<bitops::regulated<uint64_t>, record>> records;
mmap_array<db_page> pages;
mmap_array<metadata_t> metadata;
mmap_array<size_t> delete_table;
database(
const std::string& records,
const std::string& pages,
const std::string& deletions,
const std::string& meta,
const size_t& record_cnt = 4096,
const size_t& page_cnt = 4096,
const size_t& delete_cnt = 512
)
: records{record_cnt, records}
, pages{page_cnt, pages}
, metadata{(size_t)1, meta}
, delete_table{delete_cnt, deletions}
{}
static database&& create(const std::string dir, size_t page_nb) {
database ret{
dir+"records",
dir+"pages",
dir+"deleted",
dir+"meta",
page_nb+page_nb/2,
page_nb,
page_nb/8
};
for(auto& n : ret.records)
{
n.second.timestamp = 0;
n.second.offset = 0;
}
for(auto& n : ret.delete_table)
{
n = std::numeric_limits<size_t>::max();
}
(*ret.metadata).last_page = 0;
(*ret.metadata).last_delete = 0;
(*ret.metadata).record_cnt = page_nb+page_nb/2;
(*ret.metadata).page_cnt = page_nb;
(*ret.metadata).delete_cnt = page_nb/8;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wreturn-local-addr"
return std::move(ret);
#pragma GCC diagnostic pop
}
static database&& open(const std::string dir) {
mmap_array<metadata_t> tmp{(size_t)1, dir+"meta"};
database ret{
dir+"records",
dir+"pages",
dir+"deleted",
dir+"meta",
(*tmp).record_cnt,
(*tmp).page_cnt,
(*tmp).delete_cnt
};
tmp.clear();
ret.records.enforce_caching();
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wreturn-local-addr"
return std::move(ret);
#pragma GCC diagnostic pop
}
/**************************************************************************
* *
* NO CONFIRM OPS *
* *
* *
*************************************************************************/
record write(const record_identifier& target, const db_page& value){
uint64_t page = std::numeric_limits<uint64_t>::max();;
size_t off = std::numeric_limits<size_t>::max();
if(metadata[0].last_delete>0)
{
off = (*metadata).last_delete;
page = delete_table[off-1];
} else {
page = (*metadata).last_page;
if(page>=pages.size()) {
throw std::runtime_error("PAGE STARVATION! MUST EXIT NOW");
}
}
if(page == std::numeric_limits<uint64_t>::max())
{
throw std::runtime_error("PAGE ERROR! MUST EXIT NOW");
}
pages[page] = value;
uint64_t hashed = std::hash<record_identifier>{}(target);
uint64_t hashed_roll = hashed;
bool succeed = false;
uint64_t ts = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
std::pair<bitops::regulated<uint64_t>,record> tmp{0, record{}};
tmp.first = hashed;
tmp.second.record_head.split = target;
tmp.second.timestamp = ts;
tmp.second.offset = page;
tmp.second.flags = (uint32_t)record_flags::confirmation;
do{
uint64_t pos = hashed_roll % records.size();
switch (static_cast<uint64_t>(records[pos].second.timestamp)) {
case 0:
[[fallthrough]];
case std::numeric_limits<uint64_t>::max():
records[pos] = tmp;
succeed = true;
break;
default:
break;
}
hashed_roll++;
}while(!succeed);
if(off != std::numeric_limits<size_t>::max())
{
(*metadata).last_delete += -1;
delete_table[off] = std::numeric_limits<size_t>::max();
} else {
(*metadata).last_page += (size_t)1;
}
return tmp.second;
}
std::pair<record, db_page> read(const record_identifier& target) {
std::pair<record, db_page> ret;
ret.second.fill(0);
uint64_t hashed = std::hash<record_identifier>{}(target);
uint64_t hashed_roll = hashed;
do{
uint64_t pos = hashed_roll % records.size();
auto& value = records[pos].second;
switch (static_cast<uint64_t>(value.timestamp)) {
case 0:
return ret;
case std::numeric_limits<uint64_t>::max():
break;
default:
if(records[pos].first == hashed)
if(std::hash<record_identifier>{}(value.record_head.split) == hashed)
{
if(ret.first.timestamp<value.timestamp)
{
ret.first = value;
ret.second = pages[value.offset];
}
break;
}
}
hashed_roll++;
}while(true);
return ret;
}
void remove(const record_identifier& target) {
uint64_t hashed = std::hash<record_identifier>{}(target);
uint64_t hashed_roll = hashed;
do{
uint64_t pos = hashed_roll % records.size();
auto& value = records[pos].second;
switch (static_cast<uint64_t>(value.timestamp)) {
case 0:
return;
case std::numeric_limits<uint64_t>::max():
break;
default:
if(records[pos].first == hashed)
if(std::hash<record_identifier>{}(value.record_head.split) == hashed)
{
value.timestamp = std::numeric_limits<uint64_t>::max();
(*metadata).last_delete+=1;
delete_table[(*metadata).last_delete-1] = value.offset;
value.offset = 0;
break;
}
}
hashed_roll++;
}while(true); // return only happens on hitting a case 0
}
void rollback(const record_identifier&) {
}
/**************************************************************************
* *
* CONFIRM OPS *
* *
* *
*************************************************************************/
record stepped_write(const record_identifier& target, const db_page& value){
uint64_t page = std::numeric_limits<uint64_t>::max();;
size_t off = std::numeric_limits<size_t>::max();
if(metadata[0].last_delete>0)
{
off = (*metadata).last_delete;
page = delete_table[off-1];
} else {
page = (*metadata).last_page;
if(page>=pages.size()) {
throw std::runtime_error("PAGE STARVATION! MUST EXIT NOW");
}
}
if(page == std::numeric_limits<uint64_t>::max())
{
throw std::runtime_error("PAGE ERROR! MUST EXIT NOW");
}
pages[page] = value;
uint64_t hashed = std::hash<record_identifier>{}(target);
uint64_t hashed_roll = hashed;
bool succeed = false;
uint64_t ts = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
std::pair<bitops::regulated<uint64_t>,record> tmp{0, record{}};
tmp.first = hashed;
tmp.second.record_head.split = target;
tmp.second.timestamp = ts;
tmp.second.offset = page;
tmp.second.flags = 0;
do{
uint64_t pos = hashed_roll % records.size();
switch (static_cast<uint64_t>(records[pos].second.timestamp)) {
case 0:
[[fallthrough]];
case std::numeric_limits<uint64_t>::max():
records[pos] = tmp;
succeed = true;
break;
default:
break;
}
hashed_roll++;
}while(!succeed);
if(off != std::numeric_limits<size_t>::max())
{
(*metadata).last_delete += -1;
delete_table[off] = std::numeric_limits<size_t>::max();
} else {
(*metadata).last_page += (size_t)1;
}
return tmp.second;
}
record try_allocate(const record_identifier& target)
{
auto attempt = read(target);
if(attempt.first.timestamp == 0)
{
db_page rnd_page;
{
std::random_device dev;
std::minstd_rand temprng(dev());
auto tmp = (std::array<uint32_t, sizeof(db_page)/sizeof(uint32_t)>*)&rnd_page;
std::generate(tmp->begin(), tmp->end(), temprng);
}
return write(target, rnd_page);
}
return record{};
}
std::pair<record, db_page> stepped_read(const record_identifier& target) {
std::pair<record, db_page> ret;
ret.second.fill(0);
uint64_t hashed = std::hash<record_identifier>{}(target);
uint64_t hashed_roll = hashed;
do{
uint64_t pos = hashed_roll % records.size();
auto& value = records[pos].second;
switch (static_cast<uint64_t>(value.timestamp)) {
case 0:
return ret;
case std::numeric_limits<uint64_t>::max():
break;
default:
if(records[pos].first == hashed)
if(std::hash<record_identifier>{}(value.record_head.split) == hashed)
{
if(static_cast<uint32_t>(value.flags) & (uint32_t)record_flags::confirmation)
if(ret.first.timestamp<value.timestamp)
{
ret.first = value;
ret.second = pages[value.offset];
}
break;
}
}
hashed_roll++;
}while(true);
return ret;
}
void stepped_remove(const record_identifier& target) {
remove(target);
}
void confirm(const record_identifier& target, const bitops::regulated<uint64_t>& timestamp) {
uint64_t hashed = std::hash<record_identifier>{}(target);
uint64_t hashed_roll = hashed;
do{
uint64_t pos = hashed_roll % records.size();
auto& value = records[pos].second;
switch (static_cast<uint64_t>(value.timestamp)) {
case 0:
return;
case std::numeric_limits<uint64_t>::max():
break;
default:
if(records[pos].first == hashed)
if(std::hash<record_identifier>{}(value.record_head.split) == hashed)
{
if(timestamp == value.timestamp)
{
value.flags = (uint32_t)value.flags | (uint32_t)record_flags::confirmation;
}
break;
}
}
hashed_roll++;
}while(true);
}
};