-
Notifications
You must be signed in to change notification settings - Fork 699
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
Mirror stored imp data to bid response #1889
Conversation
endpoints/openrtb2/auction.go
Outdated
} | ||
|
||
if includeStoredImps && offset != -1 { | ||
impId, err := jsonparser.GetString(resolvedImp, "id") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A Note On jsonparser
:
This is substantially faster for reading values from a json blob than the Go json package, but you need to keep in mind that each jsonparser.GetXXX
call re-parses the entire json. Based on performance measurements, the tipping point of efficiency is around 4 calls. At that point, please consider switching to EachKey
to use a single pass.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. We are about to reach 4 calls.
The very last call takes storedImps[impIds[i]]
as input, 3 others resolvedImp
I added a comment about this.
endpoints/openrtb2/auction.go
Outdated
videoData, _, _, err := jsonparser.Get(storedImps[impIds[i]], "video") | ||
if err == nil { | ||
impToStoredReq[impId] = videoData | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To improve performance for non-video ads, please nest the other jsonparser checks under includeStoredImps
. ie:
includeVideoAttributes, err := jsonparser.GetBoolean(resolvedImp, "ext", "prebid", "options", "echovideoattrs")
if err != nil && err != jsonparser.KeyPathNotFoundError {
return nil, nil, []error{err}
}
if includeVideoAttributes {
_, _, videoImpOffset, err := jsonparser.Get(resolvedImp, "video")
if err != nil && err != jsonparser.KeyPathNotFoundError {
return nil, nil, []error{err}
}
if videoImpOffset != -1 {
impId, err := jsonparser.GetString(resolvedImp, "id")
if err != nil {
return nil, nil, []error{err}
}
//extract video attributes only
videoData, _, _, err := jsonparser.Get(storedImps[impIds[i]], "video")
if err == nil {
impToStoredReq[impId] = videoData
}
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, modified
endpoints/openrtb2/auction.go
Outdated
//extract video attributes only | ||
videoData, _, _, err := jsonparser.Get(storedImps[impIds[i]], "video") | ||
if err == nil { | ||
impToStoredReq[impId] = videoData |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name impToStoredReq
is now a bit misleading since it's a subset for video auctions. Perhaps it would be best to include the full stored request here and "cut out" the video section to bid response handler?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went through this again. The reason I put this here is to optimize amount of data we have in memory (have only video impressions we will use later) and to reduce complexity of code execution in insertStoredImpData
function: if no stored video data found - do not execute any logic. Also if we leave this code here - it will fit the logic around it: prepare stored request/video data.
I'm open to modify this and move this piece of code to bid response handler (insertStoredImpData). Please advice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed impToStoredReq
to impToStoredVideoReq
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modified according to discussion
endpoints/openrtb2/auction.go
Outdated
@@ -1391,22 +1394,52 @@ func (deps *endpointDeps) processStoredRequests(ctx context.Context, requestJson | |||
err = fmt.Errorf("imp.ext.prebid.storedrequest.id %s: Stored Imp has Invalid JSON: %s", impIds[i], Err) | |||
} | |||
} | |||
return nil, []error{err} | |||
return nil, impToStoredVideoReq, []error{err} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not return nil
here instead of impToStoredVideoReq
? I know that this map will be empty here but an empty map is not equal to nil
and so any potential nil checks on this map outside of this function would fail and also from a reading perspective, nil
clearly indicates that a nil map is returned vs impToStoredVideoReq
indicates that there might be some key-values in this map
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, modified
endpoints/openrtb2/auction.go
Outdated
return nil, nil, []error{err} | ||
} | ||
//extract video attributes only | ||
videoData, _, _, err := jsonparser.Get(storedImps[impIds[i]], "video") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about including the entire stored request here rather than just the video stored req part? You can then check for the includeVideoAttributes
flag on response building and if it is set, extract the video part and include that in the response ext. This way the entire stored request would be present in the Auction
object throughout the auction process, in case anything else needs it in the future. Also, doing something response specific in processStoredRequests
just doesn't seem right
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modified according to discussion
fe7c11a
to
e7e5324
Compare
@mansinahar @SyntaxNode code modified according to our code review discussion, please re-review |
exchange/exchange.go
Outdated
@@ -135,6 +136,11 @@ func NewExchange(adapters map[openrtb_ext.BidderName]adaptedBidder, cache prebid | |||
} | |||
} | |||
|
|||
type StoredImpData struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably call this something along the lines of ImpExtInfo
as the thought here is that this struct would be able to hold imp related extra info like the echoVideoAttrs
flag and the stored imp data. And then the map in AuctionRequest
should probably be called something like ImpExtInfoMap
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modified
exchange/exchange.go
Outdated
@@ -135,6 +136,11 @@ func NewExchange(adapters map[openrtb_ext.BidderName]adaptedBidder, cache prebid | |||
} | |||
} | |||
|
|||
type StoredImpData struct { | |||
IncludeVideoAttributes bool | |||
Data []byte |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And this should the probably be called StoredImpData
or just StoredImp
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed
endpoints/openrtb2/auction.go
Outdated
@@ -192,6 +192,7 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http | |||
LegacyLabels: labels, | |||
Warnings: warnings, | |||
GlobalPrivacyControlHeader: secGPC, | |||
ImpToStoredReq: impExtInfoMap, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@VeronikaSolovei9 Looks like you missed renaming this field here in AuctionRequest
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, renamed.
endpoints/openrtb2/auction_test.go
Outdated
for i, requestData := range testStoredRequestsErrors { | ||
_, _, errList := deps.processStoredRequests(context.Background(), json.RawMessage(requestData)) | ||
if len(errList) == 0 { | ||
t.Error("processStoredRequests should return error") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does t.Error stop the running test - or is that t.Fatal?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some reason I cannot answer to your comment about resolvedImp
.
At this moment we already have final imp in byte array, but don't have it in object yet.
resolvedImp, err := jsonpatch.MergePatch(storedImps[impIds[i]], imps[idIndices[i]])
We have raw data here that we want to return in response.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
t.Error
reports error in logs, but doesn't stop the execution
endpoints/openrtb2/auction_test.go
Outdated
|
||
var testStoreVideoAttr = []bool{ | ||
true, true, false, false, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style Nitpick: Consider making the slices definitions in-line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modified!
exchange/exchange.go
Outdated
@@ -935,7 +943,7 @@ func (e *exchange) makeBid(bids []*pbsOrtbBid, auc *auction, returnCreative bool | |||
} | |||
} | |||
|
|||
if bidExtJSON, err := makeBidExtJSON(bid.bid.Ext, bidExtPrebid); err != nil { | |||
if bidExtJSON, err := makeBidExtJSON(bid.bid.Ext, bidExtPrebid, impExtInfoMap[bid.bid.ImpID]); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we need to first check if impExtInfoMap
even contains an element with bid.bid.ImpID
before we pass it to makeBidExtJSON
? Because not all imps might have a stored Imp
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added
exchange/exchange.go
Outdated
return nil, err | ||
|
||
if len(ext) != 0 { | ||
// update existing bid.ext with our prebid section. if bid.ext.prebid already exists, it will be overwritten. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please update this comment to incorporate for your changes about video attributes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment is still valid.
if bid.ext.prebid already exists, it will be overwritten
- this is what it does.
EchoVideoAttrs will be inserted in bid.ext
not bid.ext.prebid
in further processing.
I'm open to modify this comment to anything better, please post your suggestions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I should have been more clearer. I agree with what you said above. What I meant by my comment was that maybe we should extend that comment to also add a note about the video attributes. Something like:
// update existing bid.ext with our prebid section and add stored video
// attributes for imp if echoVideoAttrs set to true.
// if bid.ext.prebid already exists, it will be overwritten.
Also, perhaps this comment makes more sense to be on the first line of this function
exchange/exchange.go
Outdated
|
||
if impExtInfo.EchoVideoAttrs { | ||
videoData, _, _, err := jsonparser.Get(impExtInfo.StoredImp, "video") | ||
if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we ignore the KeyPathNotFoundError
here because a stored imp might not always have a video
part and if it doesn't I assume we'd just wanna continue rather than return an error, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If request is valid, there shouldn't be KeyPathNotFoundError
. But I agree this case needs to be handled. Unit test added as well. Good catch!
exchange/exchange.go
Outdated
) | ||
|
||
type ContextKey string | ||
|
||
const DebugContextKey = ContextKey("debugInfo") | ||
const StoredRequestAttributes = "storedrequestattributes" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think openrtb_ext/bid.go
would make a better place for this as we're using this value as a key in the bid ext?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was also thinking about it too. And the thing is that PrebidExtKey
constant is located in openrtb_ext/device.go
. There are no bid.ext constants in bid.go. Not sure where this one should go.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I was wondering the same about PrebidExtKey
but PrebidExtKey
is a constant key that is valid for different extension objects such as bid and imp. So we want to have that in a common place. That common place definitely shouldn't be device.go
.. maybe an openrtb_ext.go
file that can hold common stuff like this but that's for another PR some other time :D. For now, I think StoredRequestAttributes
would make more sense in openrtb_ext/bid.go
since that is specific to bid ext.
impExtInfo: ImpExtInfo{true, []byte(`{"video":{!}}`)}, | ||
expectedBidExt: ``, | ||
expectedErrMessage: "invalid character", | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please consider adding a test case where the impExtInfo
doesn't have a video part in it. Also, this isn't exactly your change but if it's not a lot of effort can I please request to add a test where ext
has a prebid
part and that gets overwritten by extBidPrebid
? Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, added.
@@ -1400,7 +1400,7 @@ func TestBidReturnsCreative(t *testing.T) { | |||
|
|||
//Run tests | |||
for _, test := range testCases { | |||
resultingBids, resultingErrs := e.makeBid(sampleBids, sampleAuction, test.inReturnCreative) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might wanna modify one of these valid buildBidResponse
test cases to include impExtInfo
and make sure that it is propagated correctly downstream and we are getting the expected values back in bid ext.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added new unit test for this: TestMakeBidsVideoAttributes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure if we need TestMakeBidsVideoAttributes
because all those test cases are already being tested in the TestMakeBidExtJSON
test that you added. In makeBids
we only pass impExtInfo
downstream, we don't really have any logic addition/deletion/modification there. In such cases I'd just add one new test case for that upstream method (in this case makeBids
) to make sure the value is being passed downstream as expected. Exhaustive test cases about the downstream method (in this case makeBidExtJSON
) where the actual logic is changed makes sense only for the test for that method. This is why I recommended that we just add a new test case for buildBidResponse
or modify and existing test case for buildBidResponse
to just make sure that the value is properly passed all the way downstream to makeBidExtJSON
.
I hope this makes sense!
endpoints/openrtb2/auction_test.go
Outdated
|
||
for i, requestData := range testStoredRequestsErrors { | ||
_, _, errList := deps.processStoredRequests(context.Background(), json.RawMessage(requestData)) | ||
if len(errList) == 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: I'd recommend using assert.Len
here rather. I know there are parts of our code where we don't use the assert library for test assertions but if we can do that going forward to keep consistency then that'd be great. The assert library makes the test assertion code a little leaner and much easier to read
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modified it to assert.Empty
, but personally len(errList) == 0
is easier to read for me.
endpoints/openrtb2/auction_test.go
Outdated
} | ||
err := errList[0] | ||
assert.Equal(t, testStoredRequestsErrorsResults[i], err.Error(), "Incorrect error") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can rather use assert.Contains
here so that you don't need to hardcode always looking at the first error in the slice. If the code changes in the future which may result in that error to move further down the slice then this would break even though the slice would still contain the expected error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, my bad I usually use assert.Contains
.
@@ -1400,7 +1400,7 @@ func TestBidReturnsCreative(t *testing.T) { | |||
|
|||
//Run tests | |||
for _, test := range testCases { | |||
resultingBids, resultingErrs := e.makeBid(sampleBids, sampleAuction, test.inReturnCreative) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure if we need TestMakeBidsVideoAttributes
because all those test cases are already being tested in the TestMakeBidExtJSON
test that you added. In makeBids
we only pass impExtInfo
downstream, we don't really have any logic addition/deletion/modification there. In such cases I'd just add one new test case for that upstream method (in this case makeBids
) to make sure the value is being passed downstream as expected. Exhaustive test cases about the downstream method (in this case makeBidExtJSON
) where the actual logic is changed makes sense only for the test for that method. This is why I recommended that we just add a new test case for buildBidResponse
or modify and existing test case for buildBidResponse
to just make sure that the value is properly passed all the way downstream to makeBidExtJSON
.
I hope this makes sense!
exchange/exchange.go
Outdated
return nil, err | ||
|
||
if len(ext) != 0 { | ||
// update existing bid.ext with our prebid section. if bid.ext.prebid already exists, it will be overwritten. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I should have been more clearer. I agree with what you said above. What I meant by my comment was that maybe we should extend that comment to also add a note about the video attributes. Something like:
// update existing bid.ext with our prebid section and add stored video
// attributes for imp if echoVideoAttrs set to true.
// if bid.ext.prebid already exists, it will be overwritten.
Also, perhaps this comment makes more sense to be on the first line of this function
exchange/exchange.go
Outdated
) | ||
|
||
type ContextKey string | ||
|
||
const DebugContextKey = ContextKey("debugInfo") | ||
const StoredRequestAttributes = "storedrequestattributes" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I was wondering the same about PrebidExtKey
but PrebidExtKey
is a constant key that is valid for different extension objects such as bid and imp. So we want to have that in a common place. That common place definitely shouldn't be device.go
.. maybe an openrtb_ext.go
file that can hold common stuff like this but that's for another PR some other time :D. For now, I think StoredRequestAttributes
would make more sense in openrtb_ext/bid.go
since that is specific to bid ext.
Comments addressed. please take another look |
@@ -135,7 +135,7 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http | |||
deps.analytics.LogAuctionObject(&ao) | |||
}() | |||
|
|||
req, errL := deps.parseRequest(r) | |||
req, impExtInfoMap, errL := deps.parseRequest(r) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: Looking at the code a bit more with the new flow, how do you feel about naming this impExtInfoByID
to provide context of the map key? I don't feel strongly about this if you feel differently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We agreed with this name with Mansi earlier. I personally like impExtInfoMap
more, but I'm open to rename it. @mansinahar what do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both of those names sound good to me. I don't feel strongly about either. Will leave it up to you Veronika.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Thanks for being patient with this one and addressing all the feedback @VeronikaSolovei9. Appreciate it :)
Implements #1075