Skip to content
This repository has been archived by the owner on Dec 1, 2024. It is now read-only.

Commit

Permalink
Add readOnly option (#98)
Browse files Browse the repository at this point in the history
  • Loading branch information
eugeneware authored and vweevers committed Apr 7, 2019
1 parent 5a17438 commit eb8e90b
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 4 deletions.
11 changes: 9 additions & 2 deletions src/database.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,14 @@ Database::~Database () {
/* Calls from worker threads, NO V8 HERE *****************************/

rocksdb::Status Database::OpenDatabase (
rocksdb::Options* options
rocksdb::Options* options,
bool readOnly
) {
return rocksdb::DB::Open(*options, **location, &db);
if (readOnly) {
return rocksdb::DB::OpenForReadOnly(*options, **location, &db);
} else {
return rocksdb::DB::Open(*options, **location, &db);
}
}

rocksdb::Status Database::PutToDatabase (
Expand Down Expand Up @@ -181,6 +186,7 @@ v8::Local<v8::Value> Database::NewInstance (v8::Local<v8::String> &location) {
NAN_METHOD(Database::Open) {
LD_METHOD_SETUP_COMMON(open, 0, 1)

bool readOnly = BooleanOptionValue(optionsObj, "readOnly", false);
bool createIfMissing = BooleanOptionValue(optionsObj, "createIfMissing", true);
bool errorIfExists = BooleanOptionValue(optionsObj, "errorIfExists");
bool compression = BooleanOptionValue(optionsObj, "compression", true);
Expand Down Expand Up @@ -217,6 +223,7 @@ NAN_METHOD(Database::Open) {
, maxOpenFiles
, blockRestartInterval
, maxFileSize
, readOnly
);
// persist to prevent accidental GC
v8::Local<v8::Object> _this = info.This();
Expand Down
2 changes: 1 addition & 1 deletion src/database.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class Database : public Nan::ObjectWrap {
static void Init ();
static v8::Local<v8::Value> NewInstance (v8::Local<v8::String> &location);

rocksdb::Status OpenDatabase (rocksdb::Options* options);
rocksdb::Status OpenDatabase (rocksdb::Options* options, bool readOnly);
rocksdb::Status PutToDatabase (
rocksdb::WriteOptions* options
, rocksdb::Slice key
Expand Down
4 changes: 3 additions & 1 deletion src/database_async.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ OpenWorker::OpenWorker (
, uint32_t maxOpenFiles
, uint32_t blockRestartInterval
, uint32_t maxFileSize
, bool readOnly
) : AsyncWorker(database, callback, "rocksdb:db.open")
, readOnly_(readOnly)
{
rocksdb::LevelDBOptions levelOptions;

Expand Down Expand Up @@ -85,7 +87,7 @@ OpenWorker::~OpenWorker () {
}

void OpenWorker::Execute () {
SetStatus(database->OpenDatabase(options));
SetStatus(database->OpenDatabase(options, readOnly_));
}

/** CLOSE WORKER **/
Expand Down
2 changes: 2 additions & 0 deletions src/database_async.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ class OpenWorker : public AsyncWorker {
, uint32_t maxOpenFiles
, uint32_t blockRestartInterval
, uint32_t maxFileSize
, bool readOnly
);

virtual ~OpenWorker ();
virtual void Execute ();

private:
rocksdb::Options* options;
bool readOnly_;
};

class CloseWorker : public AsyncWorker {
Expand Down
115 changes: 115 additions & 0 deletions test/open-read-only-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
'use strict'

const test = require('tape')
const leveldown = require('../')
const testCommon = require('abstract-leveldown/testCommon')
const fs = require('fs')
const path = require('path')

var location = testCommon.location()

// This is used because it's not sufficient on windows to set a parent folder as readonly
function chmodFilesSync (dir, mode) {
var files = fs.readdirSync(dir)
files.forEach(function (file) {
var fullPath = path.join(dir, file)
fs.chmodSync(fullPath, mode)
})
}

test('setUp', function (t) {
// just in case we thew an error last time and don't have perms to remove db
if (fs.existsSync(location)) {
fs.chmodSync(location, 0o755)
chmodFilesSync(location, 0o755)
}
testCommon.setUp(t)
})

test('test write to read/write database', function (t) {
var db = leveldown(location)
db.open(function (err) {
t.error(err)
db.put('my key', 'my value', function (err) {
t.error(err, 'no write error')
db.get('my key', function (err, value) {
t.error(err, 'no read error')
t.equal(value.toString(), 'my value', 'correct value')
db.close(t.end.bind(t))
})
})
})
})

test('test throw error reading read-only database', function (t) {
chmodFilesSync(location, 0o555)
fs.chmodSync(location, 0o555)
var db = leveldown(location)
db.open(function (err) {
t.ok(err, 'should get error reading read only database')
t.ok(/IO Error/i.test(err && err.message), 'should get io error')
db.close(t.end.bind(t))
})
})

test('test read from a read-only database if readOnly is true', function (t) {
chmodFilesSync(location, 0o555)
fs.chmodSync(location, 0o555)
var db = leveldown(location)
db.open({ readOnly: true }, function (err) {
t.error(err)
db.get('my key', function (err, value) {
t.error(err, 'no read error')
t.equal(value.toString(), 'my value', 'correct value')
db.close(t.end.bind(t))
})
})
})

test('test throw error reading read-only database if readOnly is false', function (t) {
chmodFilesSync(location, 0o555)
fs.chmodSync(location, 0o555)
var db = leveldown(location)
db.open({ readOnly: false }, function (err) {
t.ok(err, 'should get error reading read only database')
t.ok(/IO Error/i.test(err && err.message), 'should get io error')
db.close(t.end.bind(t))
})
})

test('test throw error putting data to read-only db if readOnly is true', function (t) {
chmodFilesSync(location, 0o555)
fs.chmodSync(location, 0o555)
var db = leveldown(location)
db.open({ readOnly: true }, function (err) {
t.error(err)
db.put('my key', 'my value', function (err) {
t.ok(err, 'should get write error')
t.ok(/Not supported operation in read only mode/i.test(err && err.message), 'should get io error')
db.close(t.end.bind(t))
})
})
})

test('test throw error deleting data from read-only db if readOnly is true', function (t) {
chmodFilesSync(location, 0o555)
fs.chmodSync(location, 0o555)
var db = leveldown(location)
db.open({ readOnly: true }, function (err) {
t.error(err)
db.del('my key', function (err) {
t.ok(err, 'should get write error')
t.ok(/Not supported operation in read only mode/i.test(err && err.message), 'should get io error')
db.close(t.end.bind(t))
})
})
})

test('tearDown', function (t) {
// just in case we thew an error last time and don't have perms to remove db
if (fs.existsSync(location)) {
fs.chmodSync(location, 0o755)
chmodFilesSync(location, 0o755)
}
testCommon.tearDown(t)
})

0 comments on commit eb8e90b

Please sign in to comment.