feat(lsm): start of on-disk database
							parent
							
								
									eb9157281b
								
							
						
					
					
						commit
						46f89059e4
					
				| 
						 | 
				
			
			@ -6,12 +6,11 @@
 | 
			
		|||
#include "lsm/str.h"
 | 
			
		||||
 | 
			
		||||
int main() {
 | 
			
		||||
  lsm_str *db_path, *data_dir;
 | 
			
		||||
  lsm_str_init_copy(&db_path, "data/data.db");
 | 
			
		||||
  lsm_str *data_dir;
 | 
			
		||||
  lsm_str_init_copy(&data_dir, "data");
 | 
			
		||||
 | 
			
		||||
  lsm_store *store;
 | 
			
		||||
  lsm_store_load(&store, db_path, data_dir);
 | 
			
		||||
  lsm_store_load(&store, data_dir);
 | 
			
		||||
 | 
			
		||||
  lsm_str *key;
 | 
			
		||||
  lsm_str_init_copy(&key, "key");
 | 
			
		||||
| 
						 | 
				
			
			@ -26,6 +25,10 @@ int main() {
 | 
			
		|||
    lsm_entry_data_append(store, handle, data);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (lsm_entry_sync(store, handle) != lsm_error_ok) {
 | 
			
		||||
    printf("godver");
 | 
			
		||||
    return 1;
 | 
			
		||||
  }
 | 
			
		||||
  lsm_entry_close(handle);
 | 
			
		||||
 | 
			
		||||
  assert(lsm_store_open_read(&handle, store, key) == lsm_error_ok);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -104,10 +104,9 @@ lsm_error lsm_store_init(lsm_store **ptr);
 | 
			
		|||
 * Open the given database file and load it into a new store object.
 | 
			
		||||
 *
 | 
			
		||||
 * @param ptr pointer to store newly allocated store
 | 
			
		||||
 * @param db_path path to the database file
 | 
			
		||||
 * @param data_path path to the data directory
 | 
			
		||||
 */
 | 
			
		||||
lsm_error lsm_store_load(lsm_store **ptr, lsm_str *db_path, lsm_str *data_path);
 | 
			
		||||
lsm_error lsm_store_load(lsm_store **ptr, lsm_str *data_path);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Dealocate an existing lsm_store object.
 | 
			
		||||
| 
						 | 
				
			
			@ -193,6 +192,14 @@ lsm_error lsm_entry_data_append_raw(lsm_store *store, lsm_entry_handle *handle,
 | 
			
		|||
lsm_error lsm_entry_data_read(uint64_t *out, char *buf,
 | 
			
		||||
                              lsm_entry_handle *handle, uint64_t len);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Persist the entry's data to disk.
 | 
			
		||||
 *
 | 
			
		||||
 * @param store store to persist entry in
 | 
			
		||||
 * @param handle handle to entry to persist
 | 
			
		||||
 */
 | 
			
		||||
lsm_error lsm_entry_sync(lsm_store *store, lsm_entry_handle *handle);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Return the length of the entry's data.
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,9 @@
 | 
			
		|||
#include "lsm/str_internal.h"
 | 
			
		||||
#include "lsm/trie.h"
 | 
			
		||||
 | 
			
		||||
#define LSM_DB_FILE_NAME "lsm.db"
 | 
			
		||||
#define LSM_IDX_FILE_NAME "lsm.idx"
 | 
			
		||||
 | 
			
		||||
typedef struct lsm_attr {
 | 
			
		||||
  lsm_attr_type type;
 | 
			
		||||
  lsm_str *str;
 | 
			
		||||
| 
						 | 
				
			
			@ -70,7 +73,21 @@ lsm_error lsm_entry_handle_init(lsm_entry_handle **out);
 | 
			
		|||
struct lsm_store {
 | 
			
		||||
  lsm_trie *trie;
 | 
			
		||||
  lsm_str *data_path;
 | 
			
		||||
  lsm_str *db_path;
 | 
			
		||||
  FILE *db_file;
 | 
			
		||||
  uint64_t db_file_size;
 | 
			
		||||
  pthread_mutex_t db_lock;
 | 
			
		||||
  FILE *idx_file;
 | 
			
		||||
  uint64_t idx_file_size;
 | 
			
		||||
  pthread_mutex_t idx_lock;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Read in the database and construct the in-memory trie index. This function
 | 
			
		||||
 * assumes the provided store is a newly initialized empty store with the
 | 
			
		||||
 * database files opened.
 | 
			
		||||
 *
 | 
			
		||||
 * @param store store to read
 | 
			
		||||
 */
 | 
			
		||||
lsm_error lsm_store_load_db(lsm_store *store);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,20 +22,54 @@ lsm_error lsm_store_init(lsm_store **ptr) {
 | 
			
		|||
    return res;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pthread_mutex_init(&store->db_lock, NULL);
 | 
			
		||||
  pthread_mutex_init(&store->idx_lock, NULL);
 | 
			
		||||
 | 
			
		||||
  *ptr = store;
 | 
			
		||||
 | 
			
		||||
  return lsm_error_ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
lsm_error lsm_store_load(lsm_store **ptr, lsm_str *db_path,
 | 
			
		||||
                         lsm_str *data_path) {
 | 
			
		||||
lsm_error lsm_store_load(lsm_store **ptr, lsm_str *data_path) {
 | 
			
		||||
  lsm_store *store;
 | 
			
		||||
  LSM_RES(lsm_store_init(&store));
 | 
			
		||||
 | 
			
		||||
  // TODO implement all of reading the db file
 | 
			
		||||
  // Try to open an existing db file or create a new one otherwise
 | 
			
		||||
  // This shit is why I need to improve the str library
 | 
			
		||||
  char db_file_path[lsm_str_len(data_path) + strlen(LSM_DB_FILE_NAME) + 2];
 | 
			
		||||
  memcpy(db_file_path, lsm_str_ptr(data_path), lsm_str_len(data_path) * sizeof(char));
 | 
			
		||||
  sprintf(&db_file_path[lsm_str_len(data_path)], "/%s", LSM_DB_FILE_NAME);
 | 
			
		||||
 | 
			
		||||
  FILE *db_file = fopen(db_file_path, "r+b");
 | 
			
		||||
 | 
			
		||||
  if (db_file == NULL) {
 | 
			
		||||
    db_file = fopen(db_file_path, "wb");
 | 
			
		||||
 | 
			
		||||
    if (db_file == NULL) {
 | 
			
		||||
      return lsm_error_failed_io;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Same for idx file
 | 
			
		||||
  char idx_file_path[lsm_str_len(data_path) + strlen(LSM_IDX_FILE_NAME) + 2];
 | 
			
		||||
  memcpy(idx_file_path, lsm_str_ptr(data_path), lsm_str_len(data_path) * sizeof(char));
 | 
			
		||||
  sprintf(&idx_file_path[lsm_str_len(data_path)], "/%s", LSM_IDX_FILE_NAME);
 | 
			
		||||
 | 
			
		||||
  FILE *idx_file = fopen(idx_file_path, "r+b");
 | 
			
		||||
 | 
			
		||||
  if (idx_file == NULL) {
 | 
			
		||||
    idx_file = fopen(idx_file_path, "wb");
 | 
			
		||||
 | 
			
		||||
    if (idx_file == NULL) {
 | 
			
		||||
      return lsm_error_failed_io;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  LSM_RES(lsm_store_load_db(store));
 | 
			
		||||
 | 
			
		||||
  store->db_path = db_path;
 | 
			
		||||
  store->data_path = data_path;
 | 
			
		||||
  store->db_file = db_file;
 | 
			
		||||
  store->idx_file = idx_file;
 | 
			
		||||
 | 
			
		||||
  *ptr = store;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,116 @@
 | 
			
		|||
#include "lsm/store_internal.h"
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
static lsm_error lsm_entry_write_uint64_t(FILE *f, uint64_t num) {
 | 
			
		||||
  size_t res = fwrite(&num, sizeof(uint64_t), 1, f);
 | 
			
		||||
 | 
			
		||||
  // Such a small write should succeed in one go
 | 
			
		||||
  if (res == 0) {
 | 
			
		||||
    return lsm_error_failed_io;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return lsm_error_ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static lsm_error lsm_entry_write_str(FILE *f, lsm_str *s) {
 | 
			
		||||
  uint64_t to_write = lsm_str_len(s);
 | 
			
		||||
  uint64_t written = 0;
 | 
			
		||||
 | 
			
		||||
  do {
 | 
			
		||||
    written += fwrite(lsm_str_ptr(s), sizeof(char), to_write - written, f);
 | 
			
		||||
  } while (written < to_write);
 | 
			
		||||
 | 
			
		||||
  return lsm_error_ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
lsm_error lsm_entry_write_db(uint64_t *size, FILE *db_file, lsm_entry *entry) {
 | 
			
		||||
  // First we write how many attributes follow
 | 
			
		||||
  LSM_RES(lsm_entry_write_uint64_t(db_file, entry->attrs.count));
 | 
			
		||||
  *size = sizeof(uint64_t);
 | 
			
		||||
 | 
			
		||||
  for (uint64_t i = 0; i < entry->attrs.count; i++) {
 | 
			
		||||
    // Write attribute type, length & value
 | 
			
		||||
    LSM_RES(lsm_entry_write_uint64_t(db_file, entry->attrs.items[i].type));
 | 
			
		||||
    LSM_RES(lsm_entry_write_uint64_t(db_file, lsm_str_len(entry->attrs.items[i].str)));
 | 
			
		||||
    LSM_RES(lsm_entry_write_str(db_file, entry->attrs.items[i].str));
 | 
			
		||||
 | 
			
		||||
    *size += 2 * sizeof(uint64_t) + lsm_str_len(entry->attrs.items[i].str) * sizeof(char);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  printf("db size: %lu\n", *size);
 | 
			
		||||
 | 
			
		||||
  return lsm_error_ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
lsm_error lsm_entry_write_idx(uint64_t *size, FILE *idx_file, lsm_entry *entry, uint64_t offset, uint64_t len) {
 | 
			
		||||
  LSM_RES(lsm_entry_write_uint64_t(idx_file, lsm_str_len(entry->key)));
 | 
			
		||||
  LSM_RES(lsm_entry_write_str(idx_file, entry->key));
 | 
			
		||||
  LSM_RES(lsm_entry_write_uint64_t(idx_file, offset));
 | 
			
		||||
  LSM_RES(lsm_entry_write_uint64_t(idx_file, len));
 | 
			
		||||
 | 
			
		||||
  *size = 3 * sizeof(uint64_t) + lsm_str_len(entry->key) * sizeof(char);
 | 
			
		||||
 | 
			
		||||
  return lsm_error_ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
lsm_error lsm_entry_sync(lsm_store *store, lsm_entry_handle *handle) {
 | 
			
		||||
  pthread_mutex_lock(&store->db_lock);
 | 
			
		||||
 | 
			
		||||
  // Append entry to end of database file
 | 
			
		||||
  if (fseek(store->db_file, SEEK_SET, store->db_file_size) != 0) {
 | 
			
		||||
    pthread_mutex_unlock(&store->db_lock);
 | 
			
		||||
 | 
			
		||||
    return lsm_error_failed_io;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint64_t entry_size;
 | 
			
		||||
  lsm_error res = lsm_entry_write_db(&entry_size, store->db_file, handle->wrapper->entry);
 | 
			
		||||
  fflush(store->db_file);
 | 
			
		||||
 | 
			
		||||
  // TODO fsync db file?
 | 
			
		||||
 | 
			
		||||
  if (res != lsm_error_ok) {
 | 
			
		||||
    pthread_mutex_unlock(&store->db_lock);
 | 
			
		||||
 | 
			
		||||
    return res;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  uint64_t entry_index = store->db_file_size;
 | 
			
		||||
  store->db_file_size += entry_size;
 | 
			
		||||
 | 
			
		||||
  pthread_mutex_unlock(&store->db_lock);
 | 
			
		||||
 | 
			
		||||
  // Append entry to index file
 | 
			
		||||
  pthread_mutex_lock(&store->idx_lock);
 | 
			
		||||
 | 
			
		||||
  if (fseek(store->idx_file, SEEK_SET, store->idx_file_size) != 0) {
 | 
			
		||||
    pthread_mutex_unlock(&store->idx_lock);
 | 
			
		||||
 | 
			
		||||
    return lsm_error_failed_io;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  res = lsm_entry_write_idx(&entry_size, store->idx_file, handle->wrapper->entry, entry_index, entry_size);
 | 
			
		||||
  fflush(store->idx_file);
 | 
			
		||||
 | 
			
		||||
  if (res == lsm_error_ok) {
 | 
			
		||||
    store->idx_file_size += entry_size;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pthread_mutex_unlock(&store->idx_lock);
 | 
			
		||||
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
lsm_error lsm_store_load_db(lsm_store *store) {
 | 
			
		||||
  uint64_t key_len;
 | 
			
		||||
  size_t res;
 | 
			
		||||
  lsm_str *key;
 | 
			
		||||
 | 
			
		||||
  while (feof(store->idx_file) > 0) {
 | 
			
		||||
    res = fread(&key_len, sizeof(uint64_t), 1, store->idx_file);
 | 
			
		||||
 | 
			
		||||
    if (res == 0) {
 | 
			
		||||
      return lsm_error_failed_io;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								src/main.c
								
								
								
								
							
							
						
						
									
										25
									
								
								src/main.c
								
								
								
								
							| 
						 | 
				
			
			@ -31,28 +31,27 @@ int main() {
 | 
			
		|||
    critical(1, "Invalid TCP port %s", port_str);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  char file_path[strlen(data_dir) + 12 + 1];
 | 
			
		||||
  sprintf(file_path, "%s/lander.data", data_dir);
 | 
			
		||||
  /* char file_path[strlen(data_dir) + 12 + 1]; */
 | 
			
		||||
  /* sprintf(file_path, "%s/lander.data", data_dir); */
 | 
			
		||||
 | 
			
		||||
  info("Initializing trie from file '%s'", file_path);
 | 
			
		||||
  /* info("Initializing trie from file '%s'", file_path); */
 | 
			
		||||
 | 
			
		||||
  Trie *trie;
 | 
			
		||||
  TrieExitCode res = trie_init(&trie, file_path);
 | 
			
		||||
  /* Trie *trie; */
 | 
			
		||||
  /* TrieExitCode res = trie_init(&trie, file_path); */
 | 
			
		||||
 | 
			
		||||
  if (res != Ok) {
 | 
			
		||||
    critical(1, "An error occured while populating the trie.");
 | 
			
		||||
  }
 | 
			
		||||
  /* if (res != Ok) { */
 | 
			
		||||
  /*   critical(1, "An error occured while populating the trie."); */
 | 
			
		||||
  /* } */
 | 
			
		||||
 | 
			
		||||
  info("Trie initialized and populated with %i entries", trie_size(trie));
 | 
			
		||||
  /* info("Trie initialized and populated with %i entries", trie_size(trie)); */
 | 
			
		||||
 | 
			
		||||
  lander_gctx *c_gctx = lander_gctx_init();
 | 
			
		||||
  c_gctx->data_dir = data_dir;
 | 
			
		||||
  c_gctx->trie = trie;
 | 
			
		||||
  /* c_gctx->trie = trie; */
 | 
			
		||||
 | 
			
		||||
  lsm_str *db_path, *data_dir2;
 | 
			
		||||
  lsm_str_init_copy(&db_path, "data/store.db");
 | 
			
		||||
  lsm_str *data_dir2;
 | 
			
		||||
  lsm_str_init_copy(&data_dir2, "data");
 | 
			
		||||
  lsm_store_load(&c_gctx->store, db_path, data_dir2);
 | 
			
		||||
  lsm_store_load(&c_gctx->store, data_dir2);
 | 
			
		||||
 | 
			
		||||
  http_loop *hl = http_loop_init(
 | 
			
		||||
      lander_routes, sizeof(lander_routes) / sizeof(lander_routes[0]), c_gctx,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue