mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
facts: proof-of-concept server-side statistics.
Publishes statistics from the server to a Minimongo collection,
Facts.server. (On the server side, Facts is *not* backed by MongoDB.) By
default, this is available to all users if autopublish is on, or no
users if it is not on, but you can configure this with
Facts.setUserIdFilter.
You can add an ugly view on the stats to your app with {{> serverFacts }}.
Current stats, by package:
- mongo-livedata:
- observe-handles
- live-results-sets (when this is less than observe-handles,
cursor de-dup is helping you)
- livedata
- subscriptions
- crossbar-listeners
- sessions
This commit is contained in:
@@ -13,6 +13,8 @@
|
||||
* Increase the maximum size spiderable will return for a page from 200kB
|
||||
to 5MB.
|
||||
|
||||
* New 'facts' package publishes internal statistics about Meteor.
|
||||
|
||||
* Upgraded dependencies:
|
||||
* SockJS server from 0.3.7 to 0.3.8
|
||||
|
||||
|
||||
1
packages/facts/.gitignore
vendored
Normal file
1
packages/facts/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.build*
|
||||
14
packages/facts/facts.html
Normal file
14
packages/facts/facts.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<template name='serverFacts'>
|
||||
<ul>
|
||||
{{#each factsByPackage}}
|
||||
<li>{{ _id }}
|
||||
<dl>
|
||||
{{#each facts}}
|
||||
<dt>{{ name }}</dt>
|
||||
<dd>{{ value }}</dd>
|
||||
{{/each}}
|
||||
</dl>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</template>
|
||||
79
packages/facts/facts.js
Normal file
79
packages/facts/facts.js
Normal file
@@ -0,0 +1,79 @@
|
||||
Facts = {};
|
||||
|
||||
var serverFactsCollection = 'Facts.server';
|
||||
|
||||
if (Meteor.isServer) {
|
||||
// By default, we publish facts to no user if autopublish is off, and to all
|
||||
// users if autopublish is on.
|
||||
var userIdFilter = function (userId) {
|
||||
return !!Package.autopublish;
|
||||
};
|
||||
|
||||
// XXX make this take effect at runtime too?
|
||||
Facts.setUserIdFilter = function (filter) {
|
||||
userIdFilter = filter;
|
||||
};
|
||||
|
||||
// XXX Use a minimongo collection instead and hook up an observeChanges
|
||||
// directly to a publish.
|
||||
var factsByPackage = {};
|
||||
var activeSubscriptions = [];
|
||||
|
||||
Facts.incrementServerFact = function (pkg, fact, increment) {
|
||||
if (!_.has(factsByPackage, pkg)) {
|
||||
factsByPackage[pkg] = {};
|
||||
factsByPackage[pkg][fact] = increment;
|
||||
_.each(activeSubscriptions, function (sub) {
|
||||
sub.added(serverFactsCollection, pkg, factsByPackage[pkg]);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var packageFacts = factsByPackage[pkg];
|
||||
if (!_.has(packageFacts, fact))
|
||||
factsByPackage[pkg][fact] = 0;
|
||||
factsByPackage[pkg][fact] += increment;
|
||||
var changedField = {};
|
||||
changedField[fact] = factsByPackage[pkg][fact];
|
||||
_.each(activeSubscriptions, function (sub) {
|
||||
sub.changed(serverFactsCollection, pkg, changedField);
|
||||
});
|
||||
};
|
||||
|
||||
// Deferred, because we have an unordered dependency on livedata.
|
||||
// XXX is this safe? could somebody try to connect before Meteor.publish is
|
||||
// called?
|
||||
Meteor.defer(function () {
|
||||
// XXX Also publish facts-by-package.
|
||||
Meteor.publish("facts", function () {
|
||||
var sub = this;
|
||||
if (!userIdFilter(this.userId)) {
|
||||
sub.ready();
|
||||
return;
|
||||
}
|
||||
activeSubscriptions.push(sub);
|
||||
_.each(factsByPackage, function (facts, pkg) {
|
||||
sub.added(serverFactsCollection, pkg, facts);
|
||||
});
|
||||
sub.onStop(function () {
|
||||
activeSubscriptions = _.without(activeSubscriptions, sub);
|
||||
});
|
||||
sub.ready();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
Facts.server = new Meteor.Collection(serverFactsCollection);
|
||||
Meteor.subscribe("facts");
|
||||
|
||||
Template.serverFacts.factsByPackage = function () {
|
||||
return Facts.server.find();
|
||||
};
|
||||
Template.serverFacts.facts = function () {
|
||||
var factArray = [];
|
||||
_.each(this, function (value, name) {
|
||||
if (name !== '_id')
|
||||
factArray.push({name: name, value: value});
|
||||
});
|
||||
return factArray;
|
||||
};
|
||||
}
|
||||
21
packages/facts/package.js
Normal file
21
packages/facts/package.js
Normal file
@@ -0,0 +1,21 @@
|
||||
Package.describe({
|
||||
summary: "Publish internal and custom app statistics"
|
||||
});
|
||||
|
||||
Package.on_use(function (api) {
|
||||
api.use(['underscore'], ['client', 'server']);
|
||||
api.use(['templating', 'mongo-livedata', 'livedata'], ['client']);
|
||||
|
||||
// Detect whether autopublish is used.
|
||||
api.use('autopublish', 'server', {weak: true});
|
||||
|
||||
// Unordered dependency on livedata, since livedata has a (weak) dependency on
|
||||
// us.
|
||||
api.use('livedata', 'server', {unordered: true});
|
||||
|
||||
api.add_files('facts.html', ['client']);
|
||||
api.add_files('facts.js', ['client', 'server']);
|
||||
|
||||
api.export('Facts');
|
||||
});
|
||||
|
||||
@@ -27,8 +27,12 @@ _.extend(DDPServer._InvalidationCrossbar.prototype, {
|
||||
var self = this;
|
||||
var id = self.next_id++;
|
||||
self.listeners[id] = {trigger: EJSON.clone(trigger), callback: callback};
|
||||
Package.facts && Package.facts.Facts.incrementServerFact(
|
||||
"livedata", "crossbar-listeners", 1);
|
||||
return {
|
||||
stop: function () {
|
||||
Package.facts && Package.facts.Facts.incrementServerFact(
|
||||
"livedata", "crossbar-listeners", -1);
|
||||
delete self.listeners[id];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -257,6 +257,9 @@ var Session = function (server, version, socket) {
|
||||
Fiber(function () {
|
||||
self.startUniversalSubs();
|
||||
}).run();
|
||||
|
||||
Package.facts && Package.facts.Facts.incrementServerFact(
|
||||
"livedata", "sessions", 1);
|
||||
};
|
||||
|
||||
_.extend(Session.prototype, {
|
||||
@@ -341,7 +344,6 @@ _.extend(Session.prototype, {
|
||||
view.changed(subscriptionHandle, id, fields);
|
||||
},
|
||||
|
||||
|
||||
startUniversalSubs: function () {
|
||||
var self = this;
|
||||
// Make a shallow copy of the set of universal handlers and start them. If
|
||||
@@ -371,6 +373,8 @@ _.extend(Session.prototype, {
|
||||
// Drop the merge box data immediately.
|
||||
self.collectionViews = {};
|
||||
self.inQueue = null;
|
||||
Package.facts && Package.facts.Facts.incrementServerFact(
|
||||
"livedata", "sessions", -1);
|
||||
},
|
||||
|
||||
// Send a message (doing nothing if no socket is connected right now.)
|
||||
@@ -768,6 +772,9 @@ var Subscription = function (
|
||||
idStringify: LocalCollection._idStringify,
|
||||
idParse: LocalCollection._idParse
|
||||
};
|
||||
|
||||
Package.facts && Package.facts.Facts.incrementServerFact(
|
||||
"livedata", "subscriptions", 1);
|
||||
};
|
||||
|
||||
_.extend(Subscription.prototype, {
|
||||
@@ -855,6 +862,8 @@ _.extend(Subscription.prototype, {
|
||||
return;
|
||||
self._deactivated = true;
|
||||
self._callStopCallbacks();
|
||||
Package.facts && Package.facts.Facts.incrementServerFact(
|
||||
"livedata", "subscriptions", -1);
|
||||
},
|
||||
|
||||
_callStopCallbacks: function () {
|
||||
|
||||
@@ -22,6 +22,9 @@ Package.on_use(function (api) {
|
||||
// runs Meteor.publish while it's loaded.
|
||||
api.use('autopublish', 'server', {weak: true});
|
||||
|
||||
// If the facts package is loaded, publish some statistics.
|
||||
api.use('facts', 'server', {weak: true});
|
||||
|
||||
api.export('DDP');
|
||||
api.export('DDPServer', 'server');
|
||||
|
||||
|
||||
@@ -989,6 +989,9 @@ var LiveResultsSet = function (cursorDescription, mongoHandle, ordered,
|
||||
Meteor.clearInterval(intervalHandle);
|
||||
});
|
||||
}
|
||||
|
||||
Package.facts && Package.facts.Facts.incrementServerFact(
|
||||
"mongo-livedata", "live-results-sets", 1);
|
||||
};
|
||||
|
||||
_.extend(LiveResultsSet.prototype, {
|
||||
@@ -1000,6 +1003,8 @@ _.extend(LiveResultsSet.prototype, {
|
||||
throw new Error("Call _addFirstObserveHandle before polling!");
|
||||
|
||||
self._observeHandles[handle._observeHandleId] = handle;
|
||||
Package.facts && Package.facts.Facts.incrementServerFact(
|
||||
"mongo-livedata", "observe-handles", 1);
|
||||
|
||||
// Run the first _poll() cycle synchronously (delivering results to the
|
||||
// first ObserveHandle).
|
||||
@@ -1115,6 +1120,8 @@ _.extend(LiveResultsSet.prototype, {
|
||||
throw new Error("Duplicate observe handle ID");
|
||||
self._observeHandles[handle._observeHandleId] = handle;
|
||||
--self._addHandleTasksScheduledButNotPerformed;
|
||||
Package.facts && Package.facts.Facts.incrementServerFact(
|
||||
"mongo-livedata", "observe-handles", 1);
|
||||
|
||||
// Send initial adds.
|
||||
if (handle._added || handle._addedBefore) {
|
||||
@@ -1143,6 +1150,8 @@ _.extend(LiveResultsSet.prototype, {
|
||||
if (!_.has(self._observeHandles, handle._observeHandleId))
|
||||
throw new Error("Unknown observe handle ID " + handle._observeHandleId);
|
||||
delete self._observeHandles[handle._observeHandleId];
|
||||
Package.facts && Package.facts.Facts.incrementServerFact(
|
||||
"mongo-livedata", "observe-handles", -1);
|
||||
|
||||
if (_.isEmpty(self._observeHandles) &&
|
||||
self._addHandleTasksScheduledButNotPerformed === 0) {
|
||||
@@ -1151,6 +1160,8 @@ _.extend(LiveResultsSet.prototype, {
|
||||
// - stops the poll timer
|
||||
// - removes us from the invalidation crossbar
|
||||
_.each(self._stopCallbacks, function (c) { c(); });
|
||||
Package.facts && Package.facts.Facts.incrementServerFact(
|
||||
"mongo-livedata", "live-results-sets", -1);
|
||||
// This will cause future _addObserveHandleAndSendInitialAdds calls to
|
||||
// throw.
|
||||
self._observeHandles = null;
|
||||
|
||||
@@ -30,6 +30,9 @@ Package.on_use(function (api) {
|
||||
// (for questionable reasons) initialized by the webapp package.
|
||||
api.use('webapp', 'server', {weak: true});
|
||||
|
||||
// If the facts package is loaded, publish some statistics.
|
||||
api.use('facts', 'server', {weak: true});
|
||||
|
||||
// Stuff that should be exposed via a real API, but we haven't yet.
|
||||
api.export('MongoInternals', 'server');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user