Machine Coding Problem

Ad-Serving Engine

maco60macoAlladsbid-selectionauction-logic
Commonly Asked By:GoogleTradeDeskMeta

Functional Scope (In-Scope)

  • Targeted Ad Matching: Filter eligible ad campaigns based on user context details (geo, device type, audience segments) using the Specification Pattern.
  • Second-Price Vickrey Auctions: Run real-time auctions where the winning advertiser pays the second-highest bid plus a $0.01 increment.
  • Atomic Budget Deductions: Manage campaign budgets in micro-cents to handle high-concurrency deductions accurately without rounding errors.
  • Frequency Capping Guards: Limit identical ad displays to a maximum of N impressions per user per day.

Explicit Boundaries (Out-of-Scope)

  • No Complex RTB OpenRTB Bid Protocols: Bypasses live external DSP/SSP bidding round-trips over open web networks.
  • No ML-Based Click-Through Rate (CTR) Predictors: Excludes predicting click probabilities using neural networks.

Clean reference designs demonstrating second-price auctions in Java and Python:

// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.*;

enum DeviceType { MOBILE, DESKTOP, TABLET }

class UserContext {
    private final String userId;
    private final String geo;
    private final DeviceType deviceType;
    private final Set<String> interests = new HashSet<>();

    public UserContext(String userId, String geo, DeviceType deviceType, List<String> interests) {
        this.userId = userId;
        this.geo = geo;
        this.deviceType = deviceType;
        this.interests.addAll(interests);
    }

    public String getUserId() { return userId; }
    public String getGeo() { return geo; }
    public DeviceType getDeviceType() { return deviceType; }
    public Set<String> getInterests() { return interests; }
}

// Specification Pattern for Targeting Filter
interface TargetingSpecification {
    boolean isSatisfiedBy(UserContext context);
}

class GeoTargetingSpecification implements TargetingSpecification {
    private final Set<String> allowedGeos;
    public GeoTargetingSpecification(String... geos) {
        this.allowedGeos = new HashSet<>(Arrays.asList(geos));
    }
    @Override
    public boolean isSatisfiedBy(UserContext context) {
        return allowedGeos.isEmpty() || allowedGeos.contains(context.getGeo());
    }
}

class DeviceTargetingSpecification implements TargetingSpecification {
    private final Set<DeviceType> allowedDevices;
    public DeviceTargetingSpecification(DeviceType... devices) {
        this.allowedDevices = new HashSet<>(Arrays.asList(devices));
    }
    @Override
    public boolean isSatisfiedBy(UserContext context) {
        return allowedDevices.isEmpty() || allowedDevices.contains(context.getDeviceType());
    }
}

class AdCampaign {
    private final String id;
    private final double bidAmount; // in dollars
    private final AtomicLong budgetMicroCents;
    private final List<TargetingSpecification> targetSpecs = new ArrayList<>();
    private final String creativeUrl;

    public AdCampaign(String id, double bidAmount, double budget, String creativeUrl) {
        this.id = id;
        this.bidAmount = bidAmount;
        this.budgetMicroCents = new AtomicLong((long)(budget * 1_000_000.0));
        this.creativeUrl = creativeUrl;
    }

    public String getId() { return id; }
    public double getBidAmount() { return bidAmount; }
    public String getCreativeUrl() { return creativeUrl; }

    public void addSpecification(TargetingSpecification spec) {
        targetSpecs.add(spec);
    }

    public boolean matchesTarget(UserContext context) {
        for (TargetingSpecification spec : targetSpecs) {
            if (!spec.isSatisfiedBy(context)) return false;
        }
        return true;
    }

    public boolean tryDeductBudget(double cost) {
        long microCost = (long)(cost * 1_000_000.0);
        while (true) {
            long current = budgetMicroCents.get();
            if (current < microCost) return false;
            if (budgetMicroCents.compareAndSet(current, current - microCost)) {
                return true;
            }
        }
    }

    public double getRemainingBudget() {
        return budgetMicroCents.get() / 1_000_000.0;
    }
}

class FrequencyCapper {
    // Map of UserID -> (CampaignID -> ImpressionCount)
    private final Map<String, Map<String, Integer>> userImpressions = new ConcurrentHashMap<>();

    public boolean isCapReached(String userId, String campaignId, int maxImpressions) {
        Map<String, Integer> campaignCounts = userImpressions.get(userId);
        if (campaignCounts == null) return false;
        return campaignCounts.getOrDefault(campaignId, 0) >= maxImpressions;
    }

    public void recordImpression(String userId, String campaignId) {
        userImpressions.computeIfAbsent(userId, k -> new ConcurrentHashMap<>())
                       .merge(campaignId, 1, Integer::sum);
    }
}

// Strategy Pattern for Auction type
interface AuctionStrategy {
    AuctionResult runAuction(List<AdCampaign> campaigns);
}

class AuctionResult {
    private final AdCampaign winner;
    private final double cost;

    public AuctionResult(AdCampaign winner, double cost) {
        this.winner = winner;
        this.cost = cost;
    }

    public AdCampaign getWinner() { return winner; }
    public double getCost() { return cost; }
}

