(#2867) - do not stack notify change listeners
diff --git a/lib/utils.js b/lib/utils.js
index 4e946eb..1510335 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -326,7 +326,17 @@
   if (this.listeners[id]) {
     return;
   }
+  var self = this;
+  var inprogress = false;
   function eventFunction() {
+    if (!self.listeners[id]) {
+      return;
+    }
+    if (inprogress) {
+      inprogress = 'waiting';
+      return;
+    }
+    inprogress = true;
     db.changes({
       include_docs: opts.include_docs,
       attachments: opts.attachments,
@@ -337,13 +347,21 @@
       doc_ids: opts.doc_ids,
       view: opts.view,
       since: opts.since,
-      query_params: opts.query_params,
-      onChange: function (c) {
-        if (c.seq > opts.since && !opts.cancelled) {
-          opts.since = c.seq;
-          exports.call(opts.onChange, c);
-        }
+      query_params: opts.query_params
+    }).on('change', function (c) {
+      if (c.seq > opts.since && !opts.cancelled) {
+        opts.since = c.seq;
+        exports.call(opts.onChange, c);
       }
+    }).on('complete', function () {
+      if (inprogress === 'waiting') {
+        process.nextTick(function () {
+          self.notify(dbName);
+        });
+      }
+      inprogress = false;
+    }).on('error', function () {
+      inprogress = false;
     });
   }
   this.listeners[id] = eventFunction;
diff --git a/tests/integration/test.changes.js b/tests/integration/test.changes.js
index 22e6a2e..d6a19ad 100644
--- a/tests/integration/test.changes.js
+++ b/tests/integration/test.changes.js
@@ -1726,6 +1726,62 @@
         });
       }, done);
     });
+    it('it handles a bunch of individual changes in live replication',
+      function (done) {
+      var db = new PouchDB(dbs.name);
+      var len = 80;
+      var called = 0;
+      var changes = db.changes({live: true});
+      changes.on('change', function () {
+        called++;
+        if (called === len) {
+          changes.cancel();
+        }
+      }).on('error', done).on('complete', function () {
+        done();
+      });
+      var i = -1;
+      function after() {
+        db.listeners('destroyed').should.have.length.lessThan(5);
+      }
+      while (++i < len) {
+        db.post({}).then(after).catch(done);
+      }
+
+    });
+
+    it('changes-filter without filter', function (done) {
+      var docs1 = [
+        {_id: '0', integer: 0},
+        {_id: '1', integer: 1},
+        {_id: '2', integer: 2},
+        {_id: '3', integer: 3},
+      ];
+      var docs2 = [
+        {_id: '4', integer: 4},
+        {_id: '5', integer: 5},
+        {_id: '6', integer: 6},
+        {_id: '7', integer: 7},
+      ];
+      var db = new PouchDB(dbs.name);
+      var count = 0;
+      db.bulkDocs({ docs: docs1 }, function (err, info) {
+        var changes = db.changes({
+          complete: function (err, result) {
+            result.status.should.equal('cancelled');
+            done();
+          },
+          onChange: function (change) {
+            count += 1;
+            if (count === 8) {
+              changes.cancel();
+            }
+          },
+          live: true
+        });
+        db.bulkDocs({ docs: docs2 });
+      });
+    });
   });
 });