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