Skip to content

Commit 0ecca86

Browse files
feat: Snapshot reads (#963)
* Initial implementation of read time parameter * linting fix * system test works * Almost there * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * Finished unit test for snapshot reads * PR follow-up * PR changes * system test fix * system test fix again Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent 7b35856 commit 0ecca86

File tree

4 files changed

+95
-1
lines changed

4 files changed

+95
-1
lines changed

src/query.ts

+1
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,7 @@ export interface IntegerTypeCastOptions {
561561

562562
export interface RunQueryOptions {
563563
consistency?: 'strong' | 'eventual';
564+
readTime?: number;
564565
gaxOptions?: CallOptions;
565566
wrapNumbers?: boolean | IntegerTypeCastOptions;
566567
}

src/request.ts

+16-1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import {
5454
RunQueryCallback,
5555
} from './query';
5656
import {Datastore} from '.';
57+
import ITimestamp = google.protobuf.ITimestamp;
5758

5859
/**
5960
* A map of read consistency values to proto codes.
@@ -284,6 +285,16 @@ class DatastoreRequest {
284285
readConsistency: code,
285286
};
286287
}
288+
if (options.readTime) {
289+
if (reqOpts.readOptions === undefined) {
290+
reqOpts.readOptions = {};
291+
}
292+
const readTime = options.readTime;
293+
const seconds = readTime / 1000;
294+
reqOpts.readOptions.readTime = {
295+
seconds: Math.floor(seconds),
296+
};
297+
}
287298

288299
this.request_(
289300
{
@@ -1058,7 +1069,11 @@ export interface RequestConfig {
10581069
export interface RequestOptions {
10591070
mutations?: google.datastore.v1.IMutation[];
10601071
keys?: Entity;
1061-
readOptions?: {readConsistency?: number; transaction?: string};
1072+
readOptions?: {
1073+
readConsistency?: number;
1074+
transaction?: string;
1075+
readTime?: ITimestamp;
1076+
};
10621077
partitionId?: google.datastore.v1.IPartitionId | null;
10631078
transactionOptions?: {
10641079
readOnly?: {};

system-test/datastore.ts

+36
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,42 @@ describe('Datastore', () => {
308308
await datastore.delete(postKey);
309309
});
310310

311+
it('should save/get/delete from a snapshot', async () => {
312+
function sleep(ms: number) {
313+
return new Promise(resolve => setTimeout(resolve, ms));
314+
}
315+
const post2 = {
316+
title: 'Another way to make pizza',
317+
tags: ['pizza', 'grill'],
318+
publishedAt: new Date(),
319+
author: 'Silvano',
320+
isDraft: false,
321+
wordCount: 400,
322+
rating: 5.0,
323+
likes: null,
324+
metadata: {
325+
views: 100,
326+
},
327+
};
328+
const path = ['Post', 'post1'];
329+
const postKey = datastore.key(path);
330+
await datastore.save({key: postKey, data: post});
331+
await sleep(1000);
332+
const savedTime = Date.now();
333+
await sleep(1000);
334+
// Save new post2 data, but then verify the timestamp read has post1 data
335+
await datastore.save({key: postKey, data: post2});
336+
const [entity] = await datastore.get(postKey, {readTime: savedTime});
337+
assert.deepStrictEqual(entity[datastore.KEY], postKey);
338+
const [entityNoOptions] = await datastore.get(postKey);
339+
assert.deepStrictEqual(entityNoOptions[datastore.KEY], postKey);
340+
delete entity[datastore.KEY];
341+
assert.deepStrictEqual(entity, post);
342+
delete entityNoOptions[datastore.KEY];
343+
assert.deepStrictEqual(entityNoOptions, post2);
344+
await datastore.delete(postKey);
345+
});
346+
311347
it('should save/get/delete with a numeric key id', async () => {
312348
const postKey = datastore.key(['Post', 123456789]);
313349
await datastore.save({key: postKey, data: post});

test/request.ts

+42
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
PrepareEntityObjectResponse,
3535
CommitResponse,
3636
GetResponse,
37+
RequestCallback,
3738
} from '../src/request';
3839

3940
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -672,6 +673,47 @@ describe('Request', () => {
672673
});
673674

674675
describe('get', () => {
676+
it('should pass along readTime for reading snapshots', done => {
677+
const savedTime = Date.now();
678+
request.request_ = (config: RequestConfig, callback: RequestCallback) => {
679+
assert.deepStrictEqual(config, {
680+
client: 'DatastoreClient',
681+
method: 'lookup',
682+
gaxOpts: undefined,
683+
reqOpts: {
684+
keys: [
685+
{
686+
path: [
687+
{
688+
kind: 'Company',
689+
id: 123,
690+
},
691+
],
692+
partitionId: {namespaceId: 'namespace'},
693+
},
694+
],
695+
readOptions: {
696+
readTime: {
697+
seconds: Math.floor(savedTime / 1000),
698+
},
699+
},
700+
},
701+
});
702+
callback(null, {
703+
deferred: [],
704+
found: [],
705+
missing: [],
706+
readTime: {seconds: Math.floor(savedTime / 1000), nanos: 0},
707+
});
708+
};
709+
request.get(key, {readTime: savedTime}, (err: any) => {
710+
if (err) {
711+
throw err;
712+
}
713+
done();
714+
});
715+
});
716+
675717
describe('success', () => {
676718
const keys = [key];
677719
const fakeEntities = [{a: 'a'}, {b: 'b'}];

0 commit comments

Comments
 (0)