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. | ||||
|  | @ -155,7 +157,7 @@ lsm_error lsm_store_open_read(lsm_entry_handle **out, lsm_store *store, | |||
|  * @param key key to search | ||||
|  */ | ||||
| lsm_error lsm_store_open_write(lsm_entry_handle **out, lsm_store *store, | ||||
|                               const lsm_str *key); | ||||
|                                const lsm_str *key); | ||||
| 
 | ||||
| /**
 | ||||
|  * Close an open entry 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