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 Greenbids Real Time Data Module #3242

Open
wants to merge 94 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
b4e06ef
Add Greenbids RTD Module
Jun 13, 2024
7e8e02a
fix module pom.xml
Jun 13, 2024
6f3f918
removed checkstyle for debug
Jun 13, 2024
96f053b
extract features to throttling msg
Jun 17, 2024
a0057e0
enable module
Jun 17, 2024
d5db65e
add onnx fetch predictor + inference
Jun 18, 2024
9567a48
check inference time
Jun 19, 2024
ad14957
update impExt.prebid.bidder from filtering
Jun 24, 2024
f096834
invocation result: update bid request
Jun 26, 2024
acece86
filtering: fix update imp ext
Jun 26, 2024
4a63528
transfer greenbidsId in ATag between RTD and analytics adapter
Jul 1, 2024
f08fde8
add exploration
Jul 11, 2024
99f3c18
add exploration
EvgeniiMunin Jul 12, 2024
d1d7933
add exploration ortb2imp
EvgeniiMunin Jul 12, 2024
09f5677
user agent features: browser, device, country
EvgeniiMunin Jul 17, 2024
a0cf84f
push artefacts
EvgeniiMunin Jul 29, 2024
7dd81fb
model per publisher + model cache
Jul 31, 2024
ff6f077
fetch gcs + model cache + auth partners
Aug 1, 2024
8860c7e
mutex reentrantlock
EvgeniiMunin Aug 2, 2024
1cccb91
fixes comments JB
Aug 8, 2024
317559f
UT RTD
Aug 19, 2024
a2586c7
refacto
Aug 20, 2024
351e090
refacto after rebase
Aug 20, 2024
1d25ad0
pbrichmedia remove changes
Aug 20, 2024
68bab22
refactos
Aug 21, 2024
de9cbbd
analytics delegator privacy action
Aug 21, 2024
6e521d5
remove checkstyle from rtd module pom.xml
Aug 21, 2024
896d492
modif prebid exception on geolite
Aug 21, 2024
6c16fb5
remove debug changes
Aug 22, 2024
1252ae5
refactos code style
Aug 22, 2024
92a1547
refacto codestyle
Aug 22, 2024
4a135eb
refacto: reorder methods in call order
Aug 22, 2024
c557ca3
fix review
Aug 26, 2024
cde2869
fix review1: pom 3.11.0
Aug 26, 2024
e1c6355
fix review1: cache remove lock + async
Aug 27, 2024
deb256e
fix review1: refacto hook call
Aug 28, 2024
d5ba490
fix review1: config + cache atomic bool flag
Aug 28, 2024
89ced51
fix review1: partner/ throttling thrs constructors
Aug 29, 2024
e3bed0c
fix review2: refactos
Aug 30, 2024
6d41e9a
fix review2: classes to beans + codestyle
Sep 2, 2024
1c78f6c
fix review2: vertx executeBlocking
Sep 3, 2024
7c9f3f2
fix review2: handle bucket null
Sep 3, 2024
061fc22
fix review3: chain vertx + refacto
Sep 5, 2024
d03e9a5
fix review3: refactos
Sep 6, 2024
25d751a
fix review3: refactos
Sep 6, 2024
430ef92
pbs 3.12
Sep 6, 2024
e26c46d
fix review4: refactos
EvgeniiMunin Sep 9, 2024
7e45ad4
fix review4: inferenceData to Bean
EvgeniiMunin Sep 10, 2024
1e887c6
fix review4: fmt
EvgeniiMunin Sep 10, 2024
bc79958
fix review4: modelcache thrcache as bean
EvgeniiMunin Sep 11, 2024
3b0e412
fix review4: fmt get methods
EvgeniiMunin Sep 11, 2024
24e0540
fix review4: checkstyle
EvgeniiMunin Sep 12, 2024
81ca6f9
fix review4: dbreader to bean
EvgeniiMunin Sep 14, 2024
7b4beb5
fix review4: autowired dbReader bean
EvgeniiMunin Sep 16, 2024
ba5b70a
fix review4: remove service annotation on greenbidsInfData
EvgeniiMunin Sep 16, 2024
20348cb
fix review5: refactos
EvgeniiMunin Sep 17, 2024
fccc532
fix review5: refactos fields non null check
EvgeniiMunin Sep 17, 2024
ffe15ea
fix review5: GreenbidsInvocationService + to bean
EvgeniiMunin Sep 17, 2024
c941a5b
fix review6: ObjectMapperProvider
EvgeniiMunin Sep 20, 2024
309710c
fix review6: refactos
EvgeniiMunin Sep 20, 2024
ed438af
fix review6: private final fields in services
EvgeniiMunin Sep 23, 2024
b905854
fix review6: refactos
EvgeniiMunin Sep 23, 2024
b374d45
fix review6: refactos
EvgeniiMunin Sep 23, 2024
c479c2d
fix review6: mapper dbReader field access
EvgeniiMunin Sep 23, 2024
77fcb1d
fix review6: validate OnnxTensor ThrMsgs
EvgeniiMunin Sep 24, 2024
83d22e1
fix review6: pom 3.13.0
EvgeniiMunin Sep 24, 2024
00add62
fix review6: remove checkstyle from module pom
EvgeniiMunin Sep 24, 2024
5aed5d9
fix review7: refactos
EvgeniiMunin Sep 25, 2024
b3e3ab0
fix review7: refactos analytics
EvgeniiMunin Sep 25, 2024
6d3d478
fix review7: refactor extractAnalyticsResultFromAnalyticsTag
EvgeniiMunin Sep 25, 2024
165f5df
fix review7: add new unit tests
EvgeniiMunin Oct 3, 2024
7c95464
fix review8: refactos
EvgeniiMunin Oct 3, 2024
50575fc
fix review8: ExtendWith annotation UT
EvgeniiMunin Oct 3, 2024
d3b747b
fix review8: refacto methods to TestBidRequestProvider
EvgeniiMunin Oct 3, 2024
291fcf2
fix review8: refactos
EvgeniiMunin Oct 3, 2024
a49c138
fix review9: refactos
EvgeniiMunin Oct 7, 2024
dac53fa
fix review9: refactos
EvgeniiMunin Oct 7, 2024
5e1db79
fix review9: ModelCacheTest debug
EvgeniiMunin Oct 8, 2024
626894b
fix review9: refactos
EvgeniiMunin Oct 8, 2024
4e27cdd
fix review9: add test on validateOnnxTensor
EvgeniiMunin Oct 8, 2024
e72e649
fix review9: fix UT
EvgeniiMunin Oct 9, 2024
e5b9e97
fix review9: fix/ debug ModelCacheTest
EvgeniiMunin Oct 9, 2024
48326a4
fix review9: Model/ThresholdCacheTest debug
EvgeniiMunin Oct 14, 2024
28f0637
fix review9: OnnxModelRunnerTest
EvgeniiMunin Oct 14, 2024
c808797
fix review9: fmt ThresholdCacheTest
EvgeniiMunin Oct 15, 2024
d29b1f8
fix review9: rebase pom 3.14.0
EvgeniiMunin Oct 15, 2024
7b2dd02
fix review10: TU lenient storage bucket blob factory
EvgeniiMunin Oct 15, 2024
c47055c
fix review10: refactos
EvgeniiMunin Oct 15, 2024
b23caac
fix review10: move tests
EvgeniiMunin Oct 15, 2024
51ed516
fix review10: refacto mapper
EvgeniiMunin Oct 15, 2024
d677ea1
fix review10: refacto mapper TestBidRequestProvider
EvgeniiMunin Oct 16, 2024
9432813
fix review10: rearrange classes
EvgeniiMunin Oct 16, 2024
37e55b0
fix review10: fix test device w/o user agent
EvgeniiMunin Oct 16, 2024
6cbd929
fix review10: move classes packaging
EvgeniiMunin Oct 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions extra/bundle/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@
<artifactId>pb-response-correction</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.prebid.server.hooks.modules</groupId>
<artifactId>greenbids-real-time-data</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>

<build>
Expand Down
37 changes: 37 additions & 0 deletions extra/modules/greenbids-real-time-data/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.prebid.server.hooks.modules</groupId>
<artifactId>all-modules</artifactId>
<version>3.14.0-SNAPSHOT</version>
</parent>

<artifactId>greenbids-real-time-data</artifactId>

<name>greenbids-real-time-data</name>
<description>Greenbids Real Time Data</description>

<dependencies>
<dependency>
<groupId>com.github.ua-parser</groupId>
<artifactId>uap-java</artifactId>
<version>1.6.1</version>
</dependency>

<dependency>
<groupId>com.microsoft.onnxruntime</groupId>
<artifactId>onnxruntime</artifactId>
<version>1.16.1</version> <!-- Use the latest available version -->
</dependency>

<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-storage</artifactId>
<version>2.41.0</version>
</dependency>
</dependencies>

</project>
1 change: 1 addition & 0 deletions extra/modules/greenbids-real-time-data/src/lombok.config
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lombok.anyConstructor.addConstructorProperties = true
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package org.prebid.server.hooks.modules.greenbids.real.time.data.config;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.google.cloud.storage.Storage;
import com.google.cloud.storage.StorageOptions;
import com.maxmind.geoip2.DatabaseReader;
import io.vertx.core.Vertx;
import org.prebid.server.exception.PreBidException;
import org.prebid.server.hooks.modules.greenbids.real.time.data.model.filter.ThrottlingThresholds;
import org.prebid.server.hooks.modules.greenbids.real.time.data.core.ThrottlingThresholdsFactory;
import org.prebid.server.hooks.modules.greenbids.real.time.data.core.GreenbidsInferenceDataService;
import org.prebid.server.hooks.modules.greenbids.real.time.data.core.FilterService;
import org.prebid.server.hooks.modules.greenbids.real.time.data.core.ModelCache;
import org.prebid.server.hooks.modules.greenbids.real.time.data.core.OnnxModelRunner;
import org.prebid.server.hooks.modules.greenbids.real.time.data.core.OnnxModelRunnerFactory;
import org.prebid.server.hooks.modules.greenbids.real.time.data.core.OnnxModelRunnerWithThresholds;
import org.prebid.server.hooks.modules.greenbids.real.time.data.core.ThresholdCache;
import org.prebid.server.hooks.modules.greenbids.real.time.data.core.GreenbidsInvocationService;
import org.prebid.server.hooks.modules.greenbids.real.time.data.v1.GreenbidsRealTimeDataProcessedAuctionRequestHook;
import org.prebid.server.json.ObjectMapperProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;

@ConditionalOnProperty(prefix = "hooks." + GreenbidsRealTimeDataModule.CODE, name = "enabled", havingValue = "true")
@Configuration
@EnableConfigurationProperties(GreenbidsRealTimeDataProperties.class)
public class GreenbidsRealTimeDataConfiguration {

@Bean
DatabaseReader dbReader(GreenbidsRealTimeDataProperties properties) {
final File database = new File(properties.getGeoLiteCountryPath());
try {
return new DatabaseReader.Builder(database).build();
} catch (IOException e) {
throw new PreBidException("Failed build DatabaseReader from DB", e);
}
}

@Bean
GreenbidsInferenceDataService greenbidsInferenceDataService(DatabaseReader dbReader) {
return new GreenbidsInferenceDataService(dbReader, ObjectMapperProvider.mapper());
}

@Bean
GreenbidsRealTimeDataModule greenbidsRealTimeDataModule(
FilterService filterService,
OnnxModelRunnerWithThresholds onnxModelRunnerWithThresholds,
GreenbidsInferenceDataService greenbidsInferenceDataService,
GreenbidsInvocationService greenbidsInvocationService) {

return new GreenbidsRealTimeDataModule(List.of(
new GreenbidsRealTimeDataProcessedAuctionRequestHook(
ObjectMapperProvider.mapper(),
filterService,
onnxModelRunnerWithThresholds,
greenbidsInferenceDataService,
greenbidsInvocationService)));
}

@Bean
FilterService filterService() {
return new FilterService();
}

@Bean
Storage storage(GreenbidsRealTimeDataProperties properties) {
return StorageOptions.newBuilder()
.setProjectId(properties.getGoogleCloudGreenbidsProject()).build().getService();
}

@Bean
OnnxModelRunnerFactory onnxModelRunnerFactory() {
return new OnnxModelRunnerFactory();
}

@Bean
ThrottlingThresholdsFactory throttlingThresholdsFactory() {
return new ThrottlingThresholdsFactory();
}

@Bean
ModelCache modelCache(
GreenbidsRealTimeDataProperties properties,
Vertx vertx,
Storage storage,
OnnxModelRunnerFactory onnxModelRunnerFactory) {

final Cache<String, OnnxModelRunner> modelCacheWithExpiration = Caffeine.newBuilder()
.expireAfterWrite(properties.getCacheExpirationMinutes(), TimeUnit.MINUTES)
.build();

return new ModelCache(
storage,
properties.getGcsBucketName(),
modelCacheWithExpiration,
properties.getOnnxModelCacheKeyPrefix(),
vertx,
onnxModelRunnerFactory);
}

@Bean
ThresholdCache thresholdCache(
GreenbidsRealTimeDataProperties properties,
Vertx vertx,
Storage storage,
ThrottlingThresholdsFactory throttlingThresholdsFactory) {

final Cache<String, ThrottlingThresholds> thresholdsCacheWithExpiration = Caffeine.newBuilder()
.expireAfterWrite(properties.getCacheExpirationMinutes(), TimeUnit.MINUTES)
.build();

return new ThresholdCache(
storage,
properties.getGcsBucketName(),
ObjectMapperProvider.mapper(),
thresholdsCacheWithExpiration,
properties.getThresholdsCacheKeyPrefix(),
vertx,
throttlingThresholdsFactory);
}

@Bean
OnnxModelRunnerWithThresholds onnxModelRunnerWithThresholds(
ModelCache modelCache,
ThresholdCache thresholdCache) {

return new OnnxModelRunnerWithThresholds(modelCache, thresholdCache);
}

@Bean
GreenbidsInvocationService greenbidsInvocationService() {
return new GreenbidsInvocationService();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.prebid.server.hooks.modules.greenbids.real.time.data.config;

import org.prebid.server.hooks.v1.Hook;
import org.prebid.server.hooks.v1.InvocationContext;
import org.prebid.server.hooks.v1.Module;

import java.util.Collection;
import java.util.List;

public class GreenbidsRealTimeDataModule implements Module {

public static final String CODE = "greenbids-real-time-data";

private final List<? extends Hook<?, ? extends InvocationContext>> hooks;

public GreenbidsRealTimeDataModule(List<? extends Hook<?, ? extends InvocationContext>> hooks) {
this.hooks = hooks;
}

@Override
public String code() {
return CODE;
}

@Override
public Collection<? extends Hook<?, ? extends InvocationContext>> hooks() {
return hooks;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.prebid.server.hooks.modules.greenbids.real.time.data.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "hooks.modules." + GreenbidsRealTimeDataModule.CODE)
@Data
public class GreenbidsRealTimeDataProperties {

String googleCloudGreenbidsProject;

String geoLiteCountryPath;

String gcsBucketName;

Integer cacheExpirationMinutes;

String onnxModelCacheKeyPrefix;

String thresholdsCacheKeyPrefix;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package org.prebid.server.hooks.modules.greenbids.real.time.data.core;

import ai.onnxruntime.OnnxTensor;
import ai.onnxruntime.OnnxValue;
import ai.onnxruntime.OrtException;
import ai.onnxruntime.OrtSession;
import org.prebid.server.exception.PreBidException;
import org.prebid.server.hooks.modules.greenbids.real.time.data.model.data.ThrottlingMessage;
import org.springframework.util.CollectionUtils;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

public class FilterService {

public Map<String, Map<String, Boolean>> filterBidders(
OnnxModelRunner onnxModelRunner,
List<ThrottlingMessage> throttlingMessages,
Double threshold) {

final OrtSession.Result results;
try {
final String[][] throttlingInferenceRows = convertToArray(throttlingMessages);
results = onnxModelRunner.runModel(throttlingInferenceRows);
return processModelResults(results, throttlingMessages, threshold);
} catch (OrtException e) {
throw new PreBidException("Exception during model inference: ", e);
}
}

private static String[][] convertToArray(List<ThrottlingMessage> messages) {
return messages.stream()
.map(message -> new String[]{
message.getBrowser(),
message.getBidder(),
message.getAdUnitCode(),
message.getCountry(),
message.getHostname(),
message.getDevice(),
message.getHourBucket(),
message.getMinuteQuadrant()})
.toArray(String[][]::new);
}

private Map<String, Map<String, Boolean>> processModelResults(
OrtSession.Result results,
List<ThrottlingMessage> throttlingMessages,
Double threshold) {

validateThrottlingMessages(throttlingMessages);

return StreamSupport.stream(results.spliterator(), false)
.peek(FilterService::validateOnnxTensor)
.filter(onnxItem -> Objects.equals(onnxItem.getKey(), "probabilities"))
.map(Map.Entry::getValue)
.map(OnnxTensor.class::cast)
.peek(tensor -> validateTensorSize(tensor, throttlingMessages.size()))
.map(tensor -> extractAndProcessProbabilities(tensor, throttlingMessages, threshold))
.map(Map::entrySet)
.flatMap(Collection::stream)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

private static void validateThrottlingMessages(List<ThrottlingMessage> throttlingMessages) {
if (throttlingMessages == null || CollectionUtils.isEmpty(throttlingMessages)) {
throw new PreBidException("throttlingMessages cannot be null or empty");
}
}

private static void validateOnnxTensor(Map.Entry<String, OnnxValue> onnxItem) {
if (!(onnxItem.getValue() instanceof OnnxTensor)) {
throw new PreBidException("Expected OnnxTensor for 'probabilities', but found: "
+ onnxItem.getValue().getClass().getName());
}
}

private static void validateTensorSize(OnnxTensor tensor, int expectedSize) {
final long[] tensorShape = tensor.getInfo().getShape();
if (tensorShape.length == 0 || tensorShape[0] != expectedSize) {
throw new PreBidException("Mismatch between tensor size and throttlingMessages size");
}
}

private Map<String, Map<String, Boolean>> extractAndProcessProbabilities(
OnnxTensor tensor,
List<ThrottlingMessage> throttlingMessages,
Double threshold) {

try {
final float[][] probabilities = extractProbabilitiesValues(tensor);
return processProbabilities(probabilities, throttlingMessages, threshold);
} catch (OrtException e) {
throw new PreBidException("Exception when extracting proba from OnnxTensor: ", e);
}
}

private float[][] extractProbabilitiesValues(OnnxTensor tensor) throws OrtException {
return (float[][]) tensor.getValue();
}

private Map<String, Map<String, Boolean>> processProbabilities(
float[][] probabilities,
List<ThrottlingMessage> throttlingMessages,
Double threshold) {

final Map<String, Map<String, Boolean>> result = new HashMap<>();

for (int i = 0; i < probabilities.length; i++) {
final ThrottlingMessage message = throttlingMessages.get(i);
final String impId = message.getAdUnitCode();
final String bidder = message.getBidder();
final boolean isKeptInAuction = probabilities[i][1] > threshold;
result.computeIfAbsent(impId, k -> new HashMap<>()).put(bidder, isKeptInAuction);
}

return result;
}
}
Loading
Loading