Skip to content

Commit 5637fc7

Browse files
bshafferamanda-tarafa
authored andcommitted
docs: Add product neutral documentation and examples.
- Show retry example - Add documentation and examples for update masks and updates with optimistic concurrency control.
1 parent 360226a commit 5637fc7

File tree

7 files changed

+196
-1
lines changed

7 files changed

+196
-1
lines changed

docs/devsite-help/call-settings.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,5 @@ The backoff between retries is more complex. It consists of:
113113

114114
- [RetrySettings.FromConstantBackoff](xref:Google.Api.Gax.Grpc.RetrySettings#Google_Api_Gax_Grpc_RetrySettings_FromConstantBackoff_System_Int32_System_TimeSpan_System_Predicate_System_Exception__Google_Api_Gax_Grpc_RetrySettings_IJitter_) which uses a multiplier of 1 and no maximum (because it's constant)
115115
- [RetrySettings.FromExponentialBackoff](xref:Google.Api.Gax.Grpc.RetrySettings#Google_Api_Gax_Grpc_RetrySettings_FromExponentialBackoff_System_Int32_System_TimeSpan_System_TimeSpan_System_Double_System_Predicate_System_Exception__Google_Api_Gax_Grpc_RetrySettings_IJitter_) which allows every aspect to be specified
116+
117+
[!code-cs[](../examples/help.CallSettings.txt#RetrySettingsTiming)]

docs/devsite-help/occ.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Optimistic Concurrency Control (OCC)
2+
3+
## Introduction to OCC
4+
5+
Optimistic Concurrency Control (OCC) is a strategy used to manage shared resources and prevent "lost updates" or race conditions when multiple users or processes attempt to modify the same resource simultaneously.
6+
7+
Google API resources contain an `Etag` property used by the server to control for optimistic concurrency. If the `Etag` value provided when modifying a resource does not match the current value on the server, it returns an error, usually with status `Aborted` (Check the documentation of the API you are using for specific OCC error codes).
8+
9+
## OCC Steps demonstrated with IAM
10+
11+
### Example
12+
13+
The following example demonstrates how to implement OCC with IAM and the `Google.Cloud.PubSub.V1` library.
14+
15+
[!code-cs[](../examples/help.Occ.txt#OccForIam)]

docs/devsite-help/toc.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
href: long-running-operations.md
2929
- name: Pagination (page streaming)
3030
href: page-streaming.md
31+
- name: Update masks
32+
href: update-masks.md
3133
- name: Resource names and IDs
3234
href: resource-names.md
3335
- name: API layers
@@ -36,6 +38,8 @@
3638
href: call-settings.md
3739
- name: Streaming RPCs
3840
href: grpc-streaming.md
41+
- name: OCC
42+
href: occ.md
3943
- name: Resource clean-up
4044
href: cleanup.md
4145
- name: Versioning

docs/devsite-help/update-masks.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Update Masks
2+
3+
## Optimizing Resource Updates with FieldMask
4+
5+
When updating resources (often corresponding to HTTP PATCH requests) in Google Cloud APIs, you generally use an **Update Mask** (represented by `Google.Protobuf.WellKnownTypes.FieldMask`).
6+
7+
The purpose of the Update Mask is to tell the server exactly which fields you intend to modify, preventing accidental overwrites or resetting of other fields that you did not include in your request object.
8+
9+
By providing a `FieldMask`, you explicitly declare the subset of fields in the resource object that should be updated.
10+
11+
## Constructing and Applying the FieldMask
12+
13+
The `FieldMask` object is part of the `Google.Protobuf` library and contains a collection of strings representing the fields paths to be modified.
14+
15+
**Crucial Point:** The strings in the `FieldMask.Paths` collection **must** correspond to the **original** field names defined in the Protocol Buffer (protobuf) schema, not the C\# property names (which are PascalCase).
16+
17+
## Example
18+
19+
[!code-cs[](../examples/help.UpdateMask.txt#UpdateMasks)]
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright 2025 Google Inc. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using Google.Cloud.Iam.V1;
16+
using Google.Cloud.PubSub.V1;
17+
using Grpc.Core;
18+
using System.Linq;
19+
using Xunit;
20+
21+
namespace Google.Cloud.Tools.Snippets;
22+
23+
[Collection(nameof(SnippetFixture))]
24+
public class OccSnippets
25+
{
26+
private readonly SnippetFixture _fixture;
27+
28+
public OccSnippets(SnippetFixture fixture) => _fixture = fixture;
29+
30+
[Fact]
31+
public void OccForIam()
32+
{
33+
string role = "roles/pubsub.viewer";
34+
string member = "domain:google.com";
35+
36+
var topicName = _fixture.CreateTopic();
37+
38+
// Sample: OccForIam
39+
PublisherServiceApiClient client = PublisherServiceApiClient.Create();
40+
41+
try
42+
{
43+
// Get the current IAM Policy with the current Etag.
44+
Policy policy = client.IAMPolicyClient.GetIamPolicy(new GetIamPolicyRequest
45+
{
46+
Resource = topicName,
47+
});
48+
49+
// Modify the local IAM Policy object.
50+
Binding binding = policy.Bindings.FirstOrDefault(b => b.Role == role);
51+
52+
if (binding != null)
53+
{
54+
if (!binding.Members.Contains(member))
55+
{
56+
binding.Members.Add(member);
57+
}
58+
}
59+
else
60+
{
61+
policy.Bindings.Add(new Binding
62+
{
63+
Role = role,
64+
Members = { member }
65+
});
66+
}
67+
68+
// Attempt to set the modified policy, it contains the Etag value from when it was read.
69+
client.IAMPolicyClient.SetIamPolicy(new SetIamPolicyRequest
70+
{
71+
Resource = topicName,
72+
Policy = policy
73+
});
74+
75+
// The policy was modified successfully.
76+
}
77+
catch (RpcException ex) when ( ex.StatusCode == StatusCode.Aborted )
78+
{
79+
// A concurrency conflict usually manifests as Aborted but depending on the API
80+
// other error codes may be used, like FailedPrecondition.
81+
// To ensure the OCC mechanism is robust, check the documentation for the API
82+
// you are implementing OCC for.
83+
84+
// Handle the etag mismatch. For example, retry the change or prompt the user to
85+
// submit their changes again.
86+
}
87+
// End sample
88+
}
89+
}

tools/Google.Cloud.Docs.Snippets/SnippetFixture.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2018 Google LLC
1+
// Copyright 2018 Google LLC
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -12,10 +12,12 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
using Google.Api.Gax.Grpc;
1516
using Google.Api.Gax.ResourceNames;
1617
using Google.Cloud.ClientTesting;
1718
using Google.Cloud.PubSub.V1;
1819
using System.Linq;
20+
using System.Threading.Tasks;
1921
using Xunit;
2022

2123
namespace Google.Cloud.Tools.Snippets
@@ -45,6 +47,14 @@ public SnippetFixture()
4547
/// </summary>
4648
internal string CreateSubscriptionId() => IdGenerator.FromGuid(prefix: SubscriptionPrefix);
4749

50+
internal string CreateTopic()
51+
{
52+
PublisherServiceApiClient client = PublisherServiceApiClient.Create();
53+
TopicName topicName = new TopicName(ProjectId, CreateTopicId());
54+
Topic topic = client.CreateTopic(topicName);
55+
return topic.Name;
56+
}
57+
4858
public override void Dispose()
4959
{
5060
var subscriber = SubscriberServiceApiClient.Create();
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2025 Google Inc. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using Google.Cloud.PubSub.V1;
16+
using Google.Protobuf.WellKnownTypes;
17+
using Xunit;
18+
19+
namespace Google.Cloud.Tools.Snippets;
20+
21+
[Collection(nameof(SnippetFixture))]
22+
public class UpdateMaskSnippets
23+
{
24+
private readonly SnippetFixture _fixture;
25+
26+
public UpdateMaskSnippets(SnippetFixture fixture) => _fixture = fixture;
27+
28+
[Fact]
29+
public void UpdateMasks()
30+
{
31+
var topicName = _fixture.CreateTopic();
32+
33+
// Sample: UpdateMasks
34+
PublisherServiceApiClient client = PublisherServiceApiClient.Create();
35+
36+
// Create a resource with the values you want to update
37+
Topic topic = new Topic
38+
{
39+
// Set the full resource name
40+
Name = topicName,
41+
// Populate the fields we intend to change
42+
Labels = { { "env", "production" } }
43+
};
44+
45+
// Create the FieldMask
46+
FieldMask updateMask = new FieldMask
47+
{
48+
// Add the original protobuf field name.
49+
// In this example, the C# property is 'Labels', but the protobuf field name is 'labels'.
50+
Paths = { "labels" }
51+
};
52+
53+
client.UpdateTopic(topic, updateMask);
54+
// End sample
55+
}
56+
}

0 commit comments

Comments
 (0)