Skip to content

Commit

Permalink
Use dev/inode to accurately identify existing/unique database files, #…
Browse files Browse the repository at this point in the history
  • Loading branch information
kriszyp committed Apr 30, 2022
1 parent 0c6612d commit 5fd717c
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 51 deletions.
3 changes: 3 additions & 0 deletions dependencies/lmdb/libraries/liblmdb/lmdb.h
Expand Up @@ -339,6 +339,9 @@ typedef int (MDB_enc_func)(const MDB_val *src, MDB_val *dst, const MDB_val *key,
*/
typedef void (MDB_sum_func)(const MDB_val *src, MDB_val *dst, const MDB_val *key);
#endif
//<lmdb-js>
typedef int (MDB_check_fd)(const mdb_filehandle_t fd, MDB_env* env);
//</lmdb-js>

/** @defgroup mdb_env Environment Flags
* @{
Expand Down
4 changes: 3 additions & 1 deletion dependencies/lmdb/libraries/liblmdb/mdb.c
Expand Up @@ -5872,6 +5872,8 @@ mdb_env_setup_locks(MDB_env *env, MDB_name *fname, int mode, int *excl)
MDB_OFF_T size, rsize;

rc = mdb_fopen(env, fname, MDB_O_LOCKS, mode, &env->me_lfd);
if (!rc)
rc = ((MDB_check_fd*) env->me_userctx)(env->me_lfd, env);
if (rc) {
/* Omit lockfile if read-only env on read-only filesystem */
if (rc == MDB_ERRCODE_ROFS && (env->me_flags & MDB_RDONLY)) {
Expand Down Expand Up @@ -6300,7 +6302,7 @@ mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode
env->boot_id = strtoll(boot_uuid, &endptr, 16);
}
#endif
/*<lmdb-js>*/
/*</lmdb-js>*/
env->me_path = strdup(path);
env->me_dbxs = calloc(env->me_maxdbs, sizeof(MDB_dbx));
env->me_dbflags = calloc(env->me_maxdbs, sizeof(uint16_t));
Expand Down
86 changes: 51 additions & 35 deletions src/env.cpp
Expand Up @@ -34,6 +34,39 @@ EnvWrap::EnvWrap(const CallbackInfo& info) : ObjectWrap<EnvWrap>(info) {
pthread_mutex_init(this->writingLock, nullptr);
pthread_cond_init(this->writingCond, nullptr);
}
MDB_env* foundEnv;
const int EXISTING_ENV_FOUND = 10;
int checkExistingEnvs(mdb_filehandle_t fd, MDB_env* env) {
uint64_t inode, dev;
#ifdef _WIN32
BY_HANDLE_FILE_INFORMATION fileInformation;
GetFileInformationByHandle(fd, &fileInformation);
dev = fileInformation.dwVolumeSerialNumber;
inode = ((uint64_t) fileInformation.nFileIndexHigh << 32) | fileInformation.nFileIndexLow;
#else
auto st = stat(path);
dev = st.st_dev;
inode = st.st_ino;
#endif
fprintf(stderr, "check existing env at dev %llu inode %llu\n", dev, inode);
for (auto envRef = EnvWrap::envTracking->envs.begin(); envRef != EnvWrap::envTracking->envs.end();) {
if (envRef->dev == dev && envRef->inode == inode) {
fprintf(stderr, "found existing env at dev %llu inode %llu\n", dev, inode);
envRef->count++;
foundEnv = envRef->env;
return EXISTING_ENV_FOUND;
}
++envRef;
}
fprintf(stderr, "no existing env at dev %llu inode %llu\n", dev, inode);
SharedEnv envRef;
envRef.dev = dev;
envRef.inode = inode;
envRef.env = env;
envRef.count = 1;
EnvWrap::envTracking->envs.push_back(envRef);
return 0;
}

EnvWrap::~EnvWrap() {
// Close if not closed already
Expand Down Expand Up @@ -210,24 +243,10 @@ Napi::Value EnvWrap::open(const CallbackInfo& info) {
}
int EnvWrap::openEnv(int flags, int jsFlags, const char* path, char* keyBuffer, Compression* compression, int maxDbs,
int maxReaders, mdb_size_t mapSize, int pageSize, char* encryptionKey) {
pthread_mutex_lock(envTracking->envsLock);
this->keyBuffer = keyBuffer;
this->compression = compression;
this->jsFlags = jsFlags;
MDB_env* env = this->env;
for (auto envPath = envTracking->envs.begin(); envPath != envTracking->envs.end();) {
char* existingPath = envPath->path;
if (!strcmp(existingPath, path)) {
envPath->count++;
mdb_env_close(env);
this->env = envPath->env;
pthread_mutex_unlock(envTracking->envsLock);
if ((jsFlags & DELETE_ON_CLOSE) || (flags & MDB_OVERLAPPINGSYNC))
openEnvWraps.push_back(this);
return 0;
}
++envPath;
}

int rc;
rc = mdb_env_set_maxdbs(env, maxDbs);
if (rc) goto fail;
Expand All @@ -244,7 +263,7 @@ int EnvWrap::openEnv(int flags, int jsFlags, const char* path, char* keyBuffer,
enckey.mv_data = encryptionKey;
enckey.mv_size = 32;
#ifdef MDB_RPAGE_CACHE
rc = mdb_env_set_encrypt(this->env, encfunc, &enckey, 0);
rc = mdb_env_set_encrypt(env, encfunc, &enckey, 0);
#else
rc = -1;
#endif
Expand All @@ -258,32 +277,32 @@ int EnvWrap::openEnv(int flags, int jsFlags, const char* path, char* keyBuffer,
if (flags & MDB_NOLOCK) {
fprintf(stderr, "You chose to use MDB_NOLOCK which is not officially supported by node-lmdb. You have been warned!\n");
}
mdb_env_set_userctx(env, checkExistingEnvs);

// Set MDB_NOTLS to enable multiple read-only transactions on the same thread (in this case, the nodejs main thread)
flags |= MDB_NOTLS;
// TODO: make file attributes configurable
// *String::Utf8Value(Isolate::GetCurrent(), path)
pthread_mutex_lock(envTracking->envsLock);
rc = mdb_env_open(env, path, flags, 0664);
mdb_env_get_flags(env, (unsigned int*) &flags);

if (rc != 0) {
mdb_env_close(env);
goto fail;
}
SharedEnv envPath;
envPath.path = strdup(path);
envPath.env = env;
envPath.count = 1;
envPath.deleteOnClose = jsFlags & DELETE_ON_CLOSE;
envTracking->envs.push_back(envPath);
if (rc == EXISTING_ENV_FOUND) {
fprintf(stderr, "env_open failed because of existing env %p", foundEnv);
env = foundEnv;
} else
goto fail;
}
mdb_env_get_flags(env, (unsigned int*) &flags);
if ((jsFlags & DELETE_ON_CLOSE) || (flags & MDB_OVERLAPPINGSYNC))
openEnvWraps.push_back(this);
pthread_mutex_unlock(envTracking->envsLock);
return 0;

fail:
pthread_mutex_unlock(envTracking->envsLock);
this->env = nullptr;
env = nullptr;
return rc;
}
Napi::Value EnvWrap::getMaxKeySize(const CallbackInfo& info) {
Expand Down Expand Up @@ -316,18 +335,11 @@ NAPI_FUNCTION(setJSFlags) {
#endif


void SharedEnv::finish(bool close) {
}
NAPI_FUNCTION(EnvWrap::onExit) {
// close all the environments
for (auto envWrap : openEnvWraps) {
envWrap->closeEnv();
}
/*pthread_mutex_lock(envTracking->envsLock);
for (auto envPath : envTracking->envs) {
envPath.finish(false);
}
pthread_mutex_unlock(envTracking->envsLock);*/
napi_value returnValue;
RETURN_UNDEFINED;
}
Expand Down Expand Up @@ -370,9 +382,13 @@ void EnvWrap::closeEnv() {
if (envFlags & MDB_OVERLAPPINGSYNC) {
mdb_env_sync(env, 1);
}
char* path;
mdb_env_get_path(env, &((const char*)path));
path = strdup(path);
mdb_env_close(env);
if (envPath->deleteOnClose) {
unlink(envPath->path);
fprintf(stderr, "Closed path %s\n", path);
if (jsFlags & DELETE_ON_CLOSE) {
unlink(path);
//unlink(strcat(envPath->path, "-lock"));
}
envTracking->envs.erase(envPath);
Expand Down
5 changes: 2 additions & 3 deletions src/lmdb-js.h
Expand Up @@ -183,10 +183,9 @@ class CursorWrap;
class SharedEnv {
public:
MDB_env* env;
char* path;
uint64_t dev;
uint64_t inode;
int count;
bool deleteOnClose;
void finish(bool close);
};

const int INTERRUPT_BATCH = 9998;
Expand Down
2 changes: 1 addition & 1 deletion test/index.test.js
Expand Up @@ -1130,7 +1130,7 @@ describe('lmdb-js', function() {
});
});
});
describe.only('Read-only Threads', function() {
describe('Read-only Threads', function() {
this.timeout(1000000);
it('will run a group of threads with read-only transactions', function(done) {
var child = spawn('node', [fileURLToPath(new URL('./readonly-threads.cjs', import.meta.url))]);
Expand Down
15 changes: 4 additions & 11 deletions test/readonly-threads.cjs
Expand Up @@ -38,12 +38,8 @@ if (isMainThread) {

setTimeout(() => {
worker.terminate()
}, 8000);
}, 20);
if (messages.length === workerCount) {
db.close();
for (var i = 0; i < messages.length; i ++) {
assert(messages[i] === value.toString('hex'));
}
console.log("done", threadId)
//setTimeout(() =>
//process.exit(0), 200);
Expand Down Expand Up @@ -72,19 +68,16 @@ if (isMainThread) {
if (msg.key) {
var value = db.get(msg.key);
let lastIterate = db.getRange().iterate()
setInterval(() => {
let interval = setInterval(() => {
db.get(msg.key);
let iterate = db.getRange().iterate();
while(!lastIterate.next().done){}
lastIterate = iterate;
}, 1);
setTimeout(() => {
if (value === null) {
clearInterval(interval)
parentPort.postMessage("");
} else {
parentPort.postMessage(value.toString('hex'));
}
}, 10000);
}, 100);

}
});
Expand Down

0 comments on commit 5fd717c

Please sign in to comment.