Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions sdk/data/azcosmos/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# Release History

## 0.3.7 (Unreleased)
## 1.0.0 (Unreleased)

### Features Added
* Added regional routing support through ClientOptions.PreferredRegions
* Added cross-region availability and failover mechanics supporting [Azure Cosmos DB SDK multiregional environment behavior](https://learn.microsoft.com/azure/cosmos-db/nosql/troubleshoot-sdk-availability)
* Added extended logging for requests, responses, and client configuration

### Breaking Changes

### Bugs Fixed

### Other Changes
* ItemOptions.SessionToken, QueryOptions.SessionToken, QueryOptions.ContinuationToken are now `*string`
* ItemResponse.SessionToken, QueryItemsResponse.ContinuationToken are now `*string`

## 0.3.6 (2023-08-18)

Expand Down
4 changes: 2 additions & 2 deletions sdk/data/azcosmos/cosmos_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -431,11 +431,11 @@ func (c *ContainerClient) NewQueryItemsPager(query string, partitionKey Partitio

return runtime.NewPager(runtime.PagingHandler[QueryItemsResponse]{
More: func(page QueryItemsResponse) bool {
return page.ContinuationToken != ""
return page.ContinuationToken != nil
},
Fetcher: func(ctx context.Context, page *QueryItemsResponse) (QueryItemsResponse, error) {
if page != nil {
if page.ContinuationToken != "" {
if page.ContinuationToken != nil {
// Use the previous page continuation if available
queryOptions.ContinuationToken = page.ContinuationToken
}
Expand Down
4 changes: 2 additions & 2 deletions sdk/data/azcosmos/cosmos_container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -466,8 +466,8 @@ func TestContainerQueryItems(t *testing.T) {
receivedIds = append(receivedIds, itemResponseBody["id"].(string))
}

if queryPager.More() && queryResponse.ContinuationToken != "someContinuationToken" {
t.Errorf("Expected ContinuationToken to be %s, but got %s", "someContinuationToken", queryResponse.ContinuationToken)
if queryPager.More() && (queryResponse.ContinuationToken == nil || *queryResponse.ContinuationToken != "someContinuationToken") {
t.Errorf("Expected ContinuationToken to be %s, but got %s", "someContinuationToken", *queryResponse.ContinuationToken)
}

if queryResponse.QueryMetrics == nil || *queryResponse.QueryMetrics != "someQueryMetrics" {
Expand Down
6 changes: 3 additions & 3 deletions sdk/data/azcosmos/cosmos_item_request_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type ItemOptions struct {
// If you wanted these nodes to participate in the same session (to be able read your own writes consistently across web tiers),
// you would have to send the SessionToken from the response of the write action on one node to the client tier, using a cookie or some other mechanism, and have that token flow back to the web tier for subsequent reads.
// If you are using a round-robin load balancer which does not maintain session affinity between requests, such as the Azure Load Balancer,the read could potentially land on a different node to the write request, where the session was created.
SessionToken string
SessionToken *string
// ConsistencyLevel overrides the account defined consistency level for this operation.
// Consistency can only be relaxed.
ConsistencyLevel *ConsistencyLevel
Expand Down Expand Up @@ -55,8 +55,8 @@ func (options *ItemOptions) toHeaders() *map[string]string {
headers[cosmosHeaderIndexingDirective] = string(*options.IndexingDirective)
}

if options.SessionToken != "" {
headers[cosmosHeaderSessionToken] = options.SessionToken
if options.SessionToken != nil {
headers[cosmosHeaderSessionToken] = *options.SessionToken
}

if options.IfMatchEtag != nil {
Expand Down
3 changes: 2 additions & 1 deletion sdk/data/azcosmos/cosmos_item_request_options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ func TestItemRequestOptionsToHeaders(t *testing.T) {
options.PreTriggers = []string{"preTrigger1", "preTrigger2"}
options.PostTriggers = []string{"postTrigger1", "postTrigger2"}
options.ConsistencyLevel = ConsistencyLevelSession.ToPtr()
options.SessionToken = "sessionToken"
sessionToken := "sessionToken"
options.SessionToken = &sessionToken
options.IndexingDirective = IndexingDirectiveInclude.ToPtr()
etagValue := azcore.ETag("someEtag")
options.IfMatchEtag = &etagValue
Expand Down
7 changes: 5 additions & 2 deletions sdk/data/azcosmos/cosmos_item_response.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,17 @@ type ItemResponse struct {
Value []byte
Response
// SessionToken contains the value from the session token header to be used on session consistency.
SessionToken string
SessionToken *string
}

func newItemResponse(resp *http.Response) (ItemResponse, error) {
response := ItemResponse{
Response: newResponse(resp),
}
response.SessionToken = resp.Header.Get(cosmosHeaderSessionToken)
sessionToken := resp.Header.Get(cosmosHeaderSessionToken)
if sessionToken != "" {
response.SessionToken = &sessionToken
}
defer resp.Body.Close()
body, err := azruntime.Payload(resp)
if err != nil {
Expand Down
12 changes: 6 additions & 6 deletions sdk/data/azcosmos/cosmos_query_request_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type QueryOptions struct {
// If you wanted these nodes to participate in the same session (to be able read your own writes consistently across web tiers),
// you would have to send the SessionToken from the response of the write action on one node to the client tier, using a cookie or some other mechanism, and have that token flow back to the web tier for subsequent reads.
// If you are using a round-robin load balancer which does not maintain session affinity between requests, such as the Azure Load Balancer,the read could potentially land on a different node to the write request, where the session was created.
SessionToken string
SessionToken *string
// ConsistencyLevel overrides the account defined consistency level for this operation.
// Consistency can only be relaxed.
ConsistencyLevel *ConsistencyLevel
Expand All @@ -30,7 +30,7 @@ type QueryOptions struct {
EnableScanInQuery bool
// ContinuationToken to be used to continue a previous query execution.
// Obtained from QueryItemsResponse.ContinuationToken.
ContinuationToken string
ContinuationToken *string
// QueryParameters allows execution of parametrized queries.
// See https://docs.microsoft.com/azure/cosmos-db/sql/sql-query-parameterized-queries
QueryParameters []QueryParameter
Expand All @@ -43,8 +43,8 @@ func (options *QueryOptions) toHeaders() *map[string]string {
headers[cosmosHeaderConsistencyLevel] = string(*options.ConsistencyLevel)
}

if options.SessionToken != "" {
headers[cosmosHeaderSessionToken] = options.SessionToken
if options.SessionToken != nil {
headers[cosmosHeaderSessionToken] = *options.SessionToken
}

if options.ResponseContinuationTokenLimitInKB > 0 {
Expand All @@ -63,8 +63,8 @@ func (options *QueryOptions) toHeaders() *map[string]string {
headers[cosmosHeaderPopulateIndexMetrics] = "true"
}

if options.ContinuationToken != "" {
headers[cosmosHeaderContinuationToken] = options.ContinuationToken
if options.ContinuationToken != nil {
headers[cosmosHeaderContinuationToken] = *options.ContinuationToken
}

headers[cosmosHeaderPopulateQueryMetrics] = "true"
Expand Down
6 changes: 4 additions & 2 deletions sdk/data/azcosmos/cosmos_query_request_options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import (
func TestQueryRequestOptionsToHeaders(t *testing.T) {
options := &QueryOptions{}
options.ConsistencyLevel = ConsistencyLevelSession.ToPtr()
options.SessionToken = "sessionToken"
sessionToken := "sessionToken"
options.SessionToken = &sessionToken
options.PageSizeHint = 20
options.EnableScanInQuery = true
options.ResponseContinuationTokenLimitInKB = 100
options.PopulateIndexMetrics = true
options.ContinuationToken = "continuationToken"
continuation := "continuationToken"
options.ContinuationToken = &continuation
header := options.toHeaders()
if header == nil {
t.Fatal("toHeaders should return non-nil")
Expand Down
7 changes: 5 additions & 2 deletions sdk/data/azcosmos/cosmos_query_response.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type QueryItemsResponse struct {
Response
// ContinuationToken contains the value of the x-ms-continuation header in the response.
// It can be used to stop a query and resume it later.
ContinuationToken string
ContinuationToken *string
// Contains the query metrics related to the query execution
QueryMetrics *string
// IndexMetrics contains the index utilization metrics if QueryOptions.PopulateIndexMetrics = true
Expand All @@ -29,7 +29,10 @@ func newQueryResponse(resp *http.Response) (QueryItemsResponse, error) {
Response: newResponse(resp),
}

response.ContinuationToken = resp.Header.Get(cosmosHeaderContinuationToken)
continuationToken := resp.Header.Get(cosmosHeaderContinuationToken)
if continuationToken != "" {
response.ContinuationToken = &continuationToken
}
queryMetrics := resp.Header.Get(cosmosHeaderQueryMetrics)
if queryMetrics != "" {
response.QueryMetrics = &queryMetrics
Expand Down
9 changes: 9 additions & 0 deletions sdk/data/azcosmos/cosmos_query_response_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ func TestQueryResponseParsing(t *testing.T) {
t.Errorf("Expected 2 documents, but got %d", len(parsedResponse.Items))
}

if parsedResponse.ContinuationToken != nil {
t.Fatal("parsedResponse.ContinuationToken is not nil")
}

for index, item := range parsedResponse.Items {
var itemResponseBody map[string]interface{}
err = json.Unmarshal(item, &itemResponseBody)
Expand Down Expand Up @@ -116,6 +120,7 @@ func TestQueryResponseParsingWithMaxInt64(t *testing.T) {
mock.WithHeader(cosmosHeaderQueryMetrics, "someQueryMetrics"),
mock.WithHeader(cosmosHeaderIndexUtilization, "indexUtilization"),
mock.WithHeader(cosmosHeaderActivityId, "someActivityId"),
mock.WithHeader(cosmosHeaderContinuationToken, "someContinuation"),
mock.WithHeader(cosmosHeaderRequestCharge, "13.42"))

req, err := azruntime.NewRequest(context.Background(), http.MethodGet, srv.URL())
Expand Down Expand Up @@ -154,6 +159,10 @@ func TestQueryResponseParsingWithMaxInt64(t *testing.T) {
t.Errorf("Expected IndexUtilization to be %s, but got %s", "indexUtilization", *parsedResponse.IndexMetrics)
}

if *parsedResponse.ContinuationToken != "someContinuation" {
t.Errorf("Expected ContinuationToken to be %s, but got %s", "someContinuation", *parsedResponse.ContinuationToken)
}

if len(parsedResponse.Items) != 2 {
t.Errorf("Expected 2 documents, but got %d", len(parsedResponse.Items))
}
Expand Down
2 changes: 1 addition & 1 deletion sdk/data/azcosmos/emulator_cosmos_aad_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func TestAAD(t *testing.T) {
t.Fatalf("Failed to create item: %v", err)
}

if itemResponse.SessionToken == "" {
if itemResponse.SessionToken == nil {
t.Fatalf("Session token is empty")
}

Expand Down
4 changes: 2 additions & 2 deletions sdk/data/azcosmos/emulator_cosmos_item_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func TestItemCRUD(t *testing.T) {
t.Fatalf("Failed to create item: %v", err)
}

if itemResponse.SessionToken == "" {
if itemResponse.SessionToken == nil {
t.Fatalf("Session token is empty")
}

Expand Down Expand Up @@ -227,7 +227,7 @@ func TestItemCRUDforNullPartitionKey(t *testing.T) {
t.Fatalf("Failed to create item: %v", err)
}

if itemResponse.SessionToken == "" {
if itemResponse.SessionToken == nil {
t.Fatalf("Session token is empty")
}

Expand Down
10 changes: 5 additions & 5 deletions sdk/data/azcosmos/emulator_cosmos_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func TestSinglePartitionQueryWithIndexMetrics(t *testing.T) {
receivedIds = append(receivedIds, itemResponseBody["id"].(string))
}

if queryPager.More() && queryResponse.ContinuationToken == "" {
if queryPager.More() && queryResponse.ContinuationToken == nil {
t.Fatal("Query has more pages but no continuation was provided")
}

Expand Down Expand Up @@ -125,7 +125,7 @@ func TestSinglePartitionQuery(t *testing.T) {
receivedIds = append(receivedIds, itemResponseBody["id"].(string))
}

if queryPager.More() && queryResponse.ContinuationToken == "" {
if queryPager.More() && queryResponse.ContinuationToken == nil {
t.Fatal("Query has more pages but no continuation was provided")
}

Expand All @@ -149,7 +149,7 @@ func TestSinglePartitionQuery(t *testing.T) {
t.Fatalf("Expected 5 items, got %d", len(queryResponse.Items))
}

if numberOfPages == 2 && opt.ContinuationToken != "" {
if numberOfPages == 2 && opt.ContinuationToken != nil {
t.Fatalf("Original options should not be modified, initial continuation was empty, now it has %v", opt.ContinuationToken)
}
}
Expand Down Expand Up @@ -261,7 +261,7 @@ func TestSinglePartitionQueryWithProjection(t *testing.T) {
receivedIds = append(receivedIds, itemResponseBody)
}

if queryPager.More() && queryResponse.ContinuationToken == "" {
if queryPager.More() && queryResponse.ContinuationToken == nil {
t.Fatal("Query has more pages but no continuation was provided")
}

Expand All @@ -285,7 +285,7 @@ func TestSinglePartitionQueryWithProjection(t *testing.T) {
t.Fatalf("Expected 5 items, got %d", len(queryResponse.Items))
}

if numberOfPages == 2 && opt.ContinuationToken != "" {
if numberOfPages == 2 && opt.ContinuationToken != nil {
t.Fatalf("Original options should not be modified, initial continuation was empty, now it has %v", opt.ContinuationToken)
}
}
Expand Down
2 changes: 1 addition & 1 deletion sdk/data/azcosmos/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ func ExampleContainerClient_ReadItem_sessionConsistency() {
}

itemSessionToken := itemResponse.SessionToken
fmt.Printf("Create response contained session %s", itemSessionToken)
fmt.Printf("Create response contained session %s", *itemSessionToken)

// In another client, maintain the session by passing the session token
itemResponse, err = container.ReadItem(context.Background(), pk, id, &azcosmos.ItemOptions{SessionToken: itemSessionToken})
Expand Down
2 changes: 1 addition & 1 deletion sdk/data/azcosmos/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
package azcosmos

// serviceLibVersion is the semantic version (see http://semver.org) of this module.
const serviceLibVersion = "v0.3.7"
const serviceLibVersion = "v1.0.0"