-
Notifications
You must be signed in to change notification settings - Fork 750
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
Introduce SeatNonBid in hookoutcome #3416
base: master
Are you sure you want to change the base?
Introduce SeatNonBid in hookoutcome #3416
Conversation
openrtb_ext/seat_non_bids.go
Outdated
// NonBidsWrapper contains the map of seat with list of nonBids | ||
type NonBidsWrapper struct { | ||
seatNonBidsMap map[string][]NonBid | ||
} |
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 use the term 'wrapper" in this package for helper objects which assist with json encoding/decoding. This is a different usage. Please use a different term, such as something like NonBidCollection
.
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.
ok, changed it to NonBidCollection
openrtb_ext/seat_non_bids.go
Outdated
|
||
// AddBid adds the nonBid into the map against the respective seat. | ||
// Note: This function is not a thread safe. | ||
func (snb *NonBidsWrapper) AddBid(bidParams NonBidParams) { |
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 method has two responsibilities, to build a NonBid object and add it to the collection. I recommend to separate these responsibilities. A helper function named something like NewNonBid
or BuildNonBid
could be encapsulate building a NonBid from a combination of the bid, reason, seat, etc...
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.
done, introduced new function NewNonBid
openrtb_ext/seat_non_bids.go
Outdated
ImpId: bidParams.Bid.ImpID, | ||
StatusCode: bidParams.NonBidReason, | ||
Ext: NonBidExt{ | ||
Prebid: ExtResponseNonBidPrebid{Bid: NonBidObject{ |
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.
It's strange to see a type called NonBidObject
instead of simply NonBid
. I'd like the names to be clearer.
We often prepend Ext
for structures contained within an extension field. However, Seat Non Bid is intended to be promoted to the top level response in the future so I support the existing names of SeatNonBid
and NonBid
. These names would remain the same when the structures are moved into the OpenRTB repo.
I suggest we rename NonBidExt
to ExtNonBid
to better align with the naming pattern used for other objects. ExtReponseNonBidPrebid
can be shorted to ExtNonBidPrebid
for improved readability and to enforce the relationship with it's parent ExtNonBid
structure. Then the unclear name NonBidObject
can be renamed to ExtNonBidPrebidBid
which is a little wordy but very clear.
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 the variables like below
NonBidExt
------> ExtNonBid
ExtReponseNonBidPrebid
----> ExtNonBidPrebid
NonBidObject
---> ExtNonBidPrebidBid
@@ -138,6 +139,12 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h | |||
activityControl := privacy.ActivityControl{} | |||
|
|||
defer func() { | |||
// if AmpObject.AuctionResponse is nil then collect nonbids from all stage outcomes and set it in the AmpObject.SeatNonBid | |||
// Nil AmpObject.AuctionResponse indicates the occurrence of a fatal error. | |||
if ao.AuctionResponse == 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.
Is there a more direct way for checking the occurrence of a fatal error instead of needing to explain the significance of an empty response in a comment? Hopefully one we can reuse in other parts of the code instead of copying/pasting this comment.
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.
One direct way would be to call below 2 lines after every return
statement.
seatNonBid.Append(getNonBidsFromStageOutcomes(hookExecutor.GetOutcomes()))
ao.SeatNonBid = seatNonBid.Get()
But currently there are around 6/7 return
statement in Auction()
& AmpAuction()
function. To avoid the repetitive code, added this block in defer statement.
Thoughts ??
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 think defer block is a good place for this.
Can we rely on the errors in ao
? Looks like fatal errors should be added in ao
in case of every error in the endpoint, like: ao.Errors = append(ao.Errors, rejectErr)
Same for equivalent logic in "auction.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.
You could perhaps have a foundErrors
boolean flag that starts false, and then is set to true whenever errors are detected. Then use this flag in the defer rather than checking for nil, resulting in a more straightforward way to satisfy the conditional, and futureproofing against any possible change where either an error happens after ao.AuctionResponse
is set or it stays nil despite no 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.
Used foundError
instead of ao.Response
nil check.
endpoints/openrtb2/amp_auction.go
Outdated
@@ -325,6 +340,7 @@ func sendAmpResponse( | |||
labels metrics.Labels, | |||
ao analytics.AmpObject, | |||
errs []error, | |||
seatNonBid *openrtb_ext.NonBidsWrapper, |
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 is this passed as a pointer / by reference?
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.
No need to pass it as pointer. corrected it.
@@ -352,6 +368,8 @@ func sendAmpResponse( | |||
glog.Errorf("/openrtb2/amp Critical error unpacking targets: %v", err) | |||
ao.Errors = append(ao.Errors, fmt.Errorf("Critical error while unpacking AMP targets: %v", err)) | |||
ao.Status = http.StatusInternalServerError | |||
seatNonBid.Append(getNonBidsFromStageOutcomes(hookExecutor.GetOutcomes())) |
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.
Related to my above comment, seatNonBid
could be nil
here.. leading to a confusing situation.
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.
Agree, made correction. Now passing the seatNonBid as a copy.
hooks/hookexecution/outcome.go
Outdated
DebugMessages []string `json:"debug_messages,omitempty"` | ||
Errors []string `json:"-"` | ||
Warnings []string `json:"-"` | ||
SeatNonBid openrtb_ext.NonBidsWrapper `json:"-"` |
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 is this not a slice of SeatNonBid objects?
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 believe, building the standard openrtb_ext.SeatNonBid
is expensive because each nonbid
needs to be mapped against respective seat
and impid
.
Similarly, obtaining standard openrtb_ext.SeatNonBid
object from each stageOutcome and again building the final openrtb_ext.SeatNonBid
would add additional computational overhead.
Example -
Assume, RawBidderResponse hook returns
SeatNonBid: []openrtb_ext.SeatNonBid{
{
NonBid: []openrtb_ext.NonBid{
{
ImpId: "imp1",
StatusCode: 100,
}
},
Seat: "pubmatic",
}
& AuctionResponseHook hook returns
SeatNonBid: []openrtb_ext.SeatNonBid{
{
NonBid: []openrtb_ext.NonBid{
{
ImpId: "imp2",
StatusCode: 100,
}
},
Seat: "pubmatic",
}
But we need to combine them for bidresponseExt.Prebid.SeatNonBid and auctionObject.SeatNonBid like
SeatNonBid: []openrtb_ext.SeatNonBid{
{
NonBid: []openrtb_ext.NonBid{
{
ImpId: "imp1",
StatusCode: 100,
},
{
ImpId: "imp2",
StatusCode: 100,
}
},
Seat: "pubmatic",
}
But, if we allow hookOutcome to return openrtb_ext.NonBidsWrapper/openrtb_ext.NonBidCollection
then it become easy to add non-bids by using SeatNonBid.AddBid()
function.
To combine the SeatNonBids from multiple hookOutcomes, we can use SeatNonBid.Append()
function.
Please refer - getNonBidsFromStageOutcomes
function which does the same.
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 mostly questions for better understanding the context.
if ao.AuctionResponse != nil { | ||
// this check ensures that we collect nonBids from stageOutcomes only once. | ||
// there could be a case where ao.AuctionResponse nil and reqWrapper.RebuildRequest returns error | ||
seatNonBid.Append(getNonBidsFromStageOutcomes(hookExecutor.GetOutcomes())) | ||
ao.SeatNonBid = seatNonBid.Get() | ||
} |
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.
Is this needed here? It duplicates logic from defer
block
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.
in defer, we are collecting non-bids only when ao.AuctionResponse is nil.
This is one place, where we can get the ao.AuctionResponse as non-nil and defer will not collect the non-bids.
@@ -138,6 +139,12 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h | |||
activityControl := privacy.ActivityControl{} | |||
|
|||
defer func() { | |||
// if AmpObject.AuctionResponse is nil then collect nonbids from all stage outcomes and set it in the AmpObject.SeatNonBid | |||
// Nil AmpObject.AuctionResponse indicates the occurrence of a fatal error. | |||
if ao.AuctionResponse == 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.
I think defer block is a good place for this.
Can we rely on the errors in ao
? Looks like fatal errors should be added in ao
in case of every error in the endpoint, like: ao.Errors = append(ao.Errors, rejectErr)
Same for equivalent logic in "auction.go"
want want | ||
}{ | ||
{ | ||
description: "request parsing failed, auctionObject should contain seatNonBid", |
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 part is not clear to me. parseRequest
has nothing to do with bid responses. Do we still want to include seatNonBid
to the auction 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.
As part of the SeatNonBid proposal (#2367) , seatNonBid is not just related to bid-responses.
Please refer NBR codes (202: Error - bad input parameters, 116: No Bid - Incomplete SupplyChain) this needs to be capture inside parseRequest
.
stageOutcomes := hookExecutor.GetOutcomes() | ||
seatNonBid.Append(getNonBidsFromStageOutcomes(stageOutcomes)) | ||
ao.SeatNonBid = seatNonBid.Get() |
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 code is also duplicating logic in defer
. Func sendAuctionResponse
executes right before the defer block. Do we need it here?
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.
In sendAuctionResponse
, we will always get the non-nil ao.Response
.
In defer block, we are collecting nonBids from stageOutcomes only when ao.Response
is nil.
We need it here, because we need to add the seatNonBids in resp.ext.prebid.seatnonbid
openrtb_ext/seat_non_bids.go
Outdated
@@ -0,0 +1,80 @@ | |||
package openrtb_ext | |||
|
|||
import "github.com/prebid/openrtb/v19/openrtb2" |
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 updated the OpenRTB library to version 20. Please merge with the master branch and change these imports to "github.com/prebid/openrtb/v20/openrtb2"
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.
done
@VeronikaSolovei9
Also, there are following cases where ao.Errors is not-empty and we don't want to call
As per current implementation, my requirement for auction.go is to call For amp_auction.go , we can rely on ao.Errors but to I was thinking to make both auction & amp_auction rely on common thing which seems to be ao.response in this case. Thoughts ?? |
@ashishshinde-pubm Thank you for your responses! We need to discuss something with the team and we will get back to you. |
@SyntaxNode , @VeronikaSolovei9 Any updates on this ? |
@ashishshinde-pubm sorry for the delay. There are a few other high priority items ahead of this but we will revisit soon. |
There are also some merge conflicts now. |
@bsardo was looking at the PR, I also have a use case to construct SeatNonBid from our hook, i was more thinking of directly modifying the we can then invoke the function in let me know how the discussion goes so that you guys figure out a agreed workflow for the scenario |
@zhongshixi As per the PRD (#2367) , the seatnonbid are not just related to bid response. |
@ashishshinde-pubm i see, thanks for the quick response, i read the PRD and i see the community's direction to include the bidder request as part of seat non bid. since this PR is only surfacing SeatNonBid in hookoutcome, i will post my thoughts on that PRD threads. |
@ashishshinde-pubm - I read through your PR and i like the design of using if you want, we can work on it together to make the puzzle complete so that seatNonBid can collect information on critical decision points in prebid server that rejects bid request and bid response |
@bsardo , @hhhjort , @VeronikaSolovei9 I've resolved the conflcts, Please have a look on this PR. |
…nto SNBR_HookStage
openrtb_ext/seat_non_bids.go
Outdated
type NonBidCollection struct { | ||
seatNonBidsMap map[string][]NonBid | ||
} |
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.
Can we get rid of NonBidCollection
and this entire file and use SeatNonBidBuilder
from #2891?
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.
Remove NonBidCollection
. But move SeatNonBidBuilder
to openrtb_ext
package from exchange
package due to cyclic import.
endpoints/openrtb2/auction.go
Outdated
// if AuctionObject.Response is nil then collect nonbids from all stage outcomes and set it in the AuctionObject. | ||
// Nil AuctionObject.Response indicates the occurrence of a fatal error. | ||
if ao.Response == nil { | ||
seatNonBid.Append(getNonBidsFromStageOutcomes(hookExecutor.GetOutcomes())) | ||
ao.SeatNonBid = seatNonBid.Get() | ||
} |
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.
While I understand why you chose to put this logic in defer
, I don't think we should get into the habit of putting such conditional and computational logic in here. Is there another place we can put this?
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 avoid adding this code at multiple places, added it in the defer block.
if response.Ext == nil { | ||
response.Ext = json.RawMessage(`{}`) | ||
} |
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 do we need to do this? Is this to avoid an error when unmarshaling?
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.
ao.HookExecutionOutcome = stageOutcomes | ||
err := setSeatNonBidRaw(ao.RequestWrapper, response, ao.SeatNonBid) |
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.
Perhaps this is necessary at the moment but it seems a little odd that we are using the request wrapper that is on the analytics object.
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.
RequestWrapper
is not being passed to sendAuctionResponse
currently. We will have to change sendAuctionResponse
definition to accept RequestWrapper
as argument instead of BidRequest
.
@bsardo Addressed the review comments. |
Hi @Pubmatic-Dhruv-Sonone, can you please merge with master again to resolve conflicts? Thanks! |
@bsardo Merged master. Please review again. |
endpoints/openrtb2/auction.go
Outdated
if setSeatNonBid(respExt, request, auctionResponse) { | ||
if respExtJson, err := jsonutil.Marshal(respExt); err == nil { | ||
if setSeatNonBid(respExt, nonBids) { | ||
if respExtJson, err := json.Marshal(respExt); 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.
Please leave the usage of our jsonutil helper.
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.
Done
endpoints/openrtb2/auction_test.go
Outdated
}, | ||
}, | ||
{ | ||
name: "returnallbistatus is true, update seatnonbid in nil responseExt", |
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.
typo in the name, should be "returnallbid
status is true..."
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.
Fixed.
@hhhjort Addressed the review comments. Please check. |
Currently, bids getting rejected in module stage are not captured in seatNonBid.
This PR introduce the SeatNonBid in HookStageOutcome so that each module stage can return rejected bids.
In auction/amp_auction, we will collect the SeatNonBid from all stageOutcomes (stageOutcomes whose execution-status is
success
)Changes in opertb_ext package -
Changes in exchange.go -
auctionresponsehook
gets executed after holdAuction function in the auction/amp-auction, hence auction will take of adding all seatnonbids in bidresponseExt.Changes in auction.go/amp_auction.go -
request.ext.prebid.returnallbidstatus
will decide and add the SeatNonBid in the bidResponseExt.ao.AuctionResponse
/ao.Response
would be nil hence we will collect SeatNonBid from stageOutcomes in the defer statement.Changes in auction.go/video_auction.go -
Changes for unit-test cases -