Do some Db work

This commit is contained in:
Jordan Bancino 2022-11-15 18:20:05 +00:00
parent 9494016d2d
commit 46cc1df385
5 changed files with 238 additions and 10 deletions

195
src/Db.c
View file

@ -25,12 +25,13 @@
#include <Memory.h> #include <Memory.h>
#include <Json.h> #include <Json.h>
#include <Util.h>
#include <pthread.h> #include <pthread.h>
struct Db struct Db
{ {
const char *dir; char *dir;
size_t cacheSize; size_t cacheSize;
size_t maxCache; size_t maxCache;
pthread_mutex_t lock; pthread_mutex_t lock;
@ -44,7 +45,10 @@ struct DbRef
pthread_mutex_t lock; pthread_mutex_t lock;
unsigned long ts; unsigned long ts;
char *file; size_t size;
char *prefix;
char *key;
}; };
static ssize_t DbComputeSize(HashMap *); static ssize_t DbComputeSize(HashMap *);
@ -147,8 +151,32 @@ DbComputeSize(HashMap * json)
return total; return total;
} }
static char *
DbHashKey(char *prefix, char *key)
{
return UtilStringConcat(prefix, key);
}
static char *
DbFileName(Db * db, char *prefix, char *key)
{
char *tmp = UtilStringConcat(prefix, "/");
char *tmp2 = UtilStringConcat(tmp, key);
char *tmp3 = UtilStringConcat(tmp2, ".json");
char *tmp4 = UtilStringConcat(db->dir, "/");
char *tmp5 = UtilStringConcat(tmp4, tmp3);
Free(tmp);
Free(tmp2);
Free(tmp3);
Free(tmp4);
return tmp5;
}
Db * Db *
DbOpen(const char *dir, size_t cache) DbOpen(char *dir, size_t cache)
{ {
Db *db; Db *db;
@ -176,6 +204,10 @@ DbOpen(const char *dir, size_t cache)
return NULL; return NULL;
} }
} }
else
{
db->cache = NULL;
}
return db; return db;
} }
@ -183,13 +215,29 @@ DbOpen(const char *dir, size_t cache)
void void
DbClose(Db * db) DbClose(Db * db)
{ {
char *key;
DbRef *val;
if (!db) if (!db)
{ {
return; return;
} }
pthread_mutex_destroy(&db->lock); pthread_mutex_destroy(&db->lock);
if (db->cache)
{
while (HashMapIterate(db->cache, &key, (void **) &val))
{
Free(key);
JsonFree(val->json);
Free(val->prefix);
Free(val->key);
pthread_mutex_destroy(&val->lock);
Free(val);
}
HashMapFree(db->cache); HashMapFree(db->cache);
}
Free(db); Free(db);
} }
@ -197,31 +245,147 @@ DbClose(Db * db)
DbRef * DbRef *
DbCreate(Db * db, char *prefix, char *key) DbCreate(Db * db, char *prefix, char *key)
{ {
FILE *fp;
char *file;
if (!db || !prefix || !key) if (!db || !prefix || !key)
{ {
return NULL; return NULL;
} }
file = DbFileName(db, prefix, key);
if (UtilLastModified(file))
{
return NULL; return NULL;
} }
return DbLock(db, prefix, key);
}
DbRef * DbRef *
DbLock(Db * db, char *prefix, char *key) DbLock(Db * db, char *prefix, char *key)
{ {
char *file;
char *hash;
DbRef *ref;
if (!db || !prefix || !key) if (!db || !prefix || !key)
{ {
return NULL; return NULL;
} }
ref = NULL;
hash = NULL;
pthread_mutex_lock(&db->lock); pthread_mutex_lock(&db->lock);
/* Check if the item is in the cache */
if (db->cache)
{
hash = DbHashKey(prefix, key);
ref = HashMapGet(db->cache, hash);
}
file = DbFileName(db, prefix, key);
if (ref) /* In cache */
{
unsigned long diskTs = UtilLastModified(file);
if (diskTs > ref->ts)
{
/* File was modified on disk since it was cached */
FILE *fp = fopen(file, "r");
HashMap *json;
if (!fp)
{
ref = NULL;
goto finish;
}
json = JsonDecode(fp);
fclose(fp);
if (!json)
{
ref = NULL;
goto finish;
}
JsonFree(ref->json);
ref->json = json;
ref->ts = diskTs;
ref->size = DbComputeSize(ref->json);
ref->prefix = UtilStringDuplicate(prefix);
ref->key = UtilStringDuplicate(key);
}
}
else
{
/* Not in cache; load from disk */
FILE *fp = fopen(file, "r");
if (!fp)
{
ref = NULL;
goto finish;
}
ref = Malloc(sizeof(DbRef));
if (!ref)
{
fclose(fp);
goto finish;
}
ref->json = JsonDecode(fp);
fclose(fp);
if (!ref->json)
{
Free(ref);
ref = NULL;
goto finish;
}
pthread_mutex_init(&ref->lock, NULL);
ref->ts = UtilServerTs();
ref->size = DbComputeSize(ref->json);
ref->prefix = prefix;
ref->key = key;
/* If cache is enabled, cache this ref */
if (db->cache)
{
HashMapSet(db->cache, hash, ref);
db->cacheSize += ref->size;
/* Cache is full; evict old items */
if (db->cacheSize > db->maxCache)
{
/* TODO */
}
}
}
pthread_mutex_unlock(&db->lock); pthread_mutex_unlock(&db->lock);
return NULL; pthread_mutex_lock(&ref->lock);
finish:
Free(file);
Free(hash);
return ref;
} }
void void
DbUnlock(Db * db, DbRef * ref) DbUnlock(Db * db, DbRef * ref)
{ {
FILE *fp;
char *file;
if (!db || !ref) if (!db || !ref)
{ {
return; return;
@ -229,11 +393,30 @@ DbUnlock(Db * db, DbRef * ref)
pthread_mutex_lock(&db->lock); pthread_mutex_lock(&db->lock);
pthread_mutex_unlock(&db->lock); file = DbFileName(db, ref->prefix, ref->key);
fp = fopen(file, "w");
if (!fp)
{ /* TODO: This seems dangerous */
return; return;
} }
JsonEncode(ref->json, fp);
fflush(fp);
fclose(fp);
pthread_mutex_unlock(&ref->lock);
/* No cache, free it now */
if (!db->cache)
{
JsonFree(ref->json);
Free(ref->prefix);
Free(ref->key);
pthread_mutex_destroy(&ref->lock);
Free(ref);
}
}
HashMap * HashMap *
DbJson(DbRef * ref) DbJson(DbRef * ref)
{ {

View file

@ -34,6 +34,7 @@
#include <errno.h> #include <errno.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/stat.h>
#include <limits.h> #include <limits.h>
unsigned long unsigned long
@ -48,6 +49,23 @@ UtilServerTs(void)
return ts; return ts;
} }
unsigned long
UtilLastModified(char *path)
{
struct stat st;
unsigned long ts;
if (stat(path, &st) == 0)
{
ts = (st.st_mtim.tv_sec * 1000) + (st.st_mtim.tv_nsec / 1000000);
return ts;
}
else
{
return 0;
}
}
char * char *
UtilUtf8Encode(unsigned long utf8) UtilUtf8Encode(unsigned long utf8)
{ {
@ -115,6 +133,27 @@ UtilStringDuplicate(char *inStr)
return outStr; return outStr;
} }
char *
UtilStringConcat(char *str1, char *str2)
{
char *ret;
size_t str1Len = strlen(str1);
size_t str2Len = strlen(str2);
ret = Malloc(str1Len + str2Len + 1);
if (!ret)
{
return NULL;
}
strcpy(ret, str1);
strcpy(ret + str1Len, str2);
return ret;
}
int int
UtilSleepMillis(long ms) UtilSleepMillis(long ms)
{ {

View file

@ -32,7 +32,7 @@ typedef struct Db Db;
typedef struct DbRef DbRef; typedef struct DbRef DbRef;
extern Db * extern Db *
DbOpen(const char *, size_t); DbOpen(char *, size_t);
extern void extern void
DbClose(Db *); DbClose(Db *);

View file

@ -57,6 +57,9 @@
extern unsigned long extern unsigned long
UtilServerTs(void); UtilServerTs(void);
extern unsigned long
UtilLastModified(char *);
/* /*
* Encode a single UTF-8 codepoint as a string buffer containing * Encode a single UTF-8 codepoint as a string buffer containing
* between 1 and 4 bytes. The string buffer is allocated on the heap, * between 1 and 4 bytes. The string buffer is allocated on the heap,
@ -88,6 +91,9 @@ extern char *
extern char * extern char *
UtilStringDuplicate(char *); UtilStringDuplicate(char *);
extern char *
UtilStringConcat(char *, char *);
/* /*
* Sleep for the given number of milliseconds. This is a simple wrapper * Sleep for the given number of milliseconds. This is a simple wrapper
* for nanosleep() that makes its usage much easier. * for nanosleep() that makes its usage much easier.