feat(lsm): store data files in nested subdirectories
parent
d3652f801c
commit
d2afb98268
|
@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [Unreleased](https://git.rustybever.be/Chewing_Bever/lander/src/branch/dev)
|
||||
|
||||
### Added
|
||||
|
||||
* HTTP Loop
|
||||
* Fully decoupled functionality from Lander-specific code
|
||||
* Users can now define custom global & request-local contexts
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include "lsm.h"
|
||||
#include "lsm/str.h"
|
||||
|
||||
#define LSM_STORE_DATA_LEVELS 3
|
||||
|
||||
/**
|
||||
* A handle referencing an entry inside a store. Read/write operations from/to
|
||||
* the entry go through this handle.
|
||||
|
|
|
@ -133,12 +133,27 @@ lsm_error lsm_entry_disk_update(lsm_entry_handle *handle);
|
|||
/**
|
||||
* Return the length of the path to this entry's data file
|
||||
*/
|
||||
uint64_t lsm_entry_data_path_len(lsm_entry_handle *handle);
|
||||
uint64_t lsm_entry_data_path_len(const lsm_entry_handle *handle);
|
||||
|
||||
/**
|
||||
* Fill in the entry's data file path in the provided buffer. Use
|
||||
* `lsm_entry_data_path_len` to allocate an appropriately-sized buffer
|
||||
*/
|
||||
void lsm_entry_data_path(char *buf, lsm_entry_handle *handle);
|
||||
void lsm_entry_data_path(char *buf, const lsm_entry_handle *handle);
|
||||
|
||||
/**
|
||||
* Open the entry's data file for reading
|
||||
*
|
||||
* @param handle handle to the entry
|
||||
*/
|
||||
lsm_error lsm_entry_data_open_read(lsm_entry_handle *handle);
|
||||
|
||||
/**
|
||||
* Open the entry's data file for writing. The file and all subdirectories in
|
||||
* the data dir are created as needed.
|
||||
*
|
||||
* @param handle handle to the entry
|
||||
*/
|
||||
lsm_error lsm_entry_data_open_write(lsm_entry_handle *handle);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -48,7 +48,8 @@ lsm_error lsm_store_open_read(lsm_entry_handle **out, lsm_store *store,
|
|||
}
|
||||
|
||||
lsm_entry_handle *handle;
|
||||
LSM_RES2(lsm_entry_handle_init(&handle), pthread_rwlock_unlock(&wrapper->lock));
|
||||
LSM_RES2(lsm_entry_handle_init(&handle),
|
||||
pthread_rwlock_unlock(&wrapper->lock));
|
||||
|
||||
handle->wrapper = wrapper;
|
||||
handle->store = store;
|
||||
|
@ -77,7 +78,8 @@ lsm_error lsm_store_open_write(lsm_entry_handle **out, lsm_store *store,
|
|||
}
|
||||
|
||||
lsm_entry_handle *handle;
|
||||
LSM_RES2(lsm_entry_handle_init(&handle), pthread_rwlock_unlock(&wrapper->lock));
|
||||
LSM_RES2(lsm_entry_handle_init(&handle),
|
||||
pthread_rwlock_unlock(&wrapper->lock));
|
||||
|
||||
handle->wrapper = wrapper;
|
||||
handle->store = store;
|
||||
|
@ -149,16 +151,9 @@ lsm_error lsm_entry_data_append(lsm_entry_handle *handle, const lsm_str *data) {
|
|||
|
||||
// Entries don't open their file unless needed
|
||||
if (handle->f == NULL) {
|
||||
char data_path[lsm_entry_data_path_len(handle) + 1];
|
||||
lsm_entry_data_path(data_path, handle);
|
||||
|
||||
FILE *f = fopen(data_path, "ab");
|
||||
|
||||
if (f == NULL) {
|
||||
return lsm_error_failed_io;
|
||||
}
|
||||
|
||||
handle->f = f;
|
||||
// An entry with no existing data will not have a data file yet, so we set
|
||||
// create to true then
|
||||
LSM_RES(lsm_entry_data_open_write(handle));
|
||||
}
|
||||
|
||||
size_t written = 0;
|
||||
|
@ -187,16 +182,7 @@ lsm_error lsm_entry_data_read(uint64_t *out, char *buf,
|
|||
|
||||
// Entries don't open their file unless needed
|
||||
if (handle->f == NULL) {
|
||||
char data_path[lsm_entry_data_path_len(handle) + 1];
|
||||
lsm_entry_data_path(data_path, handle);
|
||||
|
||||
FILE *f = fopen(data_path, "rb");
|
||||
|
||||
if (f == NULL) {
|
||||
return lsm_error_failed_io;
|
||||
}
|
||||
|
||||
handle->f = f;
|
||||
LSM_RES(lsm_entry_data_open_read(handle));
|
||||
}
|
||||
|
||||
uint64_t read;
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "lsm.h"
|
||||
#include "lsm/store.h"
|
||||
#include "lsm/store_internal.h"
|
||||
#include "lsm/str.h"
|
||||
|
||||
|
@ -228,24 +231,86 @@ uint64_t lsm_entry_data_len(lsm_entry_handle *handle) {
|
|||
return handle->wrapper->entry->data_len;
|
||||
}
|
||||
|
||||
uint64_t lsm_entry_data_path_len(lsm_entry_handle *handle) {
|
||||
// [data path]/[entry key][data file suffix]
|
||||
return lsm_str_len(handle->store->data_path) +
|
||||
lsm_str_len(handle->wrapper->entry->key) +
|
||||
strlen(LSM_DATA_FILE_SUFFIX) + 1;
|
||||
uint64_t lsm_entry_data_path_len(const lsm_entry_handle *handle) {
|
||||
const lsm_str *data_path = handle->store->data_path;
|
||||
const lsm_str *key = handle->wrapper->entry->key;
|
||||
|
||||
uint8_t levels =
|
||||
key->len <= LSM_STORE_DATA_LEVELS ? key->len : LSM_STORE_DATA_LEVELS;
|
||||
|
||||
return data_path->len + 1 + 2 * levels + key->len +
|
||||
strlen(LSM_DATA_FILE_SUFFIX);
|
||||
}
|
||||
void lsm_entry_data_path(char *buf, lsm_entry_handle *handle) {
|
||||
lsm_str *data_path = handle->store->data_path;
|
||||
lsm_str *key = handle->wrapper->entry->key;
|
||||
|
||||
memcpy(buf, lsm_str_ptr(data_path), lsm_str_len(data_path));
|
||||
void lsm_entry_data_path(char *path, const lsm_entry_handle *handle) {
|
||||
const lsm_str *data_path = handle->store->data_path;
|
||||
const lsm_str *key = handle->wrapper->entry->key;
|
||||
|
||||
uint64_t index = lsm_str_len(data_path);
|
||||
buf[index] = '/';
|
||||
uint8_t levels =
|
||||
key->len <= LSM_STORE_DATA_LEVELS ? key->len : LSM_STORE_DATA_LEVELS;
|
||||
|
||||
index += 1;
|
||||
memcpy(&buf[index], lsm_str_ptr(key), lsm_str_len(key));
|
||||
memcpy(path, lsm_str_ptr(data_path), data_path->len);
|
||||
path[data_path->len] = '/';
|
||||
|
||||
uint64_t index = data_path->len + 1;
|
||||
|
||||
// Create each directory in the file hierarchy
|
||||
for (uint8_t i = 0; i < levels; i++) {
|
||||
path[index] = lsm_str_char(key, i);
|
||||
path[index + 1] = '/';
|
||||
|
||||
index += 2;
|
||||
}
|
||||
|
||||
memcpy(&path[index], lsm_str_ptr(key), lsm_str_len(key));
|
||||
|
||||
index += lsm_str_len(key);
|
||||
strcpy(&buf[index], LSM_DATA_FILE_SUFFIX);
|
||||
strcpy(&path[index], LSM_DATA_FILE_SUFFIX);
|
||||
}
|
||||
|
||||
lsm_error lsm_entry_data_open_write(lsm_entry_handle *handle) {
|
||||
char path[lsm_entry_data_path_len(handle) + 1];
|
||||
lsm_entry_data_path(path, handle);
|
||||
|
||||
const lsm_str *data_path = handle->store->data_path;
|
||||
const lsm_str *key = handle->wrapper->entry->key;
|
||||
|
||||
uint8_t levels =
|
||||
key->len <= LSM_STORE_DATA_LEVELS ? key->len : LSM_STORE_DATA_LEVELS;
|
||||
|
||||
// Create all required directories in the path
|
||||
for (uint8_t i = 0; i < levels; i++) {
|
||||
path[data_path->len + 2 * (i + 1)] = '\0';
|
||||
|
||||
if ((mkdir(path, 0755) != 0) && (errno != EEXIST)) {
|
||||
return lsm_error_failed_io;
|
||||
}
|
||||
|
||||
path[data_path->len + 2 * (i + 1)] = '/';
|
||||
}
|
||||
|
||||
FILE *f = fopen(path, "ab");
|
||||
|
||||
if (f == NULL) {
|
||||
return lsm_error_failed_io;
|
||||
}
|
||||
|
||||
handle->f = f;
|
||||
|
||||
return lsm_error_ok;
|
||||
}
|
||||
|
||||
lsm_error lsm_entry_data_open_read(lsm_entry_handle *handle) {
|
||||
char path[lsm_entry_data_path_len(handle) + 1];
|
||||
lsm_entry_data_path(path, handle);
|
||||
|
||||
FILE *f = fopen(path, "rb");
|
||||
|
||||
if (f == NULL) {
|
||||
return lsm_error_failed_io;
|
||||
}
|
||||
|
||||
handle->f = f;
|
||||
|
||||
return lsm_error_ok;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue