[Pkg-javascript-commits] [node-leveldown] 59/492: fix db GC when using ReadStream x 2

Andrew Kelley andrewrk-guest at moszumanska.debian.org
Sun Jul 6 17:13:44 UTC 2014


This is an automated email from the git hooks/post-receive script.

andrewrk-guest pushed a commit to annotated tag rocksdb-0.10.1
in repository node-leveldown.

commit 39b44bc41aa8e9c04ef2d72db323a7c313dc90c9
Author: Rod Vagg <rod at vagg.org>
Date:   Sun Oct 28 21:41:29 2012 +1100

    fix db GC when using ReadStream x 2
    
    see read-stream-test.js for details on what this odd bug is
---
 lib/read-stream.js       |  3 ++
 package.json             |  2 ++
 test/read-stream-test.js | 73 ++++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 75 insertions(+), 3 deletions(-)

diff --git a/lib/read-stream.js b/lib/read-stream.js
index d1309f5..d8ffd65 100644
--- a/lib/read-stream.js
+++ b/lib/read-stream.js
@@ -33,6 +33,9 @@ function ReadStream (options, db, iteratorFactory) {
   this.readable = true
   this.writable = false
 
+  // purely to keep `db` around until we're done so it's not GCed if the user doesn't keep a ref
+  this._db = db
+
   this._options = extend(extend({}, defaultOptions), options)
   if (typeof this._options.start !== 'undefined')
     this._options.start = toBuffer(this._options.start)
diff --git a/package.json b/package.json
index 58fc2c8..3e26754 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,8 @@
       , "tar"           : "*"
       , "mkfiletree"    : "*"
       , "readfiletree"  : "*"
+      , "slow-stream"   : "*"
+      , "delayed"       : "*"
     }
   , "repository": {
         "type": "git"
diff --git a/test/read-stream-test.js b/test/read-stream-test.js
index 250770e..4b2cb01 100644
--- a/test/read-stream-test.js
+++ b/test/read-stream-test.js
@@ -1,8 +1,13 @@
 /* Copyright (c) 2012 Rod Vagg <@rvagg> */
 
-var buster  = require('buster')
-  , assert  = buster.assert
-  , common  = require('./common')
+var buster     = require('buster')
+  , assert     = buster.assert
+  , levelup    = require('../lib/levelup.js')
+  , common     = require('./common')
+  , SlowStream = require('slow-stream')
+  , delayed    = require('delayed')
+  , rimraf     = require('rimraf')
+  , async      = require('async')
 
 buster.testCase('ReadStream', {
     'setUp': function () {
@@ -493,4 +498,66 @@ buster.testCase('ReadStream', {
         }.bind(this))
       }.bind(this))
     }
+
+    // ok, so here's the deal, this is kind of obscure: when you have 2 databases open and
+    // have a readstream coming out from both of them with no references to the dbs left
+    // V8 will GC one of them and you'll get an failed assert from leveldb.
+    // This ISN'T a problem if you only have one of them open, even if the db gets GCed!
+    // Process:
+    //   * open
+    //   * batch write data
+    //   * close
+    //   * reopen
+    //   * create ReadStream, keeping no reference to the db
+    //   * pipe ReadStream through SlowStream just to make sure GC happens
+    //       - the error should occur here if the bug exists
+    //   * when both streams finish, verify all 'data' events happened
+  , '=>test ReadStream without db ref doesn\'t get GCed': function (done) {
+      var dataSpy1   = this.spy()
+        , dataSpy2   = this.spy()
+        , location1  = common.nextLocation()
+        , location2  = common.nextLocation()
+        , sourceData = this.sourceData
+        , verify     = function () {
+            // no reference to `db` here, should have been GCed by now if it could be
+            assert(dataSpy1.callCount, sourceData.length)
+            assert(dataSpy2.callCount, sourceData.length)
+            async.parallel([ rimraf.bind(null, location1), rimraf.bind(null, location2) ], done)
+          }
+        , execute    = function (d, callback) {
+            // no reference to `db` here, could be GCed
+            d.readStream
+              .pipe(new SlowStream({ maxWriteInterval: 5 }))
+              .on('data', d.spy)
+              .on('end', delayed.delayed(callback, 0.05))
+          }
+        , open       = function (reopen, location, callback) {
+            levelup(location, { createIfMissing: !reopen, errorIfExists: !reopen }, callback)
+          }
+        , write      = function (db, callback) { db.batch(sourceData.slice(), callback) }
+        , close      = function (db, callback) { db.close(callback) }
+        , setup      = function (callback) {
+            async.map([ location1, location2 ], open.bind(null, false), function (err, dbs) {
+              refute(err)
+              if (err) return
+              async.map(dbs, write, function (err) {
+                refute(err)
+                if (err) return
+                async.forEach(dbs, close, callback)
+              })
+            })
+          }
+        , reopen    = function () {
+            async.map([ location1, location2 ], open.bind(null, true), function (err, dbs) {
+              refute(err)
+              if (err) return
+              async.forEach([
+                  { readStream: dbs[0].readStream(), spy: dataSpy1 }
+                , { readStream: dbs[1].readStream(), spy: dataSpy2 }
+              ], execute, verify)
+            })
+          }
+
+      setup(delayed.delayed(reopen, 0.05))
+    }
 })
\ No newline at end of file

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-leveldown.git



More information about the Pkg-javascript-commits mailing list