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

chore: [#176778467] Track loading services details stats #2785

Merged
merged 12 commits into from
Feb 5, 2021
5 changes: 3 additions & 2 deletions ts/sagas/services/refreshStoredServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export function* refreshStoredServices(
);
})
.map(_ => _.service_id);

yield put(loadServicesDetail(serviceDetailIdsToLoad));
if (serviceDetailIdsToLoad.length > 0) {
yield put(loadServicesDetail(serviceDetailIdsToLoad));
}
}
114 changes: 113 additions & 1 deletion ts/sagas/startup/loadServiceDetailRequestHandler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { readableReport } from "italia-ts-commons/lib/reporters";
import { call, Effect, fork, put, take } from "redux-saga/effects";
import { call, Effect, fork, put, take, takeLatest } from "redux-saga/effects";
import { ActionType, getType } from "typesafe-actions";
import { buffers, channel, Channel } from "redux-saga";
import { Millisecond } from "italia-ts-commons/lib/units";
import { BackendClient } from "../../api/backend";
import {
loadServiceDetail,
Expand All @@ -11,6 +12,8 @@ import { SagaCallReturnType } from "../../types/utils";
import { handleOrganizationNameUpdateSaga } from "../services/handleOrganizationNameUpdateSaga";
import { handleServiceReadabilitySaga } from "../services/handleServiceReadabilitySaga";
import { totServiceFetchWorkers } from "../../config";
import { applicationChangeState } from "../../store/actions/application";
import { mixpanelTrack } from "../../mixpanel";

/**
* A generator to load the service details from the Backend
Expand Down Expand Up @@ -77,6 +80,9 @@ function* handleServiceLoadRequest(
export function* watchServicesDetailLoadSaga(
getService: ReturnType<typeof BackendClient>["getService"]
) {
// start a saga to track services detail load stats
yield fork(watchLoadServicesDetailToTrack);

// Create the channel used for the communication with the handlers.
const requestsChannel: Channel<ActionType<
typeof loadServiceDetail.request
Expand All @@ -100,3 +106,109 @@ export function* watchServicesDetailLoadSaga(
);
}
}

/**
* listen for loading services details events to extract some track information
* like amount of details to load and how much time they take
*/
function* watchLoadServicesDetailToTrack() {
yield takeLatest(
[loadServicesDetail, loadServiceDetail.success, applicationChangeState],
action => {
switch (action.type) {
// request to load a set of services detail
// copying object is needed to avoid "immutable" error on frozen objects
case getType(loadServicesDetail):
const stats: ServicesDetailLoadTrack = {
...servicesDetailLoadTrack,
kind: undefined,
startTime: new Date().getTime() as Millisecond,
servicesId: new Set([...action.payload]),
loaded: 0,
toLoad: servicesDetailLoadTrack.servicesId.size
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
toLoad: servicesDetailLoadTrack.servicesId.size
toLoad: action.payload.length

With the current implementation toLoad is always 0.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's a bug, due to refactoring!
Good catch!
fixed
29baf38

};
servicesDetailLoadTrack = stats;
break;
// single service detail is been loaded
case getType(loadServiceDetail.success):
servicesDetailLoadTrack.servicesId.delete(action.payload.service_id);
const statsServiceLoad: ServicesDetailLoadTrack = {
...servicesDetailLoadTrack,
loaded:
servicesDetailLoadTrack.toLoad -
servicesDetailLoadTrack.servicesId.size
};
servicesDetailLoadTrack = statsServiceLoad;
if (statsServiceLoad.servicesId.size === 0) {
// all service are been loaded
trackServicesDetailLoad({
...servicesDetailLoadTrack,
kind: "COMPLETE",
loadingTime: (new Date().getTime() -
servicesDetailLoadTrack.startTime) as Millisecond
});
Copy link
Contributor

@fabriziofff fabriziofff Feb 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With some multiple tries on Android, I ran into this case (kind
PARTIAL):
Schermata 2021-02-05 alle 15 07 18

My proposal:
create a common function to calculate the loading time

const calculateLoadingTime = (startTime: Millisecond): Millisecond =>
  (startTime !== 0 ? new Date().getTime() - startTime : 0) as Millisecond;

and use it at line 147 and 163.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this scenario is when the app changes state but actually it is not loading any service
I add your suggestion and a condition to send "PARTIAL" only when there is a services loading running

16b1481

}

break;
// app changes state
case getType(applicationChangeState):
/**
* if the app went in inactive or background state these measurements
* could be not valid since the OS could apply a freeze or a limitation around the app context
* so the app could run but with few limitations
*/
if (action.payload !== "active") {
trackServicesDetailLoad({
...servicesDetailLoadTrack,
loadingTime: (new Date().getTime() -
servicesDetailLoadTrack.startTime) as Millisecond,
kind: "PARTIAL"
});
}
// app comes back active, restore stats
else if (action.payload === "active") {
servicesDetailLoadTrack = {
...servicesDetailLoadTrack,
kind: undefined,
startTime: new Date().getTime() as Millisecond,
loaded: 0,
toLoad: servicesDetailLoadTrack.servicesId.size
};
}
break;
}
}
);
}

type ServicesDetailLoadTrack = {
// when loading starts
startTime: Millisecond;
// the amount of loading millis
loadingTime: Millisecond;
// the amount of services detail to load
toLoad: number;
// the amount of services detail loaded
loaded: number;
// the set of the services id that remain to be loaded
servicesId: Set<string>;
// COMPLETE: all services detail are been loaded
// PARTIAL: a sub-set of services detail to load are been loaded
kind?: "COMPLETE" | "PARTIAL";
};
// eslint-disable-next-line functional/no-let
let servicesDetailLoadTrack: ServicesDetailLoadTrack = {
startTime: 0 as Millisecond,
loadingTime: 0 as Millisecond,
toLoad: 0,
loaded: 0,
servicesId: new Set<string>()
};

const trackServicesDetailLoad = (trackingStats: ServicesDetailLoadTrack) => {
void mixpanelTrack("SERVICES_DETAIL_LOADING_STATS", {
...trackingStats,
// drop servicesId since it is not serialized in mixpanel and it could be an extra overhead on sending
servicesId: undefined
});
};