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

Add BidderResponseDurationMin check #2848

Merged
merged 10 commits into from
Jul 31, 2023

Conversation

onkarvhanumante
Copy link
Contributor

PR makes changes to add Tmax.BidderResponseDurationMin check in auction workflow. As per this change, PBS won't send request to bidder server if remaining duration less than BidderResponseDurationMin

- As of now, `bidderRequestStartTime` is part of `ExtraRequestInfo`. The ExtraRequestInfo is further passed as details to Adapter's MakeRequest() implementation.

- We don't want to pass bidderRequestStartTime details to `MakeRequest()`. So remove bidderRequestStartTime from ExtraRequestInfo and add it in bidRequestOptions.
onkarvhanumante added a commit that referenced this pull request Jun 21, 2023
Copy link
Contributor

@gargcreation1992 gargcreation1992 left a comment

Choose a reason for hiding this comment

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

lgtm!

@bsardo bsardo added the blocked label Jun 28, 2023
@onkarvhanumante onkarvhanumante changed the base branch from tmax/update-bidder-tmax to master July 25, 2023 06:03
@onkarvhanumante onkarvhanumante dismissed stale reviews from gargcreation1992 and Sonali-More-Xandr July 25, 2023 06:03

The base branch was changed.

@onkarvhanumante onkarvhanumante force-pushed the tmax/add-bidder-resp-min-check branch from ec51657 to 1b6ebea Compare July 25, 2023 07:14
@onkarvhanumante
Copy link
Contributor Author

onkarvhanumante commented Jul 25, 2023

@@ -190,11 +193,11 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, bidderRequest Bidde
dataLen = len(reqData) + len(bidderRequest.BidderStoredResponses)
responseChannel = make(chan *httpCallInfo, dataLen)
if len(reqData) == 1 {
responseChannel <- bidder.doRequest(ctx, reqData[0], reqInfo.BidderRequestStartTime)
responseChannel <- bidder.doRequest(ctx, reqData[0], bidRequestOptions.bidderRequestStartTime, bidRequestOptions.tmaxAdjustments)
} else {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we combine two arguments and pass one argument bidRequestOptions instead?

Copy link
Contributor Author

@onkarvhanumante onkarvhanumante Jul 26, 2023

Choose a reason for hiding this comment

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

type bidRequestOptions struct {
accountDebugAllowed bool
headerDebugAllowed bool
addCallSignHeader bool
bidAdjustments map[string]float64
tmaxAdjustments *TmaxAdjustmentsPreprocessed
bidderRequestStartTime time.Time
}

bidRequestOptions holds different values which are not needed by doRequest. IMO better to pass only values those are needed

Copy link
Contributor

@gargcreation1992 gargcreation1992 left a comment

Choose a reason for hiding this comment

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

requesting one minor change. Rest lgtm!

@@ -46,6 +47,9 @@ func (b *bidderTmaxCtx) Deadline() (deadline time.Time, ok bool) {
deadline, ok = b.ctx.Deadline()
return
}
func (b *bidderTmaxCtx) Until(t time.Time) time.Duration {
return time.Until(t)
}

func getBidderTmax(ctx bidderTmaxContext, requestTmaxMS int64, tmaxAdjustments TmaxAdjustmentsPreprocessed) int64 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we write a short description of the logic behing the calculations this function performs?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

bd1c910 adds description

@@ -46,6 +47,9 @@ func (b *bidderTmaxCtx) Deadline() (deadline time.Time, ok bool) {
deadline, ok = b.ctx.Deadline()
return
}
func (b *bidderTmaxCtx) Until(t time.Time) time.Duration {
return time.Until(t)
Copy link
Contributor

Choose a reason for hiding this comment

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

Nitpick: Are we planning to add more functionality into Until in the future? If not, given that it doesn't access nor works with bidderTmaxCtx, maybe we can get rid of it and simply use the time library function. "time" is already listed in the imports of exchange/bidder.go so we wouldn't need the extra import either.

741   func hasShorterDurationThanTmax(ctx bidderTmaxContext, tmaxAdjustments TmaxAdjustmentsPreprocessed) bool {
742       if tmaxAdjustments.IsEnforced {
743           if deadline, ok := ctx.Deadline(); ok {
744               overheadNS := time.Duration(tmaxAdjustments.BidderNetworkLatencyBuffer+tmaxAdjustments.PBSResponsePreparationDuration) * time.Millisecond
745               bidderTmax := deadline.Add(-overheadNS)
746  
747 -             remainingDuration := ctx.Until(bidderTmax).Milliseconds()
    +             remainingDuration := time.Until(bidderTmax).Milliseconds()
748               return remainingDuration < int64(tmaxAdjustments.BidderResponseDurationMin)
749           }
750       }
751       return false
752   }
exchange/bidder.go

This could also be the case of RemainingDurationMS. Unless this is for testing purposes and I'm missing the big picture.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

time.Until() internally calls time.Now(). Its difficult to test code path when value returned from time.Now() is used. By using ctx.Until() we can mock this return value.

Refer TestHasShorterDurationThanTmax unit test.

ctx := &mockBidderTmaxCtx{startTime: startTime, deadline: deadline, now: now, ok: true}

In the test, mockBidderTmaxCtx is passed as implementer for biddetTmaxCtx. So during unit test mockBidderTmaxCtx.Until() is called.

func (m *mockBidderTmaxCtx) Until(t time.Time) time.Duration {
return t.Sub(m.now)
}

By using ctx.Until() with mockBidderTmaxCtx, we can easily control the return value for testing purposes. This approach enables us to test specific code paths that were previously difficult time.Until().

Copy link
Contributor

Choose a reason for hiding this comment

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

How about avoiding time.Now() entirely by storing the openrtb2.BidRequest.TMax field in the tmaxAdjustments parameter and using it for the calculation. I believe both calculations and test cases could be simplified:

741 - func hasShorterDurationThanTmax(ctx bidderTmaxContext, tmaxAdjustments TmaxAdjustmentsPreprocessed) bool {
    + // hasShorterDurationThanTmax returns true if there's enough tmax to make the roundtrip to the bid server 
    + // doing a calculation that involves the original bid request tmax
    + func hasShorterDurationThanTmax(tmaxAdjustments TmaxAdjustmentsPreprocessed) bool {
742       if tmaxAdjustments.IsEnforced {
743 -         if deadline, ok := ctx.Deadline(); ok {
744               overheadNS := time.Duration(tmaxAdjustments.BidderNetworkLatencyBuffer+tmaxAdjustments.PBSResponsePreparationDuration) * time.Millisecond
745 -             bidderTmax := deadline.Add(-overheadNS)
746
747 -             remainingDuration := ctx.Until(bidderTmax).Milliseconds()
    +             remainingDuration := tmaxAdjustments.BidRequestTmax - overheadNS
748               return remainingDuration < int64(tmaxAdjustments.BidderResponseDurationMin)
749 -         }
750       }
751       return false
752   }
exchange/bidder.go

We would have to copy the openrtb2.BidRequest.TMax into tmaxAdjustments.BidRequestTmax and recalculate it's value inside func (e *exchange) makeAuctionContext(ctx context.Context, needsCache bool) (exchange/exchange.go line 646) if necessary.

Do you think we can pull this off?

Copy link
Contributor Author

@onkarvhanumante onkarvhanumante Jul 27, 2023

Choose a reason for hiding this comment

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

We need to get the elapsed time since start of auction handler function.
For this we are using, deadline context intialised at the start. Refer following:

timeout := deps.cfg.AuctionTimeouts.LimitAuctionTimeout(time.Duration(req.TMax) * time.Millisecond)
if timeout > 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithDeadline(ctx, start.Add(timeout))
defer cancel()
}

This elapsed time from deadline context is fetched using ctx.Deadline() and then BidderNetworkLatencyBuffer, PBSResponsePreparationDuration is deducted from resultant deadline to get bidderTmax.

I am not sure how using openrtb2.BidRequest.TMax we can get elapsed time

@@ -47,6 +47,8 @@ func (b *bidderTmaxCtx) Deadline() (deadline time.Time, ok bool) {
deadline, ok = b.ctx.Deadline()
return
}

// returns the remaining duration until the specified time
Copy link
Contributor

Choose a reason for hiding this comment

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

Super nitpick: Can we use the usual Golang convention of prefixing the comment with the name of the function?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@@ -46,6 +47,9 @@ func (b *bidderTmaxCtx) Deadline() (deadline time.Time, ok bool) {
deadline, ok = b.ctx.Deadline()
return
}
func (b *bidderTmaxCtx) Until(t time.Time) time.Duration {
return time.Until(t)
Copy link
Contributor

Choose a reason for hiding this comment

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

How about avoiding time.Now() entirely by storing the openrtb2.BidRequest.TMax field in the tmaxAdjustments parameter and using it for the calculation. I believe both calculations and test cases could be simplified:

741 - func hasShorterDurationThanTmax(ctx bidderTmaxContext, tmaxAdjustments TmaxAdjustmentsPreprocessed) bool {
    + // hasShorterDurationThanTmax returns true if there's enough tmax to make the roundtrip to the bid server 
    + // doing a calculation that involves the original bid request tmax
    + func hasShorterDurationThanTmax(tmaxAdjustments TmaxAdjustmentsPreprocessed) bool {
742       if tmaxAdjustments.IsEnforced {
743 -         if deadline, ok := ctx.Deadline(); ok {
744               overheadNS := time.Duration(tmaxAdjustments.BidderNetworkLatencyBuffer+tmaxAdjustments.PBSResponsePreparationDuration) * time.Millisecond
745 -             bidderTmax := deadline.Add(-overheadNS)
746
747 -             remainingDuration := ctx.Until(bidderTmax).Milliseconds()
    +             remainingDuration := tmaxAdjustments.BidRequestTmax - overheadNS
748               return remainingDuration < int64(tmaxAdjustments.BidderResponseDurationMin)
749 -         }
750       }
751       return false
752   }
exchange/bidder.go

We would have to copy the openrtb2.BidRequest.TMax into tmaxAdjustments.BidRequestTmax and recalculate it's value inside func (e *exchange) makeAuctionContext(ctx context.Context, needsCache bool) (exchange/exchange.go line 646) if necessary.

Do you think we can pull this off?

@onkarvhanumante onkarvhanumante merged commit 8ffa1c6 into master Jul 31, 2023
@onkarvhanumante onkarvhanumante deleted the tmax/add-bidder-resp-min-check branch July 31, 2023 11:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants