Skip to content

Commit 2a9086f

Browse files
authored
feat: add support for tagging (#576)
1 parent 6a58433 commit 2a9086f

File tree

10 files changed

+608
-38
lines changed

10 files changed

+608
-38
lines changed

google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractReadContext.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,12 @@ RequestOptions buildRequestOptions(Options options) {
563563
if (options.hasPriority()) {
564564
builder.setPriority(options.priority());
565565
}
566+
if (options.hasTag()) {
567+
builder.setRequestTag(options.tag());
568+
}
569+
if (getTransactionTag() != null) {
570+
builder.setTransactionTag(getTransactionTag());
571+
}
566572
return builder.build();
567573
}
568574

@@ -707,6 +713,15 @@ public void close() {
707713
@Nullable
708714
abstract TransactionSelector getTransactionSelector();
709715

716+
/**
717+
* Returns the transaction tag for this {@link AbstractReadContext} or <code>null</code> if this
718+
* {@link AbstractReadContext} does not have a transaction tag.
719+
*/
720+
@Nullable
721+
String getTransactionTag() {
722+
return null;
723+
}
724+
710725
/** This method is called when a statement returned a new transaction as part of its results. */
711726
@Override
712727
public void onTransactionMetadata(Transaction transaction, boolean shouldIncludeId) {}

google-cloud-spanner/src/main/java/com/google/cloud/spanner/Options.java

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,14 @@ public static ReadQueryUpdateTransactionOption priority(RpcPriority priority) {
101101
return new PriorityOption(priority);
102102
}
103103

104+
/**
105+
* Specifying this will cause the reads, queries, updates and writes operations statistics
106+
* collection to be grouped by tag.
107+
*/
108+
public static ReadQueryUpdateTransactionOption tag(String name) {
109+
return new TagOption(name);
110+
}
111+
104112
/**
105113
* Specifying this will cause the list operations to fetch at most this many records in a page.
106114
*/
@@ -194,6 +202,19 @@ void appendToOptions(Options options) {
194202
}
195203
}
196204

205+
static final class TagOption extends InternalOption implements ReadQueryUpdateTransactionOption {
206+
private final String tag;
207+
208+
TagOption(String tag) {
209+
this.tag = tag;
210+
}
211+
212+
@Override
213+
void appendToOptions(Options options) {
214+
options.tag = tag;
215+
}
216+
}
217+
197218
private boolean withCommitStats;
198219
private Long limit;
199220
private Integer prefetchChunks;
@@ -202,6 +223,7 @@ void appendToOptions(Options options) {
202223
private String pageToken;
203224
private String filter;
204225
private RpcPriority priority;
226+
private String tag;
205227

206228
// Construction is via factory methods below.
207229
private Options() {}
@@ -266,6 +288,14 @@ Priority priority() {
266288
return priority == null ? null : priority.proto;
267289
}
268290

291+
boolean hasTag() {
292+
return tag != null;
293+
}
294+
295+
String tag() {
296+
return tag;
297+
}
298+
269299
@Override
270300
public String toString() {
271301
StringBuilder b = new StringBuilder();
@@ -290,6 +320,9 @@ public String toString() {
290320
if (priority != null) {
291321
b.append("priority: ").append(priority).append(' ');
292322
}
323+
if (tag != null) {
324+
b.append("tag: ").append(tag).append(' ');
325+
}
293326
return b.toString();
294327
}
295328

@@ -320,7 +353,8 @@ public boolean equals(Object o) {
320353
|| hasPageSize() && that.hasPageSize() && Objects.equals(pageSize(), that.pageSize()))
321354
&& Objects.equals(pageToken(), that.pageToken())
322355
&& Objects.equals(filter(), that.filter())
323-
&& Objects.equals(priority(), that.priority());
356+
&& Objects.equals(priority(), that.priority())
357+
&& Objects.equals(tag(), that.tag());
324358
}
325359

326360
@Override
@@ -350,6 +384,9 @@ public int hashCode() {
350384
if (priority != null) {
351385
result = 31 * result + priority.hashCode();
352386
}
387+
if (tag != null) {
388+
result = 31 * result + tag.hashCode();
389+
}
353390
return result;
354391
}
355392

google-cloud-spanner/src/main/java/com/google/cloud/spanner/PartitionedDmlTransaction.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,11 +181,16 @@ ExecuteSqlRequest newTransactionRequestFrom(final Statement statement, final Opt
181181

182182
builder.setResumeToken(ByteString.EMPTY);
183183

184-
if (options.hasPriority()) {
185-
builder.setRequestOptions(
186-
RequestOptions.newBuilder().setPriority(options.priority()).build());
184+
if (options.hasPriority() || options.hasTag()) {
185+
RequestOptions.Builder requestOptionsBuilder = RequestOptions.newBuilder();
186+
if (options.hasPriority()) {
187+
requestOptionsBuilder.setPriority(options.priority());
188+
}
189+
if (options.hasTag()) {
190+
requestOptionsBuilder.setRequestTag(options.tag());
191+
}
192+
builder.setRequestOptions(requestOptionsBuilder.build());
187193
}
188-
189194
return builder.build();
190195
}
191196

google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionImpl.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,15 @@ public CommitResponse writeAtLeastOnceWithOptions(
173173
.setSingleUseTransaction(
174174
TransactionOptions.newBuilder()
175175
.setReadWrite(TransactionOptions.ReadWrite.getDefaultInstance()));
176-
if (commitRequestOptions.hasPriority()) {
177-
requestBuilder.setRequestOptions(
178-
RequestOptions.newBuilder().setPriority(commitRequestOptions.priority()).build());
176+
if (commitRequestOptions.hasPriority() || commitRequestOptions.hasTag()) {
177+
RequestOptions.Builder requestOptionsBuilder = RequestOptions.newBuilder();
178+
if (commitRequestOptions.hasPriority()) {
179+
requestOptionsBuilder.setPriority(commitRequestOptions.priority());
180+
}
181+
if (commitRequestOptions.hasTag()) {
182+
requestOptionsBuilder.setTransactionTag(commitRequestOptions.tag());
183+
}
184+
requestBuilder.setRequestOptions(requestOptionsBuilder.build());
179185
}
180186
Span span = tracer.spanBuilder(SpannerImpl.COMMIT).startSpan();
181187
try (Scope s = tracer.withSpan(span)) {

google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunnerImpl.java

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -299,9 +299,15 @@ ApiFuture<CommitResponse> commitAsync() {
299299
CommitRequest.newBuilder()
300300
.setSession(session.getName())
301301
.setReturnCommitStats(options.withCommitStats());
302-
if (options.hasPriority()) {
303-
builder.setRequestOptions(
304-
RequestOptions.newBuilder().setPriority(options.priority()).build());
302+
if (options.hasPriority() || getTransactionTag() != null) {
303+
RequestOptions.Builder requestOptionsBuilder = RequestOptions.newBuilder();
304+
if (options.hasPriority()) {
305+
requestOptionsBuilder.setPriority(options.priority());
306+
}
307+
if (getTransactionTag() != null) {
308+
requestOptionsBuilder.setTransactionTag(getTransactionTag());
309+
}
310+
builder.setRequestOptions(requestOptionsBuilder.build());
305311
}
306312
synchronized (lock) {
307313
if (transactionIdFuture == null && transactionId == null && runningAsyncOperations == 0) {
@@ -349,9 +355,15 @@ public void run() {
349355
requestBuilder.setTransactionId(
350356
transactionId == null ? transactionIdFuture.get() : transactionId);
351357
}
352-
if (options.hasPriority()) {
353-
requestBuilder.setRequestOptions(
354-
RequestOptions.newBuilder().setPriority(options.priority()).build());
358+
if (options.hasPriority() || getTransactionTag() != null) {
359+
RequestOptions.Builder requestOptionsBuilder = RequestOptions.newBuilder();
360+
if (options.hasPriority()) {
361+
requestOptionsBuilder.setPriority(options.priority());
362+
}
363+
if (getTransactionTag() != null) {
364+
requestOptionsBuilder.setTransactionTag(getTransactionTag());
365+
}
366+
requestBuilder.setRequestOptions(requestOptionsBuilder.build());
355367
}
356368
final CommitRequest commitRequest = requestBuilder.build();
357369
span.addAnnotation("Starting Commit");
@@ -531,6 +543,12 @@ public void onTransactionMetadata(Transaction transaction, boolean shouldInclude
531543
}
532544
}
533545

546+
@Nullable
547+
String getTransactionTag() {
548+
if (this.options.hasTag()) return this.options.tag();
549+
return null;
550+
}
551+
534552
@Override
535553
public SpannerException onError(SpannerException e, boolean withBeginTransaction) {
536554
// If the statement that caused an error was the statement that included a BeginTransaction

google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractReadContextTest.java

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ TestReadContext build() {
6464
}
6565
}
6666

67+
class TestReadContextWithTagBuilder
68+
extends AbstractReadContext.Builder<TestReadContextWithTagBuilder, TestReadContextWithTag> {
69+
@Override
70+
TestReadContextWithTag build() {
71+
return new TestReadContextWithTag(this);
72+
}
73+
}
74+
6775
private final class TestReadContext extends AbstractReadContext {
6876
TestReadContext(TestReadContextBuilder builder) {
6977
super(builder);
@@ -75,6 +83,21 @@ TransactionSelector getTransactionSelector() {
7583
}
7684
}
7785

86+
private final class TestReadContextWithTag extends AbstractReadContext {
87+
TestReadContextWithTag(TestReadContextWithTagBuilder builder) {
88+
super(builder);
89+
}
90+
91+
@Override
92+
TransactionSelector getTransactionSelector() {
93+
return TransactionSelector.getDefaultInstance();
94+
}
95+
96+
String getTransactionTag() {
97+
return "app=spanner,env=test";
98+
}
99+
}
100+
78101
private TestReadContext context;
79102

80103
@Before
@@ -162,4 +185,46 @@ public void testGetExecuteBatchDmlRequestBuilderWithPriority() {
162185
Options.fromQueryOptions(Options.priority(RpcPriority.LOW)));
163186
assertEquals(Priority.PRIORITY_LOW, request.getRequestOptions().getPriority());
164187
}
188+
189+
public void executeSqlRequestBuilderWithRequestOptions() {
190+
ExecuteSqlRequest request =
191+
context
192+
.getExecuteSqlRequestBuilder(
193+
Statement.newBuilder("SELECT FOO FROM BAR").build(),
194+
QueryMode.NORMAL,
195+
Options.fromUpdateOptions(Options.tag("app=spanner,env=test,action=query")),
196+
false)
197+
.build();
198+
assertThat(request.getSql()).isEqualTo("SELECT FOO FROM BAR");
199+
assertThat(request.getRequestOptions().getRequestTag())
200+
.isEqualTo("app=spanner,env=test,action=query");
201+
assertThat(request.getRequestOptions().getTransactionTag()).isEmpty();
202+
}
203+
204+
@Test
205+
public void executeSqlRequestBuilderWithRequestOptionsWithTxnTag() {
206+
SessionImpl session = mock(SessionImpl.class);
207+
when(session.getName()).thenReturn("session-1");
208+
TestReadContextWithTagBuilder builder = new TestReadContextWithTagBuilder();
209+
TestReadContextWithTag contextWithTag =
210+
builder
211+
.setSession(session)
212+
.setRpc(mock(SpannerRpc.class))
213+
.setDefaultQueryOptions(defaultQueryOptions)
214+
.setExecutorProvider(mock(ExecutorProvider.class))
215+
.build();
216+
217+
ExecuteSqlRequest request =
218+
contextWithTag
219+
.getExecuteSqlRequestBuilder(
220+
Statement.newBuilder("SELECT FOO FROM BAR").build(),
221+
QueryMode.NORMAL,
222+
Options.fromUpdateOptions(Options.tag("app=spanner,env=test,action=query")),
223+
false)
224+
.build();
225+
assertThat(request.getSql()).isEqualTo("SELECT FOO FROM BAR");
226+
assertThat(request.getRequestOptions().getRequestTag())
227+
.isEqualTo("app=spanner,env=test,action=query");
228+
assertThat(request.getRequestOptions().getTransactionTag()).isEqualTo("app=spanner,env=test");
229+
}
165230
}

0 commit comments

Comments
 (0)