Skip to content

Commit

Permalink
Merge pull request #381 from tigrisdata/main
Browse files Browse the repository at this point in the history
Merged by Reviewpad
  • Loading branch information
reviewpad[bot] authored May 26, 2023
2 parents ffe5d79 + 47fd1fb commit 2ccb59b
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 73 deletions.
28 changes: 14 additions & 14 deletions package-lock.json

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

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
"eslint-plugin-unicorn": "^43.0.2",
"eslint-plugin-unused-imports": "^2.0.0",
"grpc_tools_node_protoc_ts": "^5.3.2",
"grpc-tools": "^1.11.3",
"grpc-tools": "^1.12.4",
"jest": "^28.1.3",
"prettier": "2.7.1",
"ts-jest": "^28.0.8",
Expand All @@ -107,7 +107,7 @@
"uuid": "^8.3.2"
},
"dependencies": {
"@grpc/grpc-js": "^1.6.10",
"@grpc/grpc-js": "^1.8.14",
"chalk": "4.1.2",
"dotenv": "^16.0.3",
"google-protobuf": "^3.21.0",
Expand Down
94 changes: 69 additions & 25 deletions src/__tests__/test-search-service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ISearchServer, SearchService } from "../proto/server/v1/search_grpc_pb";
import { sendUnaryData, ServerUnaryCall, ServerWritableStream } from "@grpc/grpc-js";
import { sendUnaryData, ServerUnaryCall, ServerWritableStream, status } from "@grpc/grpc-js";
import {
CreateByIdRequest,
CreateByIdResponse,
Expand Down Expand Up @@ -38,11 +38,18 @@ import {
SearchHit,
SearchHitMeta,
} from "../proto/server/v1/api_pb";
import assert from "assert";

export const SearchServiceFixtures = {
Success: "validIndex",
AlreadyExists: "existingIndex",
DoesNotExist: "NoIndex",
RetryUnknown: "Unknown",
RetryUnavailable: "Unavailable",
RetryInternal: "Internal",
RetryResourceEx: "ResourceExhausted",
RetryToFail: "RetryToFail",
NoRetryOnFail: "NoRetry",
Docs: new Map([
["1", { title: "नमस्ते to India", tags: ["travel"] }],
["2", { title: "reliable systems 🙏", tags: ["it"] }],
Expand Down Expand Up @@ -124,30 +131,67 @@ class TestSearchService {
return;
},
search(call: ServerWritableStream<SearchIndexRequest, SearchIndexResponse>): void {
const expectedUpdatedAt = new google_protobuf_timestamp_pb.Timestamp().setSeconds(
SearchServiceFixtures.SearchDocs.UpdatedAtSeconds
);
const resp = new SearchIndexResponse();
SearchServiceFixtures.Docs.forEach((d) =>
resp.addHits(
new SearchHit()
.setData(enc.encode(JSON.stringify(d)))
.setMetadata(new SearchHitMeta().setUpdatedAt(expectedUpdatedAt))
)
);
resp.setMeta(
new SearchMetadata()
.setFound(5)
.setTotalPages(5)
.setPage(new Page().setSize(1).setCurrent(1))
);
resp
.getFacetsMap()
.set(
"title",
new SearchFacet().setCountsList([new FacetCount().setCount(2).setValue("Philosophy")])
);
call.write(resp);
const previousAttempts = call.metadata.get("grpc-previous-rpc-attempts");
const prevAttempt =
previousAttempts.length == 0 ? 0 : parseInt(previousAttempts[0].toString());
switch (call.request.getIndex()) {
case SearchServiceFixtures.RetryUnavailable:
if (prevAttempt < 2) {
call.emit("error", { code: status.UNAVAILABLE });
break;
}
case SearchServiceFixtures.RetryUnknown:
if (prevAttempt < 2) {
call.emit("error", { code: status.UNKNOWN });
break;
}
case SearchServiceFixtures.RetryResourceEx:
if (prevAttempt < 2) {
call.emit("error", { code: status.RESOURCE_EXHAUSTED });
break;
}
case SearchServiceFixtures.RetryInternal:
if (prevAttempt < 2) {
call.emit("error", { code: status.INTERNAL });
break;
}
call.write(new SearchIndexResponse());
break;
case SearchServiceFixtures.RetryToFail:
assert(prevAttempt < 3);
call.emit("error", { code: status.UNKNOWN });
break;
case SearchServiceFixtures.NoRetryOnFail:
assert(prevAttempt == 0);
call.emit("error", { code: status.DEADLINE_EXCEEDED });
break;
case SearchServiceFixtures.Success:
const expectedUpdatedAt = new google_protobuf_timestamp_pb.Timestamp().setSeconds(
SearchServiceFixtures.SearchDocs.UpdatedAtSeconds
);
const resp = new SearchIndexResponse();
SearchServiceFixtures.Docs.forEach((d) =>
resp.addHits(
new SearchHit()
.setData(enc.encode(JSON.stringify(d)))
.setMetadata(new SearchHitMeta().setUpdatedAt(expectedUpdatedAt))
)
);
resp.setMeta(
new SearchMetadata()
.setFound(5)
.setTotalPages(5)
.setPage(new Page().setSize(1).setCurrent(1))
);
resp
.getFacetsMap()
.set(
"title",
new SearchFacet().setCountsList([new FacetCount().setCount(2).setValue("Philosophy")])
);
call.write(resp);
break;
}
call.end();
},
update(
Expand Down
70 changes: 51 additions & 19 deletions src/__tests__/test-service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { TigrisService } from "../proto/server/v1/api_grpc_pb";
import * as grpc from "@grpc/grpc-js";
import { sendUnaryData, Server, ServerUnaryCall, ServerWritableStream } from "@grpc/grpc-js";
import {
sendUnaryData,
Server,
ServerErrorResponse,
ServerUnaryCall,
ServerWritableStream,
} from "@grpc/grpc-js";
import { v4 as uuidv4 } from "uuid";
import {
BeginTransactionRequest,
Expand Down Expand Up @@ -353,25 +359,46 @@ export class TestTigrisService {
call: ServerUnaryCall<DescribeDatabaseRequest, DescribeDatabaseResponse>,
callback: sendUnaryData<DescribeDatabaseResponse>
): void {
const result: DescribeDatabaseResponse = new DescribeDatabaseResponse();
const collectionsDescription: CollectionDescription[] = [];
for (
let index = 0;
index < TestTigrisService.COLLECTION_MAP.get(call.request.getProject()).length;
index++
) {
collectionsDescription.push(
new CollectionDescription()
.setCollection(TestTigrisService.COLLECTION_MAP.get(call.request.getProject())[index])
.setMetadata(new CollectionMetadata())
.setSchema("schema" + index)
);
const previousAttempts = call.metadata.get("grpc-previous-rpc-attempts");
const prevAttempt =
previousAttempts.length == 0 ? 0 : parseInt(previousAttempts[0].toString());
switch (call.request.getProject()) {
case RetryRPCProjectName.Unavailable:
// max 3 attempts
assert(prevAttempt < 3);
callback({ name: "not available", code: Status.UNAVAILABLE }, undefined);
break;
case RetryRPCProjectName.SucceedOnThirdAttempt:
// error out on first 2 attempts
if (prevAttempt == 2) {
callback(undefined, new DescribeDatabaseResponse());
} else {
callback({ name: "", code: Status.UNKNOWN }, undefined);
}
break;
default:
const result: DescribeDatabaseResponse = new DescribeDatabaseResponse();
const collectionsDescription: CollectionDescription[] = [];
for (
let index = 0;
index < TestTigrisService.COLLECTION_MAP.get(call.request.getProject()).length;
index++
) {
collectionsDescription.push(
new CollectionDescription()
.setCollection(
TestTigrisService.COLLECTION_MAP.get(call.request.getProject())[index]
)
.setMetadata(new CollectionMetadata())
.setSchema("schema" + index)
);
}
result
.setMetadata(new DatabaseMetadata())
.setCollectionsList(collectionsDescription)
.setBranchesList(["main", "staging", TestTigrisService.ExpectedBranch]);
callback(undefined, result);
}
result
.setMetadata(new DatabaseMetadata())
.setCollectionsList(collectionsDescription)
.setBranchesList(["main", "staging", TestTigrisService.ExpectedBranch]);
callback(undefined, result);
},

dropCollection(
Expand Down Expand Up @@ -753,3 +780,8 @@ export enum Branch {
Existing = "existing",
NotFound = "no-project",
}

export enum RetryRPCProjectName {
Unavailable = "unavailable",
SucceedOnThirdAttempt = "third_attempt",
}
30 changes: 26 additions & 4 deletions src/__tests__/tigris.rpc.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Server, ServerCredentials, ServiceError } from "@grpc/grpc-js";
import { Server, ServerCredentials, ServiceError, status } from "@grpc/grpc-js";
import { TigrisService } from "../proto/server/v1/api_grpc_pb";
import TestService, { Branch, TestTigrisService } from "./test-service";
import TestService, { Branch, RetryRPCProjectName, TestTigrisService } from "./test-service";
import TestServiceCache, { TestCacheService } from "./test-cache-service";

import {
Expand Down Expand Up @@ -28,8 +28,7 @@ import {
} from "../error";
import { Status } from "@grpc/grpc-js/build/src/constants";
import { Status as TigrisStatus } from "../constants";
import { Case, Collation, SearchQuery } from "../search";
import { SearchResult } from "../search";
import { Case, Collation, SearchQuery, SearchResult } from "../search";

describe("rpc tests", () => {
let server: Server;
Expand Down Expand Up @@ -66,6 +65,29 @@ describe("rpc tests", () => {
done();
});

describe("grpc client", () => {
it("fails after retrying on service UNAVAILABLE", async () => {
const tigris = new Tigris({
serverUrl: testConfig.serverUrl,
projectName: RetryRPCProjectName.Unavailable,
branch: "main",
});
const db1 = tigris.getDatabase();
const resp = db1.describe();
return expect(resp).rejects.toThrow(`${status.UNAVAILABLE}`);
});
it("succeeds on third attempt", async () => {
const tigris = new Tigris({
serverUrl: testConfig.serverUrl,
projectName: RetryRPCProjectName.SucceedOnThirdAttempt,
branch: "main",
});
const db1 = tigris.getDatabase();
const resp = db1.describe();
return expect(resp).resolves.toBeDefined();
});
});

it("getDatabase", () => {
const tigris = new Tigris(testConfig);
const db1 = tigris.getDatabase();
Expand Down
30 changes: 29 additions & 1 deletion src/__tests__/tigris.search.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
TigrisIndexSchema,
TigrisIndexType,
} from "../search";
import { Server, ServerCredentials } from "@grpc/grpc-js";
import { Server, ServerCredentials, status } from "@grpc/grpc-js";
import TestSearchService, { SearchServiceFixtures } from "./test-search-service";
import { SearchService } from "../proto/server/v1/search_grpc_pb";
import { SearchField } from "../decorators/tigris-search-field";
Expand Down Expand Up @@ -177,6 +177,34 @@ describe("Search Indexing", () => {
]);
}
});

const retryCodes: Array<string> = [
SearchServiceFixtures.RetryUnavailable,
SearchServiceFixtures.RetryUnknown,
SearchServiceFixtures.RetryInternal,
SearchServiceFixtures.RetryResourceEx,
];

it.each(retryCodes)(
"retries and succeeds at third attempt when failure is '%s'",
async (indexName) => {
const index = await tigris.getIndex<Book>(indexName);
const result = index.search({}).toArray();
await expect(result).resolves.toBeDefined();
}
);

it("retries and fails after 3 attempts", async () => {
const index = await tigris.getIndex<Book>(SearchServiceFixtures.RetryToFail);
const result = index.search({}).toArray();
await expect(result).rejects.toThrow(`${status.UNKNOWN}`);
});

it("never retries for other status codes", async () => {
const index = await tigris.getIndex<Book>(SearchServiceFixtures.NoRetryOnFail);
const result = index.search({}).toArray();
await expect(result).rejects.toBeDefined();
});
});

it("returns a promise with page number", async () => {
Expand Down
Loading

0 comments on commit 2ccb59b

Please sign in to comment.