Skip to content

Conversation

yvrhdn
Copy link
Contributor

@yvrhdn yvrhdn commented Jul 20, 2023

What this PR does:
Add version handling and a new PATCH endpoint to the user-configurable overrides. Existing endpoints gain support for conditional headers adding protection against data races.

New PATCH endpoint: instead of providing the full limits struct, clients can send a partial json with the other fields set to null or omitted. End result: Tempo will read the existing user-configurable overrides and only updates the fields that are set in the patch.

Changes to GET, POST and DELETE:

GET and POST will return the current version of the overrides using the Etag header.

GET http://localhost:3200/api/overrides

HTTP/1.1 200 OK
Content-Type: application/json
Etag: 123

POST and DELETE must have the If-Match hedaer containing the version of a previous request. This ensures the overrides haven't been modified since the last GET.

backend.VersionedReaderWriter is a new interface to add version handling to backends. While GCS and Azure support conditional headers, S3 does not. Instead we adopt a best effort mechanism in which we fetch the current version and check it before writing.

Implementation:

  • GCS: All good ✅
  • S3: as good as we can get it without API support or external dependencies
  • Azure: TODO ❌ reading versions and setting if-match headers requires us to update to the new SDK, I will do this in a separate PR

Which issue(s) this PR fixes:
Fixes #

Checklist

Comment on lines +50 to +52
// ConfirmVersioning is enabled when creating the backend client. If versioning is disabled no
// checks against concurrent writes will be performed.
ConfirmVersioning bool `yaml:"confirm_versioning"`
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered placing this on gcs.Config instead but I'm worried this would conflict with our existing bucket configuration. For storing traces you don't need versioning, so setting this to default true would cause a lot of issues.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with the reasoning here 👍

if err != nil {
return
return nil, "", err
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a change in behaviour: instead of returning nil on not found just pass through the backend.ErrDoesNotExist error. I thought this would be easier and make the client more of a simple passthrough component.

@@ -198,6 +211,44 @@ func (rw *readerWriter) ReadRange(ctx context.Context, name string, keypath back
func (rw *readerWriter) Shutdown() {
}

func (rw *readerWriter) WriteVersioned(ctx context.Context, name string, keypath backend.KeyPath, data io.Reader, version backend.Version) (backend.Version, error) {
// TODO use conditional if-match API
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To properly implement this we need to upgrade our Azure SDK from azure-storage-blob-go to the newer azure-sdk-for-go. Since this impacts the entire package I'm going to do this in a separate PR...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Koenraad Verheyden added 2 commits July 24, 2023 18:30
…g-patch

# Conflicts:
#	CHANGELOG.md
#	cmd/tempo/app/modules.go
#	integration/e2e/overrides_test.go
#	modules/overrides/userconfigurableapi/client.go
#	modules/overrides/userconfigurableapi/http.go
#	modules/overrides/userconfigurableapi/http_test.go
Copy link
Contributor

@zalegrala zalegrala left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks pretty good to me. One comment about the signatures in a couple spots. I was reminded the other day while digging around in tempodb, that there are some good reasons to accept interfaces as arguments, and return structs. I think it can make working with those objects a little easier in some cases, like testing. "accept interfaces, return structs" goes back a ways in google, but I don't know the source. Anyway, maybe worth considering or some conversation. What do you think?


var _ VersionedReaderWriter = (*fakeVersionedReaderWriter)(nil)

func NewFakeVersionedReaderWriter(r RawReader, w RawWriter) VersionedReaderWriter {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider returning the struct instead of the interface.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good tip that I almost always forget about. I have the tendency to hide abstractions from my Java days, but I agree returning the struct gives more flexibility to the caller since they can create their own interface around it.

For this specific example: fakeVersionedReaderWriter is private and returning it gives me this warning in GoLand. Not sure if making it public or just returning the interface would be best here? 🤔😅

Screenshot 2023-07-26 at 15 40 06

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think making it public is okay.

Copy link
Contributor

@mapno mapno left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Just left a few minor comments.

Comment on lines +50 to +52
// ConfirmVersioning is enabled when creating the backend client. If versioning is disabled no
// checks against concurrent writes will be performed.
ConfirmVersioning bool `yaml:"confirm_versioning"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with the reasoning here 👍

@yvrhdn yvrhdn enabled auto-merge (squash) July 26, 2023 18:47
@yvrhdn yvrhdn merged commit 56bb981 into grafana:main Jul 26, 2023
@yvrhdn yvrhdn deleted the kvrhdn/user-configurable-overrides-versioning-patch branch July 26, 2023 19:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants