RTBKit  0.9
Open-source framework to create real-time ad bidding systems.
testing/test_agent.h
00001 /* test_agent.h                                                    -*- C++ -*-
00002    Jeremy Banres, 27 November 2012
00003    Copyright (c) 2012 Datacratic Inc.  All rights reserved.
00004 
00005    Test for an agent.
00006 */
00007 
00008 #pragma once
00009 
00010 #include "rtbkit/plugins/bidding_agent/bidding_agent.h"
00011 #include "jml/arch/futex.h"
00012 
00013 namespace RTBKIT {
00014 
00015 struct TestAgent : public RTBKIT::BiddingAgent {
00016     TestAgent(std::shared_ptr<RTBKIT::ServiceProxies> proxies,
00017               const std::string & name = "testAgent")
00018         : RTBKIT::BiddingAgent(proxies, name)
00019     {
00020         setDefaultConfig();
00021         setupCallbacks();
00022         clear();
00023     }
00024 
00025     RTBKIT::AgentConfig config;
00026 
00027     void setDefaultConfig()
00028     {
00029         RTBKIT::AgentConfig config;
00030         config.account = {"testCampaign", "testStrategy"};
00031         config.maxInFlight = 20000;
00032         config.creatives.push_back(RTBKIT::Creative::sampleLB);
00033         config.creatives.push_back(RTBKIT::Creative::sampleWS);
00034         config.creatives.push_back(RTBKIT::Creative::sampleBB);
00035 
00036         this->config = config;
00037     }
00038 
00039     void start()
00040     {
00041         BiddingAgent::start();
00042         configure();
00043     }
00044 
00045     void clear()
00046     {
00047         numHeartbeats = numBidRequests = numErrors = numGotConfig = 0;
00048         numWins = numLosses = numNoBudgets = numTooLates = 0;
00049         numBidsOutstanding = 0;
00050     }
00051 
00052     bool sleepUntilIdle(double waitTime = 0.0)
00053     {
00054         Date timeout;
00055         if (waitTime > 0.0) timeout = Date::now().plusSeconds(waitTime);
00056 
00057         for (;;) {
00058             int oldOutstanding = numBidsOutstanding;
00059 
00060             //cerr << "numBidsOutstanding = " << oldOutstanding << endl;
00061 
00062             if (oldOutstanding == 0) return true;
00063             
00064             Date now = Date::now();
00065             if (now >= timeout && waitTime != 0.0) return false;
00066             
00067             int res;
00068 
00069             if (waitTime == 0)
00070                 res = ML::futex_wait(numBidsOutstanding, oldOutstanding);
00071             else res = ML::futex_wait(numBidsOutstanding, oldOutstanding,
00072                                       now.secondsUntil(timeout));
00073 
00074             //cerr << "res = " << res << endl;
00075 
00076             if (res == 0) return true;
00077             if (errno == ETIMEDOUT) return false;
00078             if (errno == EINTR) continue;
00079             if (errno == EAGAIN) continue;
00080             
00081             throw ML::Exception(errno, "futex_wait");
00082         }
00083     }
00084 
00085     int numHeartbeats;
00086     int numBidRequests;
00087     int numErrors;
00088     int numGotConfig;
00089     int numWins;
00090     int numLosses;
00091     int numNoBudgets;
00092     int numTooLates;
00093 
00094     typedef ML::Spinlock Lock;
00095     typedef boost::lock_guard<Lock> Guard;
00096     mutable Lock lock;
00097 
00098     std::set<Id> awaitingStatus;
00099     int numBidsOutstanding;
00100     
00101     void defaultError(double timestamp, const std::string & error,
00102                  const std::vector<std::string> & message)
00103     {
00104         using namespace std;
00105         cerr << "agent got error: " << error << " from message: "
00106              << message << endl;
00107         __sync_fetch_and_add(&numErrors, 1);
00108     }
00109 
00110     void finishBid(int & counter, const RTBKIT::BidResult & args)
00111     {
00112         __sync_fetch_and_add(&counter, 1);
00113         Guard guard(lock);
00114         if (!awaitingStatus.erase(args.auctionId))
00115             throw ML::Exception("couldn't find in progress auction");
00116 
00117         numBidsOutstanding = awaitingStatus.size();
00118         if (numBidsOutstanding == 0)
00119             ML::futex_wake(numBidsOutstanding);
00120     }
00121 
00122     void defaultWin(const RTBKIT::BidResult & args)
00123     {
00124         finishBid(numWins, args);
00125         //cerr << args.accountInfo << endl;
00126     }
00127                 
00128     void defaultLoss(const RTBKIT::BidResult & args)
00129     {
00130         finishBid(numLosses, args);
00131     }
00132 
00133     void defaultNoBudget(const RTBKIT::BidResult & args)
00134     {
00135         finishBid(numNoBudgets, args);
00136     }
00137 
00138     void defaultTooLate(const RTBKIT::BidResult & args)
00139     {
00140         finishBid(numTooLates, args);
00141     }
00142 
00143     void doBid(const Id & id,
00144                const Bids & bids,
00145                const Json::Value & metadata)
00146     {
00147         if (bids.size() != 0)
00148             recordBid(id);
00149         RTBKIT::BiddingAgent::doBid(id, bids, metadata);
00150     }
00151 
00152     void bidNull(double timestamp,
00153                  const Id & id,
00154                  std::shared_ptr<RTBKIT::BidRequest> br,
00155                  const Bids & bids,
00156                  double timeLeftMs,
00157                  const Json::Value & augmentations)
00158     {
00159         using namespace std;
00160         //cerr << "got auction " << id << endl;
00161         doBid(id, bids, Json::Value());
00162         __sync_fetch_and_add(&numBidRequests, 1);
00163     }
00164 
00165     void recordBid(const Id & id)
00166     {
00167         Guard guard(lock);
00168         if (!awaitingStatus.insert(id).second)
00169             throw ML::Exception("auction already in progress");
00170         
00171         numBidsOutstanding = awaitingStatus.size();
00172     }
00173 
00174     void setupCallbacks()
00175     {
00176         onError
00177             = boost::bind(&TestAgent::defaultError, this, _1, _2, _3);
00178         onBidRequest
00179             = boost::bind(&TestAgent::bidNull, this, _1, _2, _3, _4, _5, _6);
00180         onWin
00181             = boost::bind(&TestAgent::defaultWin, this, _1);
00182         onLoss
00183             = boost::bind(&TestAgent::defaultLoss, this, _1);
00184         onNoBudget
00185             = boost::bind(&TestAgent::defaultNoBudget, this, _1);
00186         onTooLate
00187             = boost::bind(&TestAgent::defaultTooLate, this, _1);
00188     }    
00189 
00190     void configure()
00191     {
00192         doConfig(config);
00193     }
00194 
00195 };
00196 
00197 } // namespace RTBKIT
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator