Skip to content
This repository was archived by the owner on Dec 1, 2024. It is now read-only.
/ levelup Public archive

levelup from already-created leveldown#299

Closed
ghost wants to merge 7 commits intomasterfrom
unknown repository
Closed

levelup from already-created leveldown#299
ghost wants to merge 7 commits intomasterfrom
unknown repository

Conversation

@ghost
Copy link
Copy Markdown

@ghost ghost commented Jan 12, 2015

The documentation would seem to suggest that you can pass in an opts.db leveldown object:

  • 'db' (object, default: LevelDOWN): LevelUP is backed by LevelDOWN to provide an interface to LevelDB.

It also mentions that db can be a constructor, but it doesn't mention that this is the only way to create a levelup from a leveldown. Currently if you already have a leveldown instance you've got to do:

var levelup = require('levelup');
var leveldown = require('leveldown');

var down = leveldown('./whatever.db');
var up = levelup({ db: function () { return down });

which is pretty strange since levelup instances are 1:1 with leveldown instances. I really don't see the benefit in passing a constructor function at all. It seems like it should be levelup's job to just upgrade existing leveldown instances, not to instantiate leveldown instances from a constructor with a location.

This patch makes the following work like the documentation suggests it should work:

var levelup = require('levelup');
var leveldown = require('leveldown');

var down = leveldown('./whatever.db');
var up = levelup({ db: down });

The documentation also says:

### levelup(db[, callback ])

which would appear to suggest that you can do levelup(down), but that doesn't actually work.
This patch also adds support for this already-documented form:

var levelup = require('levelup');
var leveldown = require('leveldown');

var down = leveldown('./whatever.db');
var up = levelup(down);

or you could just do:

var levelup = require('levelup');
var leveldown = require('leveldown');

var db = levelup(leveldown('./whatever.db'));

These forms to me seem much closer to the intent of levelup: to wrap features such as encodings and createReadStream on top of a leveldown.

Further out, I'm not really sure what the location property is doing in there at all or what it's for. It seems like handling a location path should be leveldown's job, not levelup. Plus there are more ways to specify a "location" than a simple string value, which leveldowns will be better at providing given their unique specializations.

@rvagg
Copy link
Copy Markdown
Member

rvagg commented Jan 12, 2015

+1 on this from me

location is an awkward quirk of history that we still haven't managed to get away from, perhaps it's time we completely ripped leveldown-by-default out of levelup and left that job to the level package, that way we don't need to do any initialisation or pass anything to a new leveldown object, we can just require a leveldown be passed in here.

For my own use, I almost always use level when I just want a vanilla leveldown+levelup setup.

@dominictarr
Copy link
Copy Markdown
Contributor

how does this handle open/closed state. I think the reason that levelup expects a constructor function is so that it can manage open state, so that it's possible to get a sync open ui. Does this still work if you pass in an already opened leveldown?

@juliangruber
Copy link
Copy Markdown
Member

👍

@mcollina
Copy link
Copy Markdown
Member

👍 for me.

@ghost
Copy link
Copy Markdown
Author

ghost commented Jan 13, 2015

@dominictarr: that does seem to be a problem:

var levelup = require('./');
var leveldown = require('leveldown');

var rows = [
    { type: 'put', key: 'a', value: 555 },
    { type: 'put', key: 'b', value: [ 3, 4, 5 ] },
    { type: 'put', key: 'c', value: { x: 7, y: 8 } }
];

var down = leveldown('/tmp/cool.db');
down.open(function () {
    console.log('open!');

    var up = levelup(down, { valueEncoding: 'json' });
    up.batch(rows, function (err) {
        if (err) return console.error(err);
        up.createReadStream().on('data', console.log);
    });
});

gives this output:

 $ node ex.js
open!

events.js:72
        throw er; // Unhandled 'error' event
              ^
OpenError: IO error: lock /tmp/cool.db/LOCK: already held by process
    at /home/substack/projects/node-levelup/lib/levelup.js:129:34

@ghost
Copy link
Copy Markdown
Author

ghost commented Jan 13, 2015

This patch seems to fix the lock error above, but I'm not sure if there are other implications:

From 39784034c68f561b88fc0ba01a4c1c1a4d3daa9e Mon Sep 17 00:00:00 2001
From: James Halliday <mail@substack.net>
Date: Tue, 13 Jan 2015 00:39:53 -0800
Subject: [PATCH] don't try to open a leveldown

---
 lib/levelup.js |    7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/lib/levelup.js b/lib/levelup.js
index e48e31d..10783b9 100644
--- a/lib/levelup.js
+++ b/lib/levelup.js
@@ -94,7 +94,12 @@ function LevelUP (location, options, callback) {
   // set this.location as enumerable but not configurable or writable
   prr(this, 'location', location, 'e')

-  this.open(callback)
+  if (down) {
+    this._status = 'open'
+    this.db = down
+  } else {
+    this.open(callback)
+  }
 }

 inherits(LevelUP, EventEmitter)
-- 
1.7.10.4

The documentation in leveldown is quite clear that leveldown does not track the open states, so it might be good to add a reminder to the levelup docs that any leveldown that is passed in should already be open.

@juliangruber
Copy link
Copy Markdown
Member

the cleaner solution seems to be to add a .open state to leveldown, so you can pass open and closed leveldown instances. however given our ecosystem, the approach above also sounds good to me

@dominictarr
Copy link
Copy Markdown
Contributor

I agree with julian here, to do this properly we need a state property on leveldown.

@ghost ghost mentioned this pull request Jan 17, 2015
@kesla
Copy link
Copy Markdown
Contributor

kesla commented Mar 17, 2015

+1 Let's get this into levelup 1.0!

@jcrugzz
Copy link
Copy Markdown
Contributor

jcrugzz commented Mar 17, 2015

im +1 on this if it wont block a better implementation (keeping the open/closed state on leveldown). @kesla you know the implications here?

@kesla
Copy link
Copy Markdown
Contributor

kesla commented Mar 18, 2015

@jcrugzz sorry no

@ralphtheninja
Copy link
Copy Markdown
Member

Further out, I'm not really sure what the location property is doing in there at all or what it's for. It seems like handling a location path should be leveldown's job, not levelup. Plus there are more ways to specify a "location" than a simple string value, which leveldowns will be better at providing given their unique specializations.

I agree. Lets get rid of location in a separate PR.

@ralphtheninja
Copy link
Copy Markdown
Member

Moved this to new PR. Closing. Thank you @substack !

@ralphtheninja
Copy link
Copy Markdown
Member

And +1 for having .open state in leveldown

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants