From aff90632534199360c299e829cb618f675290a2c Mon Sep 17 00:00:00 2001 From: etyp Date: Fri, 7 Sep 2018 00:14:03 -0400 Subject: [PATCH 1/2] Track computations from autoruns and ensure they're all stopped on publish stop --- server.coffee | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/server.coffee b/server.coffee index 0496212..ffe2052 100644 --- a/server.coffee +++ b/server.coffee @@ -162,10 +162,12 @@ extendPublish (name, publishFunction, options) -> return handles = [] + trackedComputations = [] # This autorun is nothing special, just that it makes sure handles are stopped when publish stops, # and that you can return cursors from the function which would be automatically published. publish.autorun = (runFunc) -> handle = Tracker.autorun (computation) -> + trackedComputations.push computation result = runFunc.call publish, computation collectionNames = getCollectionNames result @@ -194,6 +196,12 @@ extendPublish (name, publishFunction, options) -> while handles.length handle = handles.shift() handle?.stop() + # Stop computations in case a subscription is stopped before ready on the client and + # the publish is using meteorhacks:unblock's "this.unblock()". Without this, observers + # created from cursors inside the autorun will exist forever. + while trackedComputations.length + computation = trackedComputations.shift() + computation?.stop() result = publishFunction.apply publish, args From 806a4226f7c5b0b3a71ed07d6c3bcb69cdbc1466 Mon Sep 17 00:00:00 2001 From: etyp Date: Fri, 7 Sep 2018 19:53:51 -0400 Subject: [PATCH 2/2] Add tests to guarantee unblock doesnt cause multiplexer leak --- package.js | 3 ++- tests.coffee | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/package.js b/package.js index 46d018d..94b62c7 100644 --- a/package.js +++ b/package.js @@ -53,7 +53,8 @@ Package.onTest(function (api) { api.use([ 'peerlibrary:assert@0.2.5', 'peerlibrary:server-autorun@0.7.1', - 'peerlibrary:classy-test@0.3.0' + 'peerlibrary:classy-test@0.3.0', + 'meteorhacks:unblock' ]); api.add_files([ diff --git a/tests.coffee b/tests.coffee index a590e8e..c0dc4e9 100644 --- a/tests.coffee +++ b/tests.coffee @@ -1,3 +1,5 @@ +allCollections = [] + for idGeneration in ['STRING', 'MONGO'] do (idGeneration) -> if idGeneration is 'STRING' @@ -8,9 +10,13 @@ for idGeneration in ['STRING', 'MONGO'] new Meteor.Collection.ObjectID() Users = new Mongo.Collection "Users_meteor_reactivepublish_tests_#{idGeneration}", {idGeneration} + allCollections.push Users Posts = new Mongo.Collection "Posts_meteor_reactivepublish_tests_#{idGeneration}", {idGeneration} + allCollections.push Posts Addresses = new Mongo.Collection "Addresses_meteor_reactivepublish_tests_#{idGeneration}", {idGeneration} + allCollections.push Addresses Fields = new Mongo.Collection "Fields_meteor_reactivepublish_tests_#{idGeneration}", {idGeneration} + allCollections.push Fields if Meteor.isServer LocalCollection = new Mongo.Collection null, {idGeneration} @@ -244,6 +250,19 @@ for idGeneration in ['STRING', 'MONGO'] @ready() + Meteor.publish "unblocked-users-posts_#{idGeneration}", (userId) -> + @unblock() + + @autorun (computation) => + user = Users.findOne userId, + fields: + posts: 1 + + Posts.find( + _id: + $in: user?.posts or [] + ) + methods = {} methods["insertPost_#{idGeneration}"] = (timestamp) -> check timestamp, Number @@ -883,5 +902,31 @@ for idGeneration in ['STRING', 'MONGO'] @assertEqual LocalCollection.find({}).count(), 15 ] + multiplexerCountBefore = 0 + multiplexerCountAfter = 0 + @unblockedPub: (publishName) -> [ + @runOnServer -> + multiplexerCountBefore = 0 + for collection in allCollections + if collection + multiplexerCountBefore += Object.keys(collection._driver.mongo._observeMultiplexers).length + -> + @userId = generateId() + handle = @subscribe "#{publishName}_#{idGeneration}", @userId + subscriptionId = handle.subscriptionId + handle?.stop() + + Meteor.setTimeout @expect(), 1000 # ms + , + @runOnServer -> + multiplexerCountAfter = 0 + for collection in allCollections + if collection + multiplexerCountAfter += Object.keys(collection._driver.mongo._observeMultiplexers).length + @assertEqual multiplexerCountBefore, multiplexerCountAfter + ] + + testClientUnblockedBasicAutorun: @unblockedPub 'unblocked-users-posts' + # Register the test case. ClassyTestCase.addTest new ReactivePublishTestCase()