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 correlation id in routerlicious-driver #4734

Merged
merged 15 commits into from
Jan 11, 2021
86 changes: 43 additions & 43 deletions lerna-package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/drivers/routerlicious-driver/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@
"@fluidframework/protocol-base": "^0.1017.1",
"@fluidframework/protocol-definitions": "^0.1017.1",
"@fluidframework/server-services-client": "^0.1017.1",
"@fluidframework/telemetry-utils": "^0.33.0",
"assert": "^2.0.0",
"axios": "^0.21.1",
"debug": "^4.1.1",
"isomorphic-ws": "^4.0.1",
"jwt-decode": "^2.2.0",
"socket.io-client": "^2.1.1",
"uuid": "^8.3.1",
"ws": "^6.1.2"
},
"devDependencies": {
Expand All @@ -55,6 +57,7 @@
"@types/mocha": "^5.2.5",
"@types/nock": "^9.3.0",
"@types/socket.io-client": "^1.4.32",
"@types/uuid": "^8.3.0",
"@types/ws": "^6.0.1",
"@typescript-eslint/eslint-plugin": "~4.2.0",
"@typescript-eslint/parser": "~4.2.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Licensed under the MIT License.
*/

import { ITelemetryLogger } from "@fluidframework/common-definitions";
import { IDocumentService, IResolvedUrl } from "@fluidframework/driver-definitions";
import { IErrorTrackingService } from "@fluidframework/protocol-definitions";
import { ICredentials } from "@fluidframework/server-services-client";
Expand All @@ -27,6 +28,7 @@ export const createDocumentService2 = (
errorTracking: IErrorTrackingService = new DefaultErrorTracking(),
disableCache = false,
historianApi = true,
logger: ITelemetryLogger,
credentials?: ICredentials): IDocumentService => new DocumentService2(
resolvedUrl,
ordererUrl,
Expand All @@ -36,6 +38,7 @@ export const createDocumentService2 = (
disableCache,
historianApi,
credentials,
logger,
tokenProvider,
tenantId,
documentId);
23 changes: 18 additions & 5 deletions packages/drivers/routerlicious-driver/src/deltaStorageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
* Licensed under the MIT License.
*/

import { OutgoingHttpHeaders } from "http";
import querystring from "querystring";
import { fromUtf8ToBase64 } from "@fluidframework/common-utils";
import { IDeltaStorageService, IDocumentDeltaStorageService } from "@fluidframework/driver-definitions";
import Axios from "axios";
import * as uuid from "uuid";
import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
import { readAndParse } from "@fluidframework/driver-utils";
import { ITelemetryLogger } from "@fluidframework/common-definitions";
import { ITokenProvider } from "./tokens";
import { DocumentStorageService } from "./documentStorageService";

Expand Down Expand Up @@ -44,7 +47,10 @@ export class DocumentDeltaStorageService implements IDocumentDeltaStorageService
* Provides access to the underlying delta storage on the server for routerlicious driver.
*/
export class DeltaStorageService implements IDeltaStorageService {
constructor(private readonly url: string, private readonly tokenProvider: ITokenProvider) {
constructor(
private readonly url: string,
private readonly tokenProvider: ITokenProvider,
private readonly logger: ITelemetryLogger | undefined) {
}

public async get(
Expand All @@ -54,22 +60,29 @@ export class DeltaStorageService implements IDeltaStorageService {
to?: number): Promise<ISequencedDocumentMessage[]> {
const query = querystring.stringify({ from, to });

let headers: { Authorization: string } | null = null;
const headers: OutgoingHttpHeaders = {
"x-correlation-id": uuid.v4(),
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the value of this ID?
I can see the value if it was written to client side telemetry, then we can use it to correlate activity between client & server. But with this code, it will land only on server, so server can generate ID with the same result, no?

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 good point. We added correlation id in server side and this PR is just for the completeness for the server side to receive http requests with correlation id in the header. As for client side telemetry, I didn't go deep into that. Could you explain a little bit more how we can add to client side telemetry? Or maybe we can talk offline with Tanvir.

Copy link
Contributor

Choose a reason for hiding this comment

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

@vladsud Correct! FRS has recently done the work to store and track correlation ids of http requests. Next, we want wrap all these calls with telemetry. Can you provide us some pointers on how ODSP driver does that? I am expecting to follow a similar pattern.

Copy link
Contributor

Choose a reason for hiding this comment

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

Take a look at how ITelemetryLogger / ITelemetryBaseLogger and usage in ODSP driver.
Basically it is passed to driver in Container.load() (when calling createDocumentService) and driver passes it through it's layers and uses to log data (look for this.logger in odspDocumentStorageManager.ts as an example).
We wrap pretty much every network call and write out a bunch of properties to allow us to do analyzes later.

Note that in similar case, we rely on server communicating to client its correlation ID (in http response), and client recording it in client-side logging. That way, server is pretty much agnostic of client and client behavior. Reverse would work too, I do not have strong opinion here.

};

const storageToken = await this.tokenProvider.fetchStorageToken(
tenantId,
id,
);

if (storageToken) {
headers = {
Authorization: `Basic ${fromUtf8ToBase64(`${tenantId}:${storageToken.jwt}`)}`,
};
headers.Authorization = `Basic ${fromUtf8ToBase64(`${tenantId}:${storageToken.jwt}`)}`;
Copy link
Contributor

@pradeepvairamani pradeepvairamani Jan 9, 2021

Choose a reason for hiding this comment

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

Nit: Why do we need this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The initial value of headers was null. Now the initial value of headers is { "x-correlation-id": uuid.v4() } (on line 63). So it needs to add a key to dictionary instead of giving a value to it.

}

const ops = await Axios.get<ISequencedDocumentMessage[]>(
`${this.url}?${query}`, { headers });

if (this.logger) {
this.logger.sendTelemetryEvent({
eventName: "R11sDriverToServer",
correlationId: headers["x-correlation-id"] as string,
});
}

return ops.data;
}
}
4 changes: 3 additions & 1 deletion packages/drivers/routerlicious-driver/src/documentService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as api from "@fluidframework/driver-definitions";
import { IClient, IErrorTrackingService } from "@fluidframework/protocol-definitions";
import { GitManager, Historian, ICredentials, IGitCache } from "@fluidframework/server-services-client";
import io from "socket.io-client";
import { ITelemetryLogger } from "@fluidframework/common-definitions";
import { DeltaStorageService, DocumentDeltaStorageService } from "./deltaStorageService";
import { DocumentStorageService } from "./documentStorageService";
import { R11sDocumentDeltaConnection } from "./documentDeltaConnection";
Expand All @@ -29,6 +30,7 @@ export class DocumentService implements api.IDocumentService {
private readonly historianApi: boolean,
private readonly directCredentials: ICredentials | undefined,
private readonly gitCache: IGitCache | undefined,
private readonly logger: ITelemetryLogger | undefined,
protected tokenProvider: ITokenProvider,
protected tenantId: string,
protected documentId: string,
Expand Down Expand Up @@ -101,7 +103,7 @@ export class DocumentService implements api.IDocumentService {
public async connectToDeltaStorage(): Promise<api.IDocumentDeltaStorageService> {
assert(this.documentStorageService, "Storage service not initialized");

const deltaStorage = new DeltaStorageService(this.deltaStorageUrl, this.tokenProvider);
const deltaStorage = new DeltaStorageService(this.deltaStorageUrl, this.tokenProvider, this.logger);
return new DocumentDeltaStorageService(this.tenantId, this.documentId,
deltaStorage, this.documentStorageService);
}
Expand Down
3 changes: 3 additions & 0 deletions packages/drivers/routerlicious-driver/src/documentService2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Licensed under the MIT License.
*/

import { ITelemetryLogger } from "@fluidframework/common-definitions";
import { IDocumentDeltaConnection, IResolvedUrl } from "@fluidframework/driver-definitions";
import * as api from "@fluidframework/protocol-definitions";
import { ICredentials } from "@fluidframework/server-services-client";
Expand All @@ -23,6 +24,7 @@ export class DocumentService2 extends DocumentService {
errorTracking: api.IErrorTrackingService,
disableCache: boolean, historianApi: boolean,
directCredentials: ICredentials | undefined,
logger: ITelemetryLogger,
tokenProvider: ITokenProvider,
tenantId: string,
documentId: string) {
Expand All @@ -36,6 +38,7 @@ export class DocumentService2 extends DocumentService {
historianApi,
directCredentials,
undefined,
logger,
tokenProvider,
tenantId,
documentId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
getQuorumValuesFromProtocolSummary,
} from "@fluidframework/driver-utils";
import Axios from "axios";
import { ChildLogger } from "@fluidframework/telemetry-utils";
import { DocumentService } from "./documentService";
import { DocumentService2 } from "./documentService2";
import { DefaultErrorTracking } from "./errorTracking";
Expand Down Expand Up @@ -99,6 +100,8 @@ export class RouterliciousDocumentServiceFactory implements IDocumentServiceFact
`Couldn't parse documentId and/or tenantId. [documentId:${documentId}][tenantId:${tenantId}]`);
}

const logger2 = ChildLogger.create(logger, "RouterliciousDriver");

if (this.useDocumentService2) {
return new DocumentService2(
fluidResolvedUrl,
Expand All @@ -109,6 +112,7 @@ export class RouterliciousDocumentServiceFactory implements IDocumentServiceFact
this.disableCache,
this.historianApi,
this.credentials,
logger2,
this.tokenProvider,
tenantId,
documentId);
Expand All @@ -123,6 +127,7 @@ export class RouterliciousDocumentServiceFactory implements IDocumentServiceFact
this.historianApi,
this.credentials,
this.gitCache,
logger2,
this.tokenProvider,
tenantId,
documentId);
Expand Down
5 changes: 4 additions & 1 deletion packages/drivers/routerlicious-driver/src/registration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Licensed under the MIT License.
*/

import { ITelemetryLogger } from "@fluidframework/common-definitions";
import { IDocumentService, IResolvedUrl } from "@fluidframework/driver-definitions";
import { IErrorTrackingService } from "@fluidframework/protocol-definitions";
import { IGitCache } from "@fluidframework/server-services-client";
Expand All @@ -28,7 +29,8 @@ export function createDocumentService(
disableCache = false,
historianApi = true,
credentials?,
seedData?: IGitCache): IDocumentService {
seedData?: IGitCache,
logger?: ITelemetryLogger): IDocumentService {
const service = new DocumentService(
resolvedUrl,
ordererUrl,
Expand All @@ -39,6 +41,7 @@ export function createDocumentService(
historianApi,
credentials,
seedData,
logger,
tokenProvider,
tenantId,
documentId);
Expand Down