Skip to content

Commit

Permalink
Merge branch 'master' into noBid_bidder/development
Browse files Browse the repository at this point in the history
  • Loading branch information
Serhii Nahornyi committed Nov 18, 2020
2 parents 2dbbe2c + 63b5ecf commit 13d1555
Show file tree
Hide file tree
Showing 220 changed files with 2,314 additions and 2,234 deletions.
7 changes: 7 additions & 0 deletions docs/config-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ This parameter exists to allow to change the location of the directory Vert.x wi
- `vertx.http-server-instances` - how many http server instances should be created.
This parameter affects how many CPU cores will be utilized by the application. Rough assumption - one http server instance will keep 1 CPU core busy.
- `vertx.init-timeout-ms` - time to wait for asynchronous initialization steps completion before considering them stuck. When exceeded - exception is thrown and Prebid Server stops.
- `vertx.enable-per-client-endpoint-metrics` - enables HTTP client metrics per destination endpoint (`host:port`)

## HTTP
- `http.port` - the port to listen on.
Expand Down Expand Up @@ -82,6 +83,12 @@ Removes and downloads file again if depending service cant process probably corr
- `amp.timeout-adjustment-ms` - reduces timeout value passed in Amp request so that Prebid Server can handle timeouts from adapters and respond to the AMP RTC request before it times out.
- `amp.custom-targeting` - a list of bidders whose custom targeting should be included in AMP responses.

## Timeout notification
- `auction.timeout-notification.timeout-ms` - HTTP timeout to use when sending notifications about bidder timeouts
- `auction.timeout-notification.log-result` - causes bidder timeout notification result to be logged
- `auction.timeout-notification.log-failure-only` - causes only bidder timeout notification failures to be logged
- `auction.timeout-notification.log-sampling-rate` - instructs apply sampling when logging bidder timeout notification results

## Video
- `auction.video.stored-required` - flag forces to merge with stored request
- `auction.blacklisted-accounts` - comma separated list of blacklisted account IDs.
Expand Down
12 changes: 12 additions & 0 deletions docs/developers/add-new-bidder.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ Bidder implementations are scattered throughout several files:

Bidder implementations may assume that any params have already been validated against the defined json-schema.

### Timeout notification support
This is an optional feature. If you wish to get timeout notifications when a bid request from PBS times out, you can implement the
`org.prebid.server.bidder.Bidder.makeTimeoutNotification` method in your bidder implementation. If you do not wish
timeout notification, do not implement the method.

`HttpRequest<Void> makeTimeoutNotification(HttpRequest<T> httpRequest)`

Here the `HttpRequest` supplied as an argument is the request returned from `makeHttpRequests` that timed out. If a bidder generates
multiple requests, and more than one of them times out, then there will be a call to `makeTimeoutNotification` for each failed
request. The method should then return a `HttpRequest` object that will be the timeout notification to be sent to the bidder.
Timeout notifications will not generate subsequent timeout notifications if they time out or fail.

### Generic OpenRTB Bidder

There's an option to implement a bidder by using a pre-existing template.
Expand Down
28 changes: 27 additions & 1 deletion docs/metrics.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,37 @@
This document describes all metrics collected and submitted to configured backends by the Prebid Server.

## System metrics
Other available metrics not mentioned here can found at
[Vert.x Dropwizard Metrics](https://vertx.io/docs/vertx-dropwizard-metrics/java/#_the_metrics) page.

### HTTP server metrics
- `vertx.http.servers.[IP]:[PORT].open-netsockets.count` - current number of open connections

where:
- `[IP]` should be equal to IP address of bound network interface on cluster node for Prebid Server (for example: `0.0.0.0`)
- `[PORT]` should be equal to `http.port` configuration property

Other available metrics can found at [Vert.x Dropwizard Metrics](https://vertx.io/docs/vertx-dropwizard-metrics/java/#_the_metrics) page.
### HTTP client metrics
- `vertx.http.clients.connections.{min,max,mean,p95,p99}` - how long connections live
- `vertx.http.clients.connections.{m1_rate,m5_rate,m15_rate,mean_rate}` - rate of the connection occurrences
- `vertx.http.clients.requests.{min,max,mean,p95,p99}` - request time
- `vertx.http.clients.requests.{m1_rate,m5_rate,m15_rate,mean_rate}` - request rate

If HTTP client per destination endpoint metrics enabled:
- `vertx.http.clients.endpoint.[ENDPOINT]:[PORT].queue-delay.{min,max,mean,p95,p99}` - wait time of a pending request in the queue
- `vertx.http.clients.endpoint.[ENDPOINT]:[PORT].queue-size.count` - actual queue size
- `vertx.http.clients.endpoint.[ENDPOINT]:[PORT].open-netsockets.count` - actual number of open sockets to the endpoint
- `vertx.http.clients.endpoint.[ENDPOINT]:[PORT].usage.{min,max,mean,p95,p99}` - time of the delay between the request starts and the response ends
- `vertx.http.clients.endpoint.[ENDPOINT]:[PORT].in-use` - actual number of in-flight requests
- `vertx.http.clients.endpoint.[ENDPOINT]:[PORT].ttfb` - wait time between the request ended and its response begins

### Database pool metrics
- `vertx.pools.datasouce.[DATASOURCE].queue-delay.{min,max,mean,p95,p99}` - duration of the delay to obtain the resource, i.e the wait time in the queue
- `vertx.pools.datasouce.[DATASOURCE].queue-size.counter` - the actual number of waiters in the queue
- `vertx.pools.datasouce.[DATASOURCE].usage.{min,max,mean,p95,p99}` - duration of the usage of the resource
- `vertx.pools.datasouce.[DATASOURCE].in-use.counter` - actual number of resources used

where `[DATASOURCE]` is a data source name, `DEFAULT_DS` by defaul.

## General auction metrics
- `app_requests` - number of requests received from applications
Expand Down Expand Up @@ -37,6 +61,8 @@ Other available metrics can found at [Vert.x Dropwizard Metrics](https://vertx.i
- `circuit.breaker.http.existing` - number of http client circuit breakers existing currently for all hosts
- `circuit-breaker.db.opened` - state of the database circuit breaker: `1` means opened (database is unavailable), `0` - closed
- `circuit-breaker.geo.opened` - state of the geo location circuit breaker: `1` means opened (geo location resource is unavailable), `0` - closed
- `timeout_notification.ok` - number of times bidders were successfully notified about timeouts
- `timeout_notification.failed` - number of unsuccessful attempts to notify bidders about timeouts

## Auction per-adapter metrics
- `adapter.<bidder-name>.no_cookie_requests` - number of requests made to `<bidder-name>` that did not contain UID
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>org.prebid</groupId>
<artifactId>prebid-server</artifactId>
<version>1.49.0-SNAPSHOT</version>
<version>1.50.0-SNAPSHOT</version>

<name>prebid-server</name>
<description>Prebid Server (Server-side Header Bidding)</description>
Expand Down
16 changes: 14 additions & 2 deletions src/main/java/org/prebid/server/auction/AuctionRequestFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -351,13 +351,20 @@ private Device populateDevice(Device device, HttpServerRequest request) {
logWarnIfNoIp(resolvedIp, resolvedIpv6);

final String ua = device != null ? device.getUa() : null;
final Integer dnt = resolveDntHeader(request);

if (!Objects.equals(deviceIp, resolvedIp)
|| !Objects.equals(deviceIpv6, resolvedIpv6)
|| StringUtils.isBlank(ua)) {
|| StringUtils.isBlank(ua) || dnt != null) {

final Device.DeviceBuilder builder = device == null ? Device.builder() : device.toBuilder();
builder.ua(StringUtils.isNotBlank(ua) ? ua : paramsExtractor.uaFrom(request));

if (StringUtils.isBlank(ua)) {
builder.ua(paramsExtractor.uaFrom(request));
}
if (dnt != null) {
builder.dnt(dnt);
}

builder
.ip(resolvedIp)
Expand All @@ -369,6 +376,11 @@ private Device populateDevice(Device device, HttpServerRequest request) {
return null;
}

private Integer resolveDntHeader(HttpServerRequest request) {
final String dnt = request.getHeader(HttpUtil.DNT_HEADER.toString());
return StringUtils.equalsAny(dnt, "0", "1") ? Integer.valueOf(dnt) : null;
}

private String sanitizeIp(String ip, IpAddress.IP version) {
final IpAddress ipAddress = ip != null ? ipAddressHelper.toIpAddress(ip) : null;
return ipAddress != null && ipAddress.getVersion() == version ? ipAddress.getIp() : null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
import com.iab.openrtb.request.Device;
import com.iab.openrtb.request.Format;
import com.iab.openrtb.request.Imp;
import org.apache.commons.collections4.CollectionUtils;
import org.prebid.server.exception.InvalidRequestException;
import org.prebid.server.proto.openrtb.ext.request.ExtDevice;
import org.prebid.server.proto.openrtb.ext.request.ExtDeviceInt;
import org.prebid.server.proto.openrtb.ext.request.ExtDevicePrebid;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
Expand Down
17 changes: 16 additions & 1 deletion src/main/java/org/prebid/server/bidder/Bidder.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.prebid.server.bidder.model.HttpRequest;
import org.prebid.server.bidder.model.Result;

import java.util.Collections;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -34,5 +35,19 @@ public interface Bidder<T> {
/**
* Extracts targeting from bidder-specific extension. It is safe to assume that {@code ext} is not null.
*/
Map<String, String> extractTargeting(ObjectNode ext);
default Map<String, String> extractTargeting(ObjectNode ext) {
return Collections.emptyMap();
}

/**
* This method is much the same as {@link #makeHttpRequests}, except it is fed the bidder request
* that timed out, and expects that only one notification "request" will be generated. A use case for multiple
* timeout notifications has not been anticipated.
* <p>
* Do note that if {@link #makeHttpRequests} returns multiple requests, and more than one of these times out,
* this method will be called once for each timed out request.
*/
default HttpRequest<Void> makeTimeoutNotification(HttpRequest<T> httpRequest) {
return null;
}
}
93 changes: 93 additions & 0 deletions src/main/java/org/prebid/server/bidder/BidderErrorNotifier.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package org.prebid.server.bidder;

import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import org.prebid.server.bidder.model.BidderError;
import org.prebid.server.bidder.model.HttpCall;
import org.prebid.server.bidder.model.HttpRequest;
import org.prebid.server.log.ConditionalLogger;
import org.prebid.server.metric.Metrics;
import org.prebid.server.vertx.http.HttpClient;
import org.prebid.server.vertx.http.model.HttpClientResponse;

import java.util.Objects;

public class BidderErrorNotifier {

private static final Logger logger = LoggerFactory.getLogger(BidderErrorNotifier.class);
private static final ConditionalLogger conditionalLogger = new ConditionalLogger(logger);

private final int timeoutNotificationTimeoutMs;
private final boolean logTimeoutNotificationResult;
private final boolean logTimeoutNotificationFailureOnly;
private final double logTimeoutNotificationSamplingRate;
private final HttpClient httpClient;
private final Metrics metrics;

public BidderErrorNotifier(int timeoutNotificationTimeoutMs,
boolean logTimeoutNotificationResult,
boolean logTimeoutNotificationFailureOnly,
double logTimeoutNotificationSamplingRate,
HttpClient httpClient,
Metrics metrics) {

this.timeoutNotificationTimeoutMs = timeoutNotificationTimeoutMs;
this.logTimeoutNotificationResult = logTimeoutNotificationResult;
this.logTimeoutNotificationFailureOnly = logTimeoutNotificationFailureOnly;
this.logTimeoutNotificationSamplingRate = logTimeoutNotificationSamplingRate;
this.httpClient = Objects.requireNonNull(httpClient);
this.metrics = Objects.requireNonNull(metrics);
}

public <T> HttpCall<T> processTimeout(HttpCall<T> httpCall, Bidder<T> bidder) {
final BidderError error = httpCall.getError();

if (error != null && error.getType() == BidderError.Type.timeout) {
final HttpRequest<Void> timeoutNotification = bidder.makeTimeoutNotification(httpCall.getRequest());
if (timeoutNotification != null) {
httpClient.request(
timeoutNotification.getMethod(),
timeoutNotification.getUri(),
timeoutNotification.getHeaders(),
timeoutNotification.getBody(),
timeoutNotificationTimeoutMs)
.map(response -> handleTimeoutNotificationSuccess(response, timeoutNotification))
.otherwise(exception -> handleTimeoutNotificationFailure(exception, timeoutNotification));
}
}

return httpCall;
}

private Void handleTimeoutNotificationSuccess(HttpClientResponse response, HttpRequest<Void> timeoutNotification) {
final boolean isSuccessful = response.getStatusCode() >= 200 && response.getStatusCode() < 300;

metrics.updateTimeoutNotificationMetric(isSuccessful);

if (logTimeoutNotificationResult && !(logTimeoutNotificationFailureOnly && isSuccessful)) {
conditionalLogger.warn(
String.format(
"Notified bidder about timeout. Status code: %s. Request body: %s",
response.getStatusCode(),
timeoutNotification.getBody()),
logTimeoutNotificationSamplingRate);
}

return null;
}

private Void handleTimeoutNotificationFailure(Throwable exception, HttpRequest<Void> timeoutNotification) {
metrics.updateTimeoutNotificationMetric(false);

if (logTimeoutNotificationResult) {
conditionalLogger.warn(
String.format(
"Error occurred while notifying bidder about timeout. Error message: %s. Request body: %s",
exception.getMessage(),
timeoutNotification.getBody()),
logTimeoutNotificationSamplingRate);
}

return null;
}
}
3 changes: 1 addition & 2 deletions src/main/java/org/prebid/server/bidder/DisabledBidder.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import org.prebid.server.bidder.model.HttpRequest;
import org.prebid.server.bidder.model.Result;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand All @@ -26,7 +25,7 @@ public DisabledBidder(String errorMessage) {

@Override
public Result<List<HttpRequest<Void>>> makeHttpRequests(BidRequest request) {
return Result.of(Collections.emptyList(), Collections.singletonList(BidderError.badInput(errorMessage)));
return Result.withError(BidderError.badInput(errorMessage));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,16 +256,16 @@ private static <T, R> Result<List<Bid>> bidsWithError(Adapter<T, R> adapter, Ada
ExchangeCall<T, R> exchangeCall, Integer responseTime) {
final BidderError error = exchangeCall.getError();
if (error != null) {
return Result.emptyWithError(error);
return Result.withError(error);
}
try {
final List<Bid> bids = adapter.extractBids(adapterRequest, exchangeCall).stream()
.map(bidBuilder -> bidBuilder.responseTimeMs(responseTime))
.map(Bid.BidBuilder::build)
.collect(Collectors.toList());
return Result.of(bids, Collections.emptyList());
return Result.withValues(bids);
} catch (PreBidException e) {
return Result.emptyWithError(BidderError.badServerResponse(e.getMessage()));
return Result.withError(BidderError.badServerResponse(e.getMessage()));
}
}

Expand Down
Loading

0 comments on commit 13d1555

Please sign in to comment.