Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate Unique Bidrequest IDs for Amp Auctions #1938

Merged
merged 3 commits into from
Sep 14, 2021

Conversation

AlexBVolcy
Copy link
Contributor

@AlexBVolcy AlexBVolcy commented Jul 26, 2021

Addressing this issue here #1507

I've added a configuration boolean flag, GenerateRequestId, which when set to true, will generate a uuid for auction bid requests, which can be used to solve the amp auction issue found here #1507

This generation can occur in two different functions:

  1. In amp_auction.go/loadRequestJSONForAmp( ) -- where we load the stored openRTB request for an incoming AMP request. Here we can check our boolean flag, and generate an ID if necessary.
  2. In auction.go/processStoredRequests() -- we check here if GenerateRequestId is true, and if so, we generate a uuid and patch it into the request

givenInTagId string
givenInStoredRequest json.RawMessage
givenGenerateStoredRequestId bool
expected int
Copy link
Collaborator

Choose a reason for hiding this comment

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

Might want to call this expectedIDLength as at first I was wondering how you arranged it to have generated ID always be "36".

@@ -1448,7 +1449,13 @@ func getStoredRequestId(data []byte) (string, bool, error) {
return "", true, errors.New("ext.prebid.storedrequest.id must be a string")
}

return string(value), true, nil
// Generates Request ID if flag is set to true -- used to solve issue with amp auctions
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is supposed to return the ID of the stored request, not the openrtb request's ID. Also by generating a random stored request ID you are virtually guaranteeing that the stored request lookup will fail, and potentially hammering the database with failing lookups.

Copy link
Collaborator

Choose a reason for hiding this comment

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

To avoid having to unmarshal/remarshal the stored request json, you probably want to patch the retrieved stored request with { "id": "<generated uuid>" } before applying the patch to the incoming request. Unless we want the spec that the flag should always overwrite any incoming request IDs, in which case we will have to do something else. But I don't see a use case for overwriting the request ID in the incoming request other than the case where a PBS instance is only supporting a client that always sends a non-random ID, and no other clients. But in this case it really should be fixed on the client side rather than hacking PBS to enforce unique IDs in all cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just to keep this PR updated, Hans and I talked about these comments and what I need to do to properly address them! A commit will be pushed today or tomorrow with the proper fixes / tests.

requestErrorJson, errL := storedRequestErrorChecker(err, requestJson, storedRequests, storedBidRequestId)
return requestErrorJson, nil, errL
}
} else if !deps.cfg.GenerateRequestId && hasStoredBidRequest {
Copy link
Collaborator

Choose a reason for hiding this comment

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

If you reach this line, and hasStoredBidRequest is true, then you know !deps.cfg.GenerateRequestId also has to be true, you don't need to test both.

Another potential logic flow is:

Suggested change
} else if !deps.cfg.GenerateRequestId && hasStoredBidRequest {
storedReqForPatch = ''
if hasStoredBidRequest {
storedReqForPatch = storedRequests[storedBidRequestId]
}
if deps.cfg.GenerateRequestId {
uuidPatch, err := generateUuidForBidRequest()
if hasStoredBidRequest {
storedReqForPatch, err := jsonpatch.MergePatch(storedRequests[storedBidRequestId], uuidPatch)
} else {
storedReqForPatch := uuidPatch
hasStoredBidRequest = true
}
}
if hasStoredBidRequest {
resolvedRequest, err = jsonpatch.MergePatch(requestJson, storedReqForPatch)
}

with the error handling and other supporting code included.

@@ -1089,7 +1161,7 @@ func TestOversizedRequest(t *testing.T) {
&mockStoredReqFetcher{},
empty_fetcher.EmptyFetcher{},
empty_fetcher.EmptyFetcher{},
&config.Configuration{MaxRequestSize: int64(len(reqBody) - 1)},
&config.Configuration{MaxRequestSize: int64(len(reqBody) - 1), GenerateRequestId: false},
Copy link
Collaborator

Choose a reason for hiding this comment

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

The nil value of a boolean is false, so you shouldn't have to explicitly set these values.

endpoints/openrtb2/amp_auction_test.go Outdated Show resolved Hide resolved
config/config.go Outdated Show resolved Hide resolved
config/config.go Outdated Show resolved Hide resolved
endpoints/openrtb2/amp_auction.go Show resolved Hide resolved
endpoints/openrtb2/amp_auction_test.go Show resolved Hide resolved
endpoints/openrtb2/auction.go Outdated Show resolved Hide resolved
endpoints/openrtb2/auction.go Show resolved Hide resolved
endpoints/openrtb2/auction.go Outdated Show resolved Hide resolved
endpoints/openrtb2/auction.go Show resolved Hide resolved
endpoints/openrtb2/auction.go Outdated Show resolved Hide resolved
@VeronikaSolovei9 VeronikaSolovei9 force-pushed the Generate_BidRequest_Id_Amp branch 2 times, most recently from 2747163 to a37cbec Compare August 24, 2021 19:10
hhhjort
hhhjort previously approved these changes Aug 26, 2021
Copy link
Collaborator

@hhhjort hhhjort left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@SyntaxNode SyntaxNode left a comment

Choose a reason for hiding this comment

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

This is missing the {{UUID}} macro, but otherwise looks solid. I'm good if you'd like to add the macro in a follow-up PR. The Java version only performs this behavior for app requests, is that a necessary restriction?

config/config.go Show resolved Hide resolved
endpoints/openrtb2/amp_auction_test.go Outdated Show resolved Hide resolved
endpoints/openrtb2/amp_auction_test.go Outdated Show resolved Hide resolved
endpoints/openrtb2/amp_auction_test.go Outdated Show resolved Hide resolved
endpoints/openrtb2/auction.go Outdated Show resolved Hide resolved
endpoints/openrtb2/auction.go Outdated Show resolved Hide resolved
endpoints/openrtb2/auction_test.go Outdated Show resolved Hide resolved
endpoints/openrtb2/auction.go Outdated Show resolved Hide resolved
@@ -1567,3 +1585,35 @@ func getAccountID(pub *openrtb2.Publisher) string {
}
return metrics.PublisherUnknown
}

func storedRequestErrorChecker(err error, requestJson []byte, storedRequests map[string]json.RawMessage, storedBidRequestId string) []error {
Copy link
Contributor

Choose a reason for hiding this comment

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

The err argument is not used. Please consider removing it.

newRequest, _, errList := deps.processStoredRequests(context.Background(), json.RawMessage(test.givenRawData))
for _, err := range errList {
assert.Empty(t, err, test.description)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

You don't need to use a loop here. You can simply write:

newRequest, _, errList := deps.processStoredRequests(context.Background(), json.RawMessage(test.givenRawData))
assert.Empty(t, errList, test.description)

@@ -344,6 +347,15 @@ func (deps *endpointDeps) loadRequestJSONForAmp(httpRequest *http.Request) (req
errs = []error{err}
return
}
// Generates Request ID if flag is set to true
Copy link
Contributor

Choose a reason for hiding this comment

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

Nitpick: Comment does not seem valuable.

isAppRequest, err := checkIfAppRequest(requestJson)
if err != nil {
return nil, nil, []error{err}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks good. I like that you've extracted the check to a method. To increase performance, I recommend moving this check within the deps.cfg.GenerateRequestID check, such that we only bother to check for an app request if the feature is turned on. I usually don't recommend nesting, but in this case it's a performance improvement in certain cases.

hhhjort
hhhjort previously approved these changes Sep 7, 2021
Copy link
Collaborator

@hhhjort hhhjort left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@SyntaxNode SyntaxNode left a comment

Choose a reason for hiding this comment

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

The AMP flow checks out with local end to end tests. Please see me comment for a bug in the APP flow.

return nil, nil, []error{err}
}
if isAppRequest {
uuidPatch, err := generateUuidForBidRequest(deps.uuidGenerator)
Copy link
Contributor

@SyntaxNode SyntaxNode Sep 9, 2021

Choose a reason for hiding this comment

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

The uuidPatch is being generated for all app requests, but it should only be generated when also hasStoredBidRequest is true.

Copy link
Collaborator

@hhhjort hhhjort Sep 13, 2021

Choose a reason for hiding this comment

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

Why do we want to disable generating request IDs when there is no stored request for an App request? Is that a performance tweak that we expect we should only need a request ID if there was a stored request involved?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Talked to SyntaxNode and this is desired behavior. Should be documented somewhere, in the end (not in the code, but the docs elsewhere) since it is not intuitively obvious when this feature should or should not kick in.

Copy link
Collaborator

@hhhjort hhhjort left a comment

Choose a reason for hiding this comment

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

LGTM

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.

4 participants