Skip to content

Commit

Permalink
chore(#9551): updates to rules-engine telemetry (#9596)
Browse files Browse the repository at this point in the history
- changes background refresh telemetry entry name to be more intuitive
- changes changes refresh telemetry entry name to be more intuitive
- adds dirty-contacts entry when refreshing due to changes monitoring

#9551
  • Loading branch information
dianabarsan authored Nov 1, 2024
1 parent d18a402 commit 46a01b9
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 41 deletions.
69 changes: 35 additions & 34 deletions webapp/src/ts/services/rules-engine.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ export class RulesEngineService implements OnDestroy {

const rulesSettings = this.getRulesSettings(rulesEngineContext);
const trackPerformance = this.performanceService.track();
const trackName = { name: [ 'rules-engine', 'initialize' ].join(':') };
const trackName = { name: this.getTelemetryTrackName('initialize') };

await this.rulesEngineCore.initialize(rulesSettings);
const isEnabled = this.rulesEngineCore.isEnabled();
Expand All @@ -142,7 +142,7 @@ export class RulesEngineService implements OnDestroy {
this.debounceActive[this.FRESHNESS_KEY] = {
active: true,
performance: {
name: [ 'rules-engine', 'ensureFreshness', 'cancel' ],
name: this.getTelemetryTrackName( 'background-refresh', 'cancel'),
track: this.performanceService.track()
},
debounceRef: rulesDebounceRef
Expand All @@ -162,7 +162,7 @@ export class RulesEngineService implements OnDestroy {
}

if (debounceInfo.active) { // Debounced function is not executed or cancelled yet.
debounceInfo.performance.track.stop({ name: debounceInfo.performance.name.join(':') });
debounceInfo.performance.track.stop({ name: debounceInfo.performance.name });
}

debounceInfo.debounceRef.cancel();
Expand Down Expand Up @@ -234,17 +234,20 @@ export class RulesEngineService implements OnDestroy {
this.debounceActive[this.CHANGE_WATCHER_KEY].active = false;
this.debounceActive[this.CHANGE_WATCHER_KEY].resolve();

await this.rulesEngineCore.updateEmissionsFor(this.debounceActive[this.CHANGE_WATCHER_KEY].params);
this.observable.next(this.debounceActive[this.CHANGE_WATCHER_KEY].params);
trackPerformance?.stop({ name: [ 'rules-engine', 'update-emissions' ].join(':') });
const contactIds = this.debounceActive[this.CHANGE_WATCHER_KEY].params;
this.telemetryService.record(this.getTelemetryTrackName('refresh', 'dirty-contacts'), contactIds.length);

await this.rulesEngineCore.updateEmissionsFor(contactIds);
this.observable.next(contactIds);
trackPerformance?.stop({ name: this.getTelemetryTrackName('refresh') });
}, this.DEBOUNCE_CHANGE_MILLIS);

this.debounceActive[this.CHANGE_WATCHER_KEY] = {
active: true,
promise: promise,
resolve: resolve,
performance: {
name: [ 'rules-engine', 'update-emissions', 'cancel' ],
name: this.getTelemetryTrackName('refresh', 'cancel'),
track: this.performanceService.track()
},
debounceRef: debounceRef,
Expand Down Expand Up @@ -355,13 +358,13 @@ export class RulesEngineService implements OnDestroy {
}

private async _refreshEmissions() {
const trackName = [ 'rules-engine', 'emissions', 'all-contacts' ];
const trackName = this.getTelemetryTrackName('background-refresh');
let trackPerformanceQueueing;
let trackPerformanceRunning;

await this.initialized;
this.telemetryService.record(
'rules-engine:emissions:dirty-contacts',
this.getTelemetryTrackName('background-refresh', 'dirty-contacts'),
this.rulesEngineCore.getDirtyContacts().length
);
this.cancelDebounce(this.FRESHNESS_KEY);
Expand All @@ -371,27 +374,27 @@ export class RulesEngineService implements OnDestroy {
.on('queued', () => trackPerformanceQueueing = this.performanceService.track())
.on('running', () => {
trackPerformanceRunning = this.performanceService.track();
trackPerformanceQueueing?.stop({ name: [ ...trackName, 'queued' ].join(':') });
trackPerformanceQueueing?.stop({ name: `${trackName}:queued` });
});

if (!trackPerformanceRunning) {
trackPerformanceRunning = this.performanceService.track();
}
trackPerformanceRunning.stop({ name: trackName.join(':') });
trackPerformanceRunning.stop({ name: trackName });
}

fetchTaskDocsForAllContacts() {
return this.ngZone.runOutsideAngular(() => this._fetchTaskDocsForAllContacts());
}

private async _fetchTaskDocsForAllContacts() {
const trackName = [ 'rules-engine', 'tasks', 'all-contacts' ];
const trackName = this.getTelemetryTrackName('tasks', 'all-contacts');
let trackPerformanceQueueing;
let trackPerformanceRunning;

await this.initialized;
this.telemetryService.record(
'rules-engine:tasks:dirty-contacts',
this.getTelemetryTrackName('tasks', 'dirty-contacts'),
this.rulesEngineCore.getDirtyContacts().length
);
this.cancelDebounce(this.FRESHNESS_KEY);
Expand All @@ -401,13 +404,13 @@ export class RulesEngineService implements OnDestroy {
.on('queued', () => trackPerformanceQueueing = this.performanceService.track())
.on('running', () => {
trackPerformanceRunning = this.performanceService.track();
trackPerformanceQueueing?.stop({ name: [ ...trackName, 'queued' ].join(':') });
trackPerformanceQueueing?.stop({ name: `${trackName}:queued` });
});

if (!trackPerformanceRunning) {
trackPerformanceRunning = this.performanceService.track();
}
trackPerformanceRunning.stop({ name: trackName.join(':') });
trackPerformanceRunning.stop({ name: trackName });
return this.hydrateTaskDocs(taskDocs);
}

Expand All @@ -416,13 +419,13 @@ export class RulesEngineService implements OnDestroy {
}

private async _fetchTaskDocsFor(contactIds) {
const trackName = [ 'rules-engine', 'tasks', 'some-contacts' ];
const trackName = this.getTelemetryTrackName('tasks', 'some-contacts');
let trackPerformanceQueueing;
let trackPerformanceRunning;

await this.initialized;
this.telemetryService.record(
'rules-engine:tasks:dirty-contacts',
this.getTelemetryTrackName('tasks', 'dirty-contacts'),
this.rulesEngineCore.getDirtyContacts().length
);
await this.waitForDebounce(this.CHANGE_WATCHER_KEY);
Expand All @@ -431,13 +434,13 @@ export class RulesEngineService implements OnDestroy {
.on('queued', () => trackPerformanceQueueing = this.performanceService.track())
.on('running', () => {
trackPerformanceRunning = this.performanceService.track();
trackPerformanceQueueing?.stop({ name: [ ...trackName, 'queued' ].join(':') });
trackPerformanceQueueing?.stop({ name: `${trackName}:queued` });
});

if (!trackPerformanceRunning) {
trackPerformanceRunning = this.performanceService.track();
}
trackPerformanceRunning?.stop({ name: trackName.join(':') });
trackPerformanceRunning?.stop({ name: trackName });
return this.hydrateTaskDocs(taskDocs);
}

Expand All @@ -446,16 +449,12 @@ export class RulesEngineService implements OnDestroy {
}

private _fetchTasksBreakdown(contactIds?) {
const trackName = [
'rules-engine',
'tasks-breakdown',
contactIds ? 'some-contacts' : 'all-contacts'
];
const trackName = this.getTelemetryTrackName('tasks-breakdown', contactIds ? 'some-contacts' : 'all-contacts');
const trackPerformance = this.performanceService.track();
return this.initialized
.then(() => this.rulesEngineCore.fetchTasksBreakdown(contactIds))
.then(taskBreakdown => {
trackPerformance?.stop({ name: trackName.join(':') });
trackPerformance?.stop({ name: trackName });
return taskBreakdown;
});
}
Expand All @@ -465,7 +464,7 @@ export class RulesEngineService implements OnDestroy {
}

private async _fetchTargets(): Promise<Target[]> {
const trackName = [ 'rules-engine', 'targets' ];
const trackName = this.getTelemetryTrackName('targets');
let trackPerformanceQueueing;
let trackPerformanceRunning;

Expand All @@ -483,27 +482,29 @@ export class RulesEngineService implements OnDestroy {
.on('queued', () => trackPerformanceQueueing = this.performanceService.track())
.on('running', () => {
trackPerformanceRunning = this.performanceService.track();
trackPerformanceQueueing?.stop({ name: [ ...trackName, 'queued' ].join(':') });
trackPerformanceQueueing?.stop({ name: `${trackName}:queued` });
});

if (!trackPerformanceRunning) {
trackPerformanceRunning = this.performanceService.track();
}
trackPerformanceRunning?.stop({ name: trackName.join(':') });
trackPerformanceRunning?.stop({ name: trackName });
return targets;
}

monitorExternalChanges(replicationResult?) {
return this.initialized.then(() => {
return replicationResult
&& replicationResult.docs
&& this.updateEmissionExternalChanges(replicationResult.docs);
});
async monitorExternalChanges(replicationResult?) {
await this.initialized;
return replicationResult?.docs && this.updateEmissionExternalChanges(replicationResult.docs);
}

contactsMarkedAsDirty(callback) {
return this.observable.subscribe(callback);
}

private getTelemetryTrackName(...params:string[]) {
return ['rules-engine', ...params].join(':');
}

}

export enum TargetType {
Expand Down
26 changes: 19 additions & 7 deletions webapp/tests/karma/ts/services/rules-engine.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ describe('RulesEngineService', () => {
expect(rulesEngineCoreStubs.updateEmissionsFor.args[0][0]).to.deep.eq(scenario.expected);
expect(stopPerformanceTrackStub.callCount).to.equal(1);
expect(stopPerformanceTrackStub.args[0][0])
.to.deep.equal({ name: 'rules-engine:update-emissions' });
.to.deep.equal({ name: 'rules-engine:refresh' });
}));
}

Expand Down Expand Up @@ -430,6 +430,7 @@ describe('RulesEngineService', () => {
await service.isEnabled();
await service.fetchTargets(); // clear old timers
stopPerformanceTrackStub.resetHistory();
telemetryService.record.resetHistory();

const callback = sinon.stub();
const subscription = service.contactsMarkedAsDirty(callback);
Expand All @@ -448,13 +449,19 @@ describe('RulesEngineService', () => {
expect(callback.callCount).to.equal(1);

subscription.unsubscribe();

expect(telemetryService.record.calledOnce).to.be.true;
expect(telemetryService.record.args[0]).to.deep.equal([ 'rules-engine:refresh:dirty-contacts', 1 ]);
expect(stopPerformanceTrackStub.callCount).to.equal(1);
expect(stopPerformanceTrackStub.args[0][0]).to.deep.equal({ name: 'rules-engine:refresh' });
}));

it('should debounce multiple incoming changes', fakeAsync(async () => {
service = TestBed.inject(RulesEngineService);
await service.isEnabled();
await service.fetchTargets(); // clear old timers
stopPerformanceTrackStub.resetHistory();
telemetryService.record.resetHistory();

const callback = sinon.stub();
const subscription = service.contactsMarkedAsDirty(callback);
Expand All @@ -475,6 +482,11 @@ describe('RulesEngineService', () => {
expect(rulesEngineCoreStubs.updateEmissionsFor.args[0][0]).to.have.members([ 'p3', '3', '2', 'p1' ]);
expect(callback.callCount).to.equal(1);
subscription.unsubscribe();

expect(telemetryService.record.calledOnce).to.be.true;
expect(telemetryService.record.args[0]).to.deep.equal([ 'rules-engine:refresh:dirty-contacts', 4 ]);
expect(stopPerformanceTrackStub.callCount).to.equal(1);
expect(stopPerformanceTrackStub.args[0][0]).to.deep.equal({ name: 'rules-engine:refresh' });
}));
});

Expand Down Expand Up @@ -554,7 +566,7 @@ describe('RulesEngineService', () => {
expect(telemetryService.record.args[0]).to.deep.equal([ 'rules-engine:tasks:dirty-contacts', 3 ]);
expect(stopPerformanceTrackStub.callCount).to.equal(3);
expect(stopPerformanceTrackStub.args[0][0]).to.deep.equal({ name: 'rules-engine:initialize' });
expect(stopPerformanceTrackStub.args[1][0]).to.deep.equal({ name: 'rules-engine:ensureFreshness:cancel' });
expect(stopPerformanceTrackStub.args[1][0]).to.deep.equal({ name: 'rules-engine:background-refresh:cancel' });
expect(stopPerformanceTrackStub.args[2][0]).to.deep.equal({ name: 'rules-engine:tasks:all-contacts' });
});

Expand Down Expand Up @@ -773,7 +785,7 @@ describe('RulesEngineService', () => {
expect(telemetryService.record.callCount).to.equal(1);
expect(stopPerformanceTrackStub.calledTwice).to.be.true;
expect(stopPerformanceTrackStub.args[0][0]).to.deep.equal({ name: 'rules-engine:initialize' });
expect(stopPerformanceTrackStub.args[1][0]).to.deep.equal({ name: 'rules-engine:emissions:all-contacts' });
expect(stopPerformanceTrackStub.args[1][0]).to.deep.equal({ name: 'rules-engine:background-refresh' });
}));

it('should cancel all ensure freshness threads', async () => {
Expand All @@ -795,7 +807,7 @@ describe('RulesEngineService', () => {
expect(telemetryService.record.args[1]).to.deep.equal(['rules-engine:tasks:dirty-contacts', 0]);
expect(stopPerformanceTrackStub.callCount).to.equal(4);
expect(stopPerformanceTrackStub.args[0][0]).to.deep.equal({ name: 'rules-engine:initialize' });
expect(stopPerformanceTrackStub.args[1][0]).to.deep.equal({ name: 'rules-engine:ensureFreshness:cancel' });
expect(stopPerformanceTrackStub.args[1][0]).to.deep.equal({ name: 'rules-engine:background-refresh:cancel' });
expect(stopPerformanceTrackStub.args[2][0]).to.deep.equal({ name: 'rules-engine:targets' });
expect(stopPerformanceTrackStub.args[3][0]).to.deep.equal({ name: 'rules-engine:tasks:all-contacts' });
});
Expand Down Expand Up @@ -825,7 +837,7 @@ describe('RulesEngineService', () => {
]);
expect(stopPerformanceTrackStub.calledTwice).to.be.true;
expect(stopPerformanceTrackStub.args[0][0]).to.deep.equal({ name: 'rules-engine:initialize' });
expect(stopPerformanceTrackStub.args[1][0]).to.deep.equal({ name: 'rules-engine:ensureFreshness:cancel' });
expect(stopPerformanceTrackStub.args[1][0]).to.deep.equal({ name: 'rules-engine:background-refresh:cancel' });

fetchTargets.events.queued[0]();
fetchTasksFor.events.queued[0]();
Expand Down Expand Up @@ -890,15 +902,15 @@ describe('RulesEngineService', () => {

expect(stopPerformanceTrackStub.calledTwice).to.be.true;
expect(stopPerformanceTrackStub.args[0][0]).to.deep.equal({ name: 'rules-engine:initialize' });
expect(stopPerformanceTrackStub.args[1][0]).to.deep.equal({ name: 'rules-engine:ensureFreshness:cancel' });
expect(stopPerformanceTrackStub.args[1][0]).to.deep.equal({ name: 'rules-engine:background-refresh:cancel' });

await nextTick();

// queued and running events are not emitted!

expect(stopPerformanceTrackStub.callCount).to.equal(5);
expect(stopPerformanceTrackStub.args[0][0]).to.deep.equal({ name: 'rules-engine:initialize' });
expect(stopPerformanceTrackStub.args[1][0]).to.deep.equal({ name: 'rules-engine:ensureFreshness:cancel' });
expect(stopPerformanceTrackStub.args[1][0]).to.deep.equal({ name: 'rules-engine:background-refresh:cancel' });
expect(stopPerformanceTrackStub.args[2][0]).to.deep.equal({ name: 'rules-engine:targets' });
expect(stopPerformanceTrackStub.args[3][0]).to.deep.equal({ name: 'rules-engine:tasks:all-contacts' });
expect(stopPerformanceTrackStub.args[4][0]).to.deep.equal({ name: 'rules-engine:tasks:some-contacts' });
Expand Down

0 comments on commit 46a01b9

Please sign in to comment.