class SecondPriceVickreyAuction implements AuctionStrategy {
    @Override
    public AuctionResult runAuction(List<AdCampaign> campaigns) {
        if (campaigns.isEmpty()) return null;
        if (campaigns.size() == 1) {
            // Pay a reserve floor price of $0.01 or custom value
            return new AuctionResult(campaigns.get(0), 0.01);
        }

        // Sort campaign list descending by bid amount
        campaigns.sort((a, b) -> Double.compare(b.getBidAmount(), a.getBidAmount()));
        AdCampaign winner = campaigns.get(0);
        double secondBid = campaigns.get(1).getBidAmount();
        double finalCost = secondBid + 0.01; // Vickrey model: Second price + 1 penny

        return new AuctionResult(winner, finalCost);
    }
}

class AdServingEngine {
    private final List<AdCampaign> campaigns = new CopyOnWriteArrayList<>();
    private final FrequencyCapper frequencyCapper = new FrequencyCapper();
    private final AuctionStrategy auctionStrategy;
    private final int dailyCapLimit;

    public AdServingEngine(AuctionStrategy auctionStrategy, int dailyCapLimit) {
        this.auctionStrategy = auctionStrategy;
        this.dailyCapLimit = dailyCapLimit;
    }

    public void registerCampaign(AdCampaign campaign) {
        campaigns.add(campaign);
    }

    public String serveAd(UserContext context) {
        List<AdCampaign> eligibleCampaigns = new ArrayList<>();
        
        for (AdCampaign campaign : campaigns) {
            // 1. Evaluate targeting specifications
            if (!campaign.matchesTarget(context)) continue;

            // 2. Enforce frequency capping guard
            if (frequencyCapper.isCapReached(context.getUserId(), campaign.getId(), dailyCapLimit)) {
                System.out.println("Frequency limit reached for Campaign " + campaign.getId() + " and User " + context.getUserId());
                continue;
            }

            // 3. Verify budget exists
            if (campaign.getRemainingBudget() <= 0.0) continue;

            eligibleCampaigns.add(campaign);
        }

        if (eligibleCampaigns.isEmpty()) {
            return "creative_fallback.jpg"; // Default fallback creative
        }

        // 4. Run second-price Vickrey auction
        AuctionResult result = auctionStrategy.runAuction(eligibleCampaigns);
        if (result == null) return "creative_fallback.jpg";

        AdCampaign winner = result.getWinner();
        double cost = result.getCost();

        // 5. Try atomic budget deduction
        if (winner.tryDeductBudget(cost)) {
            frequencyCapper.recordImpression(context.getUserId(), winner.getId());
            System.out.println("Served Ad: " + winner.getId() + " to " + context.getUserId() 
                               + ". Winner bid: $" + winner.getBidAmount() + ". Final Price Paid: $" + String.format("%.2f", cost));
            return winner.getCreativeUrl();
        } else {
            // Recurse / fallback if winner ran out of budget in a race condition
            System.out.println("Winner " + winner.getId() + " failed budget check due to concurrent checkout. Retrying...");
            return serveAd(context);
        }
    }
}

// Complete Simulation
public class Main {
    public static void main(String[] args) {
        AdServingEngine engine = new AdServingEngine(new SecondPriceVickreyAuction(), 2);

        // Define campaigns with different bid sizes
        AdCampaign c1 = new AdCampaign("C-NIKE", 2.50, 10.00, "nike_shoes.jpg");
        c1.addSpecification(new GeoTargetingSpecification("US", "CA"));
        c1.addSpecification(new DeviceTargetingSpecification(DeviceType.MOBILE));

        AdCampaign c2 = new AdCampaign("C-ADIDAS", 1.80, 15.00, "adidas_boost.jpg");
        c2.addSpecification(new GeoTargetingSpecification("US"));

        AdCampaign c3 = new AdCampaign("C-PUMA", 3.00, 0.50, "puma_wild.jpg"); // puma high bid but very low budget
        c3.addSpecification(new GeoTargetingSpecification("US"));

        engine.registerCampaign(c1);
        engine.registerCampaign(c2);
        engine.registerCampaign(c3);

        UserContext user = new UserContext("U-Mark", "US", DeviceType.MOBILE, Arrays.asList("sports", "running"));

        // 1. Serving first ad - expect Puma to win but pay second price ($2.51)
        System.out.println("--- Ad Request 1 ---");
        engine.serveAd(user);

        // 2. Puma budget is now $0.50 - $2.51 = failed (insufficient budget)
        // Next request - expect Nike to win and pay Adidas's price ($1.81)
        System.out.println("\n--- Ad Request 2 ---");
        engine.serveAd(user);

        // 3. Nike wins again, pays Adidas ($1.81)
        System.out.println("\n--- Ad Request 3 ---");
        engine.serveAd(user);

        // 4. Frequency cap check (Nike has been shown 2 times, limit is 2)
        // Nike should be blocked. Expect Adidas to win and pay reserve limit ($0.01)
        System.out.println("\n--- Ad Request 4 (Capping verification) ---");
        engine.serveAd(user);
    }
}