Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add waitedOn metric #108

Merged
merged 25 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9e8755f
add waited on tracking for methods
leonardoventurini Nov 28, 2023
dac8871
add waited on tracking for pubsub
leonardoventurini Nov 29, 2023
f2faf8e
fix test
leonardoventurini Nov 29, 2023
bc45bcf
ignore async event in tests instead
leonardoventurini Oct 5, 2023
61f94fa
optimize timestamp
leonardoventurini Nov 29, 2023
dd7f69f
Track waitedOn before unblock
zodern Nov 29, 2023
d925490
add wait on tracking to unblock
leonardoventurini Nov 30, 2023
a8d15be
add log
leonardoventurini Nov 30, 2023
f6ccebc
increase threshold
leonardoventurini Nov 30, 2023
cef9a5e
fix: dont count waitedOn twice - rely on trackWaitedOn
renanccastro Dec 28, 2023
c38e121
fix: tests on older meteor versions
renanccastro Dec 28, 2023
a67d89e
fix: flaky test
renanccastro Dec 28, 2023
fd8d156
feat: fix meteor version test
renanccastro Dec 29, 2023
57c44e8
feat: check for this.unblock on tests based on meteor version
renanccastro Dec 29, 2023
b050ca5
fix: remove pub/sub repeated tracking and fix semver check
renanccastro Jan 3, 2024
58ad449
fix: older versions transpilation
renanccastro Jan 3, 2024
65cbc0c
Update zodern:types
zodern Jan 10, 2024
10b1f56
Adjust flaky test
zodern Jan 10, 2024
5b11579
Adjust flaky test
zodern Jan 10, 2024
77a3fd9
Adjust flaky test
zodern Jan 10, 2024
f8f9c7a
Add better messages in flaky tests
zodern Jan 10, 2024
5d6a100
Merge branch 'master' into feature/wait-time-metric
zodern Jan 10, 2024
f27c12d
Adjust flaky test
zodern Jan 10, 2024
8d49b9f
Wait for test code to finish
zodern Jan 10, 2024
410117c
Improve explanation for testing latest release
zodern Jan 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
- '--release 2.7.3'
- '--release 2.8.2'
- '--release 2.9.0'
# Latest version
# Left empty to use latest version
-
steps:
- name: Checkout code
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ versions.json
.idea
.eslintcache
.envrc
/packages/*
1 change: 1 addition & 0 deletions lib/hijack/async.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export function wrapFibers () {
wrapped = true;

let originalYield = Fibers.yield;

Fibers.yield = function () {
let kadiraInfo = Kadira._getInfo();
if (kadiraInfo) {
Expand Down
2 changes: 2 additions & 0 deletions lib/hijack/wrap_session.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { TimeoutManager } from './timeout_manager';

const MAX_PARAMS_LENGTH = 4000;


export function wrapSession (sessionProto) {
let originalProcessMessage = sessionProto.processMessage;
sessionProto.processMessage = function (msg) {
Expand Down Expand Up @@ -101,6 +102,7 @@ export function wrapSession (sessionProto) {
response = Kadira.env.kadiraInfo.withValue(kadiraInfo, function () {
return orginalSubHandler.call(self, msg, unblock);
});

unblock();
} else {
response = orginalSubHandler.call(self, msg, unblock);
Expand Down
13 changes: 12 additions & 1 deletion lib/models/methods.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { _ } from 'meteor/underscore';
const { DDSketch } = require('monti-apm-sketches-js');
import { KadiraModel } from './0model';
import { TracerStore } from '../tracer/tracer_store';
import { Ntp } from '../ntp';
import { calculateWaitedOnTime } from '../utils';

const { DDSketch } = require('monti-apm-sketches-js');

const METHOD_METRICS_FIELDS = ['wait', 'db', 'http', 'email', 'async', 'compute', 'total'];

Expand Down Expand Up @@ -55,6 +57,7 @@
errors: 0,
fetchedDocSize: 0,
sentMsgSize: 0,
waitedOn: 0,
histogram: new DDSketch({
alpha: 0.02
})
Expand Down Expand Up @@ -117,6 +120,14 @@
methodMetrics.sentMsgSize += size;
};

MethodsModel.prototype.trackWaitedOn = function (method, messageQueue) {
const timestamp = Ntp._now();
const dateId = this._getDateId(timestamp);

let methodMetrics = this._getMetrics(dateId, method);
methodMetrics.waitedOn += calculateWaitedOnTime(messageQueue);
};

/*
There are two types of data

Expand All @@ -134,13 +145,13 @@
this.methodMetricsByMinute = Object.create(null);

// create final payload for methodMetrics
for (let key in methodMetricsByMinute) {

Check warning on line 148 in lib/models/methods.js

View workflow job for this annotation

GitHub Actions / build

The body of a for-in should be wrapped in an if statement to filter unwanted properties from the prototype
const methodMetrics = methodMetricsByMinute[key];
// converting startTime into the actual serverTime
let startTime = methodMetrics.startTime;
methodMetrics.startTime = Kadira.syncedDate.syncTime(startTime);

for (let methodName in methodMetrics.methods) {

Check warning on line 154 in lib/models/methods.js

View workflow job for this annotation

GitHub Actions / build

The body of a for-in should be wrapped in an if statement to filter unwanted properties from the prototype
METHOD_METRICS_FIELDS.forEach(function (field) {
methodMetrics.methods[methodName][field] /=
methodMetrics.methods[methodName].count;
Expand Down
16 changes: 13 additions & 3 deletions lib/models/pubsub.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Meteor } from 'meteor/meteor';
import { _ } from 'meteor/underscore';
const logger = Npm.require('debug')('kadira:pubsub');
const { DDSketch } = require('monti-apm-sketches-js');
import { KadiraModel } from './0model';
import { TracerStore } from '../tracer/tracer_store';
import { Ntp } from '../ntp';
import {countKeys, getProperty, iterate} from '../utils';
import { calculateWaitedOnTime, countKeys, getProperty, iterate } from '../utils';

const logger = Npm.require('debug')('kadira:pubsub');
const { DDSketch } = require('monti-apm-sketches-js');

export function PubsubModel () {
this.metricsByMinute = Object.create(null);
Expand Down Expand Up @@ -153,6 +154,7 @@
liveFetchedDocSize: 0,
initiallySentMsgSize: 0,
liveSentMsgSize: 0,
waitedOn: 0,
histogram: new DDSketch({
alpha: 0.02
})
Expand Down Expand Up @@ -231,12 +233,12 @@
let avgObserverReuse = subscriptionData.avgObserverReuse;

// to the averaging
for (let dateId in metricsByMinute) {

Check warning on line 236 in lib/models/pubsub.js

View workflow job for this annotation

GitHub Actions / build

The body of a for-in should be wrapped in an if statement to filter unwanted properties from the prototype
let dateMetrics = metricsByMinute[dateId];
// We need to convert startTime into actual serverTime
dateMetrics.startTime = Kadira.syncedDate.syncTime(dateMetrics.startTime);

for (let publication in metricsByMinute[dateId].pubs) {

Check warning on line 241 in lib/models/pubsub.js

View workflow job for this annotation

GitHub Actions / build

The body of a for-in should be wrapped in an if statement to filter unwanted properties from the prototype
let singlePubMetrics = metricsByMinute[dateId].pubs[publication];
// We only calculate resTime for new subscriptions
singlePubMetrics.resTime /= singlePubMetrics.subs;
Expand Down Expand Up @@ -384,3 +386,11 @@
throw new Error('Kadira: Unknown docs fetched type');
}
};

PubsubModel.prototype.trackWaitedOn = function (name, messageQueue) {
let timestamp = Ntp._now();
let publicationName = this._getPublicationName(name);
let metrics = this._getMetrics(timestamp, publicationName);

metrics.waitedOn += calculateWaitedOnTime(messageQueue);
};
15 changes: 15 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getClientArchVersion } from './common/utils';
import { Ntp } from './ntp';

const { DDSketch } = require('monti-apm-sketches-js');

Expand Down Expand Up @@ -113,3 +114,17 @@ export function pick (obj, keys) {
return result;
}, {});
}

export function calculateWaitedOnTime (messageQueue) {
let waitedOnTime = 0;

const now = Ntp._now();

messageQueue.toArray().forEach(function (msg) {
if (msg._waitEventId) {
waitedOnTime += now - msg._waitEventId.at;
}
});

return waitedOnTime;
}
7 changes: 7 additions & 0 deletions lib/wait_time_builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@ export class WaitTimeBuilder {
}
delete self._currentProcessingMessages[session.id];
unblocked = true;

if (msg.msg === 'method') {
Kadira.models.methods.trackWaitedOn(msg.method, session.inQueue);
} else if (msg.msg === 'sub') {
Kadira.models.pubsub.trackWaitedOn(msg.name, session.inQueue);
}

unblock();

TimeoutManager.clearTimeout();
Expand Down
2 changes: 1 addition & 1 deletion package.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ function configurePackage (api, isTesting) {
api.use('meteorhacks:zones@1.2.1', { weak: true });
api.use('simple:json-routes@2.1.0', { weak: true });
api.use('zodern:meteor-package-versions@0.2.0');
api.use('zodern:types@1.0.9');
api.use('zodern:types@1.0.11');

api.use([
'minimongo', 'mongo', 'ddp', 'ejson', 'ddp-common',
Expand Down
23 changes: 19 additions & 4 deletions tests/_helpers/helpers.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
import { Meteor } from 'meteor/meteor';
import { Random } from 'meteor/random';
import { DDP } from 'meteor/ddp';
const Future = Npm.require('fibers/future');
import { MethodStore, TestData } from './globals';

const Future = Npm.require('fibers/future');

export const GetMeteorClient = function (_url) {
const url = _url || Meteor.absoluteUrl();
return DDP.connect(url, {retry: false});
return DDP.connect(url, {retry: false, });
};

export const waitForConnection = function (client) {
let timeout = Date.now() + 1000;
while (Date.now() < timeout) {
let status = client.status();
if (status.connected) {
return;
}

Meteor._sleepForMs(50);
}

throw new Error('timed out waiting for connection');
};

export const RegisterMethod = function (F) {
Expand All @@ -31,7 +46,7 @@ export const EnableTrackingMethods = function () {
// };
};

export const GetLastMethodEvents = function (_indices) {
export const GetLastMethodEvents = function (_indices, ignore = []) {
if (MethodStore.length < 1) {
return [];
}
Expand All @@ -43,7 +58,7 @@ export const GetLastMethodEvents = function (_indices) {
return events;

function isNotCompute (event) {
return event[0] !== 'compute';
return event[0] !== 'compute' && !ignore.includes(event[0]);
}

function filterFields (event) {
Expand Down
12 changes: 12 additions & 0 deletions tests/_helpers/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@ Meteor.publish('tinytest-data-random', function () {
return TestData.find({aa: {$ne: Random.id()}});
});

Meteor.publish('tinytest-waited-on', function () {
Meteor._sleepForMs(25);
return TestData.find();
});

Meteor.publish('tinytest-waited-on2', function () {
Meteor._sleepForMs(10);
if (this.unblock) this.unblock();
Meteor._sleepForMs(40);
return TestData.find();
});

Meteor.publish('tinytest-data-cursor-fetch', function () {
TestData.find({}).fetch();
this.ready();
Expand Down
3 changes: 2 additions & 1 deletion tests/check_for_oplog.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ Tinytest.addAsync('CheckForOplog - Kadira.checkWhyNoOplog - reactive publish', f

const info = Kadira._getInfo();

const event = _.last(info.trace.events);
const event = _.last(info.trace.events.filter(e => e.type !== 'async'));

observeChangesEvent = _.first(event.nested);

return TestData.find({});
Expand Down
4 changes: 2 additions & 2 deletions tests/hijack/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ Tinytest.add(
});
let client = GetMeteorClient();
let result = client.call(methodId);
let events = GetLastMethodEvents([0, 2]);
let events = GetLastMethodEvents([0, 2], ['async']);
let expected = [
['start',undefined,{userId: null, params: '[]'}],
['wait',undefined,{waitOn: []}],
Expand Down Expand Up @@ -174,7 +174,7 @@ Tinytest.add(
});
let client = GetMeteorClient();
let result = client.call(methodId);
let events = GetLastMethodEvents([0, 2]);
let events = GetLastMethodEvents([0, 2], ['async']);
let expected = [
['start',undefined,{userId: null, params: '[]'}],
['wait',undefined,{waitOn: []}],
Expand Down
2 changes: 1 addition & 1 deletion tests/hijack/mongo_driver_events.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Tinytest.addAsync(
checkRange(stats.checkoutTime, 0, 100, 40000);
checkRange(stats.maxCheckoutTime, 0, 10, 300);
checkRange(stats.pending, 0, 0, 200);
checkRange(stats.checkedOut, 0, 0, 15);
checkRange(stats.checkedOut, 0, 0, 25);
checkRange(stats.created, 0, 1, 100);
done();
}
Expand Down
67 changes: 67 additions & 0 deletions tests/models/methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { EJSON } from 'meteor/ejson';
import { MethodsModel } from '../../lib/models/methods';
import { TestData } from '../_helpers/globals';
import { CleanTestData, GetMeteorClient, RegisterMethod, Wait, WithDocCacheGetSize } from '../_helpers/helpers';
import { Meteor } from 'meteor/meteor';
import { Ntp } from '../../lib/ntp';

Tinytest.add(
'Models - Method - buildPayload simple',
Expand All @@ -21,6 +23,7 @@ Tinytest.add(
count: 2,
errors: 0,
wait: 0,
waitedOn: 0,
db: 0,
http: 0,
email: 0,
Expand Down Expand Up @@ -68,6 +71,7 @@ Tinytest.add(
count: 2,
errors: 1,
wait: 0,
waitedOn: 0,
db: 0,
http: 0,
email: 0,
Expand Down Expand Up @@ -186,6 +190,69 @@ Tinytest.add(
}
);

Tinytest.addAsync('Models - Method - Waited On - track wait time of queued messages', async (test, done) => {
let methodId = RegisterMethod( function (id) {
Meteor._sleepForMs(25);
return id;
});

let client = GetMeteorClient();

for (let i = 0; i < 10; i++) {
client.call(methodId, i, () => {});
}

Meteor._sleepForMs(1000);

const metrics = Kadira.models.methods._getMetrics(Ntp._now(), methodId);

test.isTrue(metrics.waitedOn > 25, `${metrics.waitedOn} should be greater than 25`);
test.isTrue(metrics.waitedOn <= 6000, `${metrics.waitedOn} should be less than 6k`);
console.log(metrics.waitedOn);
done();
});

Tinytest.addAsync('Models - Method - Waited On - check unblock time', async (test, done) => {
let methodId = RegisterMethod( function (id) {
this.unblock();
Meteor._sleepForMs(25);
return id;
});

let client = GetMeteorClient();

for (let i = 0; i < 10; i++) {
client.call(methodId, i, () => {});
}

Meteor._sleepForMs(1000);

const metrics = Kadira.models.methods._getMetrics(Ntp._now(), methodId);

test.isTrue(metrics.waitedOn <= 1, 'waitedOn should be less or equal than 1');

done();
});

Tinytest.addAsync('Models - Method - Waited On - track wait time of next message', async (test, done) => {
let slowMethod = RegisterMethod( function () {
Meteor._sleepForMs(25);
});
let fastMethod = RegisterMethod( function () {});

let client = GetMeteorClient();

client.call(slowMethod, () => {});
client.call(fastMethod, () => {});

Meteor._sleepForMs(200);

const metrics = Kadira.models.methods._getMetrics(Ntp._now(), slowMethod);
test.isTrue(metrics.waitedOn >= 20, `${metrics.waitedOn} should be greater than 20`);

done();
});

export const model = new MethodsModel();

function CreateMethodCompleted (sessionName, methodName, methodId, startTime, methodDelay) {
Expand Down
Loading
Loading