fix: retry grpc calls upon service unavailable response (#1011)
* fix: retry grpc calls upon service unavailable response * fix: retry grpc calls for session verification
This commit is contained in:
parent
6dde9209af
commit
7c67a5a0f9
8 changed files with 92 additions and 27 deletions
14
.pnp.cjs
generated
14
.pnp.cjs
generated
|
@ -2190,10 +2190,10 @@ const RAW_RUNTIME_STATE =
|
||||||
}]\
|
}]\
|
||||||
]],\
|
]],\
|
||||||
["@grpc/grpc-js", [\
|
["@grpc/grpc-js", [\
|
||||||
["npm:1.9.12", {\
|
["npm:1.9.13", {\
|
||||||
"packageLocation": "./.yarn/cache/@grpc-grpc-js-npm-1.9.12-cb97be6754-fe13b04844.zip/node_modules/@grpc/grpc-js/",\
|
"packageLocation": "./.yarn/cache/@grpc-grpc-js-npm-1.9.13-33f9b49e10-c52150053c.zip/node_modules/@grpc/grpc-js/",\
|
||||||
"packageDependencies": [\
|
"packageDependencies": [\
|
||||||
["@grpc/grpc-js", "npm:1.9.12"],\
|
["@grpc/grpc-js", "npm:1.9.13"],\
|
||||||
["@grpc/proto-loader", "npm:0.7.10"],\
|
["@grpc/proto-loader", "npm:0.7.10"],\
|
||||||
["@types/node", "npm:20.2.5"]\
|
["@types/node", "npm:20.2.5"]\
|
||||||
],\
|
],\
|
||||||
|
@ -5532,7 +5532,7 @@ const RAW_RUNTIME_STATE =
|
||||||
"packageLocation": "./packages/api-gateway/",\
|
"packageLocation": "./packages/api-gateway/",\
|
||||||
"packageDependencies": [\
|
"packageDependencies": [\
|
||||||
["@standardnotes/api-gateway", "workspace:packages/api-gateway"],\
|
["@standardnotes/api-gateway", "workspace:packages/api-gateway"],\
|
||||||
["@grpc/grpc-js", "npm:1.9.12"],\
|
["@grpc/grpc-js", "npm:1.9.13"],\
|
||||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||||
|
@ -5582,7 +5582,7 @@ const RAW_RUNTIME_STATE =
|
||||||
["@aws-sdk/client-sqs", "npm:3.462.0"],\
|
["@aws-sdk/client-sqs", "npm:3.462.0"],\
|
||||||
["@cbor-extract/cbor-extract-linux-arm64", "npm:2.1.1"],\
|
["@cbor-extract/cbor-extract-linux-arm64", "npm:2.1.1"],\
|
||||||
["@cbor-extract/cbor-extract-linux-x64", "npm:2.1.1"],\
|
["@cbor-extract/cbor-extract-linux-x64", "npm:2.1.1"],\
|
||||||
["@grpc/grpc-js", "npm:1.9.12"],\
|
["@grpc/grpc-js", "npm:1.9.13"],\
|
||||||
["@simplewebauthn/server", "npm:8.1.1"],\
|
["@simplewebauthn/server", "npm:8.1.1"],\
|
||||||
["@simplewebauthn/typescript-types", "npm:8.0.0"],\
|
["@simplewebauthn/typescript-types", "npm:8.0.0"],\
|
||||||
["@standardnotes/api", "npm:1.26.26"],\
|
["@standardnotes/api", "npm:1.26.26"],\
|
||||||
|
@ -5809,7 +5809,7 @@ const RAW_RUNTIME_STATE =
|
||||||
"packageLocation": "./packages/grpc/",\
|
"packageLocation": "./packages/grpc/",\
|
||||||
"packageDependencies": [\
|
"packageDependencies": [\
|
||||||
["@standardnotes/grpc", "workspace:packages/grpc"],\
|
["@standardnotes/grpc", "workspace:packages/grpc"],\
|
||||||
["@grpc/grpc-js", "npm:1.9.12"],\
|
["@grpc/grpc-js", "npm:1.9.13"],\
|
||||||
["@types/google-protobuf", "npm:3.15.10"],\
|
["@types/google-protobuf", "npm:3.15.10"],\
|
||||||
["google-protobuf", "npm:3.21.2"],\
|
["google-protobuf", "npm:3.21.2"],\
|
||||||
["grpc-tools", "npm:1.12.4"],\
|
["grpc-tools", "npm:1.12.4"],\
|
||||||
|
@ -6082,7 +6082,7 @@ const RAW_RUNTIME_STATE =
|
||||||
["@aws-sdk/client-s3", "npm:3.462.0"],\
|
["@aws-sdk/client-s3", "npm:3.462.0"],\
|
||||||
["@aws-sdk/client-sns", "npm:3.462.0"],\
|
["@aws-sdk/client-sns", "npm:3.462.0"],\
|
||||||
["@aws-sdk/client-sqs", "npm:3.462.0"],\
|
["@aws-sdk/client-sqs", "npm:3.462.0"],\
|
||||||
["@grpc/grpc-js", "npm:1.9.12"],\
|
["@grpc/grpc-js", "npm:1.9.13"],\
|
||||||
["@standardnotes/api", "npm:1.26.26"],\
|
["@standardnotes/api", "npm:1.26.26"],\
|
||||||
["@standardnotes/common", "workspace:packages/common"],\
|
["@standardnotes/common", "workspace:packages/common"],\
|
||||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||||
|
|
Binary file not shown.
|
@ -31,7 +31,7 @@
|
||||||
"start": "yarn node dist/bin/server.js"
|
"start": "yarn node dist/bin/server.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@grpc/grpc-js": "^1.9.12",
|
"@grpc/grpc-js": "^1.9.13",
|
||||||
"@standardnotes/domain-core": "workspace:^",
|
"@standardnotes/domain-core": "workspace:^",
|
||||||
"@standardnotes/domain-events": "workspace:*",
|
"@standardnotes/domain-events": "workspace:*",
|
||||||
"@standardnotes/domain-events-infra": "workspace:*",
|
"@standardnotes/domain-events-infra": "workspace:*",
|
||||||
|
|
|
@ -8,6 +8,7 @@ import * as grpc from '@grpc/grpc-js'
|
||||||
import { CrossServiceTokenCacheInterface } from '../Cache/CrossServiceTokenCacheInterface'
|
import { CrossServiceTokenCacheInterface } from '../Cache/CrossServiceTokenCacheInterface'
|
||||||
import { ServiceProxyInterface } from '../Proxy/ServiceProxyInterface'
|
import { ServiceProxyInterface } from '../Proxy/ServiceProxyInterface'
|
||||||
import { GRPCSyncingServerServiceProxy } from './GRPCSyncingServerServiceProxy'
|
import { GRPCSyncingServerServiceProxy } from './GRPCSyncingServerServiceProxy'
|
||||||
|
import { Status } from '@grpc/grpc-js/build/src/constants'
|
||||||
|
|
||||||
export class GRPCServiceProxy implements ServiceProxyInterface {
|
export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -27,11 +28,14 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||||
private gRPCSyncingServerServiceProxy: GRPCSyncingServerServiceProxy,
|
private gRPCSyncingServerServiceProxy: GRPCSyncingServerServiceProxy,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async validateSession(headers: {
|
async validateSession(
|
||||||
authorization: string
|
headers: {
|
||||||
sharedVaultOwnerContext?: string
|
authorization: string
|
||||||
}): Promise<{ status: number; data: unknown; headers: { contentType: string } }> {
|
sharedVaultOwnerContext?: string
|
||||||
return new Promise((resolve, reject) => {
|
},
|
||||||
|
retryAttempt?: number,
|
||||||
|
): Promise<{ status: number; data: unknown; headers: { contentType: string } }> {
|
||||||
|
const promise = new Promise((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
const request = new AuthorizationHeader()
|
const request = new AuthorizationHeader()
|
||||||
request.setBearerToken(headers.authorization)
|
request.setBearerToken(headers.authorization)
|
||||||
|
@ -80,6 +84,32 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||||
return reject(error)
|
return reject(error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await promise
|
||||||
|
|
||||||
|
if (retryAttempt) {
|
||||||
|
this.logger.info(`Request to Auth Server succeeded after ${retryAttempt} retries`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result as { status: number; data: unknown; headers: { contentType: string } }
|
||||||
|
} catch (error) {
|
||||||
|
const requestDidNotMakeIt =
|
||||||
|
'code' in (error as Record<string, unknown>) && (error as Record<string, unknown>).code === Status.UNAVAILABLE
|
||||||
|
|
||||||
|
const tooManyRetryAttempts = retryAttempt && retryAttempt > 2
|
||||||
|
if (!tooManyRetryAttempts && requestDidNotMakeIt) {
|
||||||
|
await this.timer.sleep(50)
|
||||||
|
|
||||||
|
const nextRetryAttempt = retryAttempt ? retryAttempt + 1 : 1
|
||||||
|
|
||||||
|
this.logger.info(`Retrying request to Auth Server for the ${nextRetryAttempt} time`)
|
||||||
|
|
||||||
|
return this.validateSession(headers, nextRetryAttempt)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async callSyncingServer(
|
async callSyncingServer(
|
||||||
|
@ -92,6 +122,21 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||||
payload !== undefined && typeof payload !== 'string' && 'api' in payload && payload.api === '20200115'
|
payload !== undefined && typeof payload !== 'string' && 'api' in payload && payload.api === '20200115'
|
||||||
|
|
||||||
if (requestIsUsingLatestApiVersions && endpoint === 'items/sync') {
|
if (requestIsUsingLatestApiVersions && endpoint === 'items/sync') {
|
||||||
|
await this.callSyncingServerGRPC(request, response, payload)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.callServer(this.syncingServerJsUrl, request, response, endpoint, payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
private async callSyncingServerGRPC(
|
||||||
|
request: Request,
|
||||||
|
response: Response,
|
||||||
|
payload?: Record<string, unknown> | string,
|
||||||
|
retryAttempt?: number,
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
const result = await this.gRPCSyncingServerServiceProxy.sync(request, response, payload)
|
const result = await this.gRPCSyncingServerServiceProxy.sync(request, response, payload)
|
||||||
|
|
||||||
response.status(result.status).send({
|
response.status(result.status).send({
|
||||||
|
@ -107,10 +152,30 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||||
data: result.data,
|
data: result.data,
|
||||||
})
|
})
|
||||||
|
|
||||||
return
|
if (retryAttempt) {
|
||||||
}
|
this.logger.info(`Request to Syncing Server succeeded after ${retryAttempt} retries`, {
|
||||||
|
userId: response.locals.user ? response.locals.user.uuid : undefined,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const requestDidNotMakeIt =
|
||||||
|
'code' in (error as Record<string, unknown>) && (error as Record<string, unknown>).code === Status.UNAVAILABLE
|
||||||
|
|
||||||
await this.callServer(this.syncingServerJsUrl, request, response, endpoint, payload)
|
const tooManyRetryAttempts = retryAttempt && retryAttempt > 2
|
||||||
|
if (!tooManyRetryAttempts && requestDidNotMakeIt) {
|
||||||
|
await this.timer.sleep(50)
|
||||||
|
|
||||||
|
const nextRetryAttempt = retryAttempt ? retryAttempt + 1 : 1
|
||||||
|
|
||||||
|
this.logger.info(`Retrying request to Syncing Server for the ${nextRetryAttempt} time`, {
|
||||||
|
userId: response.locals.user ? response.locals.user.uuid : undefined,
|
||||||
|
})
|
||||||
|
|
||||||
|
return this.callSyncingServerGRPC(request, response, payload, nextRetryAttempt)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async callRevisionsServer(
|
async callRevisionsServer(
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
"@aws-sdk/client-sqs": "^3.462.0",
|
"@aws-sdk/client-sqs": "^3.462.0",
|
||||||
"@cbor-extract/cbor-extract-linux-arm64": "^2.1.1",
|
"@cbor-extract/cbor-extract-linux-arm64": "^2.1.1",
|
||||||
"@cbor-extract/cbor-extract-linux-x64": "^2.1.1",
|
"@cbor-extract/cbor-extract-linux-x64": "^2.1.1",
|
||||||
"@grpc/grpc-js": "^1.9.12",
|
"@grpc/grpc-js": "^1.9.13",
|
||||||
"@simplewebauthn/server": "^8.1.1",
|
"@simplewebauthn/server": "^8.1.1",
|
||||||
"@simplewebauthn/typescript-types": "^8.0.0",
|
"@simplewebauthn/typescript-types": "^8.0.0",
|
||||||
"@standardnotes/api": "^1.26.26",
|
"@standardnotes/api": "^1.26.26",
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
"build": "tsc --build"
|
"build": "tsc --build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@grpc/grpc-js": "^1.9.12",
|
"@grpc/grpc-js": "^1.9.13",
|
||||||
"google-protobuf": "^3.21.2"
|
"google-protobuf": "^3.21.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
"@aws-sdk/client-s3": "^3.462.0",
|
"@aws-sdk/client-s3": "^3.462.0",
|
||||||
"@aws-sdk/client-sns": "^3.462.0",
|
"@aws-sdk/client-sns": "^3.462.0",
|
||||||
"@aws-sdk/client-sqs": "^3.462.0",
|
"@aws-sdk/client-sqs": "^3.462.0",
|
||||||
"@grpc/grpc-js": "^1.9.12",
|
"@grpc/grpc-js": "^1.9.13",
|
||||||
"@standardnotes/api": "^1.26.26",
|
"@standardnotes/api": "^1.26.26",
|
||||||
"@standardnotes/common": "workspace:*",
|
"@standardnotes/common": "workspace:*",
|
||||||
"@standardnotes/domain-core": "workspace:^",
|
"@standardnotes/domain-core": "workspace:^",
|
||||||
|
|
16
yarn.lock
16
yarn.lock
|
@ -1583,13 +1583,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@grpc/grpc-js@npm:^1.9.12":
|
"@grpc/grpc-js@npm:^1.9.13":
|
||||||
version: 1.9.12
|
version: 1.9.13
|
||||||
resolution: "@grpc/grpc-js@npm:1.9.12"
|
resolution: "@grpc/grpc-js@npm:1.9.13"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@grpc/proto-loader": "npm:^0.7.8"
|
"@grpc/proto-loader": "npm:^0.7.8"
|
||||||
"@types/node": "npm:>=12.12.47"
|
"@types/node": "npm:>=12.12.47"
|
||||||
checksum: fe13b04844b525ad860521589e2d640bb8cfeea46e3cb8e4eab537e0a4fcb04a033083c25d5c3cd4e061a6471c933f6f12e81dcc626acdcf68435e6e4a833a06
|
checksum: c52150053ca3911bf9ec5012265aa754627aba9c60577ef07c594c5c22896e939ec0f656cc130a54a8651ea0ae23f385a4a48868fc71ff56dff54eeaec8b6912
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
@ -4084,7 +4084,7 @@ __metadata:
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@standardnotes/api-gateway@workspace:packages/api-gateway"
|
resolution: "@standardnotes/api-gateway@workspace:packages/api-gateway"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@grpc/grpc-js": "npm:^1.9.12"
|
"@grpc/grpc-js": "npm:^1.9.13"
|
||||||
"@standardnotes/domain-core": "workspace:^"
|
"@standardnotes/domain-core": "workspace:^"
|
||||||
"@standardnotes/domain-events": "workspace:*"
|
"@standardnotes/domain-events": "workspace:*"
|
||||||
"@standardnotes/domain-events-infra": "workspace:*"
|
"@standardnotes/domain-events-infra": "workspace:*"
|
||||||
|
@ -4147,7 +4147,7 @@ __metadata:
|
||||||
"@aws-sdk/client-sqs": "npm:^3.462.0"
|
"@aws-sdk/client-sqs": "npm:^3.462.0"
|
||||||
"@cbor-extract/cbor-extract-linux-arm64": "npm:^2.1.1"
|
"@cbor-extract/cbor-extract-linux-arm64": "npm:^2.1.1"
|
||||||
"@cbor-extract/cbor-extract-linux-x64": "npm:^2.1.1"
|
"@cbor-extract/cbor-extract-linux-x64": "npm:^2.1.1"
|
||||||
"@grpc/grpc-js": "npm:^1.9.12"
|
"@grpc/grpc-js": "npm:^1.9.13"
|
||||||
"@simplewebauthn/server": "npm:^8.1.1"
|
"@simplewebauthn/server": "npm:^8.1.1"
|
||||||
"@simplewebauthn/typescript-types": "npm:^8.0.0"
|
"@simplewebauthn/typescript-types": "npm:^8.0.0"
|
||||||
"@standardnotes/api": "npm:^1.26.26"
|
"@standardnotes/api": "npm:^1.26.26"
|
||||||
|
@ -4361,7 +4361,7 @@ __metadata:
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@standardnotes/grpc@workspace:packages/grpc"
|
resolution: "@standardnotes/grpc@workspace:packages/grpc"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@grpc/grpc-js": "npm:^1.9.12"
|
"@grpc/grpc-js": "npm:^1.9.13"
|
||||||
"@types/google-protobuf": "npm:^3"
|
"@types/google-protobuf": "npm:^3"
|
||||||
google-protobuf: "npm:^3.21.2"
|
google-protobuf: "npm:^3.21.2"
|
||||||
grpc-tools: "npm:^1.12.4"
|
grpc-tools: "npm:^1.12.4"
|
||||||
|
@ -4618,7 +4618,7 @@ __metadata:
|
||||||
"@aws-sdk/client-s3": "npm:^3.462.0"
|
"@aws-sdk/client-s3": "npm:^3.462.0"
|
||||||
"@aws-sdk/client-sns": "npm:^3.462.0"
|
"@aws-sdk/client-sns": "npm:^3.462.0"
|
||||||
"@aws-sdk/client-sqs": "npm:^3.462.0"
|
"@aws-sdk/client-sqs": "npm:^3.462.0"
|
||||||
"@grpc/grpc-js": "npm:^1.9.12"
|
"@grpc/grpc-js": "npm:^1.9.13"
|
||||||
"@standardnotes/api": "npm:^1.26.26"
|
"@standardnotes/api": "npm:^1.26.26"
|
||||||
"@standardnotes/common": "workspace:*"
|
"@standardnotes/common": "workspace:*"
|
||||||
"@standardnotes/domain-core": "workspace:^"
|
"@standardnotes/domain-core": "workspace:^"
|
||||||
|
|
Loading…
Reference in a new issue