![]() |
RTBKit
0.9
Open-source framework to create real-time ad bidding systems.
|
00001 /* rtb_router_js.cc 00002 Jeremy Barnes, 5 April 2011 00003 Copyright (c) 2011 Datacratic. All rights reserved. 00004 00005 JS bindings for RTB router. 00006 */ 00007 00008 #include "rtbkit/core/router/router_stack.h" 00009 #include "rtb_js.h" 00010 #include "auction_js.h" 00011 #include "banker_js.h" 00012 #include "v8.h" 00013 #include "node.h" 00014 #include "soa/js/js_value.h" 00015 #include "soa/js/js_utils.h" 00016 #include "soa/js/js_wrapped.h" 00017 #include "soa/js/js_call.h" 00018 #include "soa/js/js_registry.h" 00019 #include "jml/arch/timers.h" 00020 #include "soa/sigslot/slot.h" 00021 #include "jml/utils/guard.h" 00022 #include <boost/make_shared.hpp> 00023 #include "rtbkit/plugins/bidding_agent/bidding_agent.h" 00024 #include "node/uv.h" 00025 #include "soa/types/js/id_js.h" 00026 #include "bid_request_js.h" 00027 #include "currency_js.h" 00028 00029 using namespace std; 00030 using namespace v8; 00031 using namespace node; 00032 using namespace ML; 00033 using namespace RTBKIT; 00034 00035 namespace Datacratic { 00036 namespace JS { 00037 00038 extern Registry registry; 00039 00040 00041 /*****************************************************************************/ 00042 /* RTB ROUTER JS */ 00043 /*****************************************************************************/ 00044 00051 const char * RTBRouterStackName = "Router"; 00052 00053 struct RTBRouterStackJS 00054 : public JSWrapped2<RTBKIT::RouterStack, RTBRouterStackJS, RTBRouterStackName, 00055 rtbModule, false> { 00056 00057 RTBRouterStackJS(const v8::Handle<v8::Object> & This, 00058 const std::shared_ptr<RTBKIT::RouterStack> & router 00059 = std::shared_ptr<RTBKIT::RouterStack>()) 00060 { 00061 wrap(This, router); 00062 } 00063 00064 static Handle<v8::Value> 00065 New(const Arguments & args) 00066 { 00067 try { 00068 new RTBRouterStackJS 00069 (args.This(), 00070 std::shared_ptr<RTBKIT::RouterStack> 00071 (new RTBKIT::RouterStack(std::make_shared<ServiceProxies>(), 00072 getArg(args, 2, "router", "routerName"), 00073 getArg(args, 0, 2.0, 00074 "secondsUntilLossAssumed")))); 00075 return args.This(); 00076 } HANDLE_JS_EXCEPTIONS; 00077 } 00078 00079 static void Initialize() 00080 { 00081 Persistent<FunctionTemplate> t = Register(New); 00082 00083 // Instance methods 00084 //NODE_SET_PROTOTYPE_METHOD(t, "initEndpoints", initEndpoints); 00085 NODE_SET_PROTOTYPE_METHOD(t, "createProxy", createProxy); 00086 NODE_SET_PROTOTYPE_METHOD(t, "bindAgents", bindAgents); 00087 NODE_SET_PROTOTYPE_METHOD(t, "start", start); 00088 NODE_SET_PROTOTYPE_METHOD(t, "sleepUntilIdle", sleepUntilIdle); 00089 NODE_SET_PROTOTYPE_METHOD(t, "shutdown", shutdown); 00090 NODE_SET_PROTOTYPE_METHOD(t, "injectAuction", injectAuction); 00091 NODE_SET_PROTOTYPE_METHOD(t, "injectWin", injectWin); 00092 NODE_SET_PROTOTYPE_METHOD(t, "injectLoss", injectLoss); 00093 NODE_SET_PROTOTYPE_METHOD(t, "injectCampaignEvent", injectCampaignEvent); 00094 NODE_SET_PROTOTYPE_METHOD(t, "numAuctionsInProgress", 00095 numAuctionsInProgress); 00096 NODE_SET_PROTOTYPE_METHOD(t, "getStats", getStats); 00097 00098 #if 0 00099 00100 t->InstanceTemplate() 00101 ->SetAccessor(String::NewSymbol("banker"), 00102 bankerGetter, bankerSetter); 00103 #endif 00104 00105 #if 0 00106 registerRWPropertyGetterSetter(&RTBKIT::RouterStack::getBanker, 00107 &RTBKIT::RouterStack::setBanker, 00108 "banker", v8::DontDelete); 00109 #endif 00110 registerMemberFn(&RTBKIT::RouterStack::notifyFinishedAuction, 00111 "notifyFinishedAuction"); 00112 registerMemberFn(&RTBKIT::RouterStack::notifyFinishedSpot, 00113 "notifyFinishedSpot"); 00114 registerMemberFn(&RTBKIT::RouterStack::init, "init"); 00115 } 00116 00117 #if 0 00118 static Handle<v8::Value> 00119 initEndpoints(const Arguments & args) 00120 { 00121 try { 00122 getShared(args) 00123 ->initEndpoints(getArg(args, 0, -1, "bidPort"), 00124 //getArg(args, 1, -1, "backchannelPort"), 00125 getArg(args, 2, "localhost", "hostname"), 00126 getArg(args, 3, 1, "nThreads")); 00127 return args.This(); 00128 } HANDLE_JS_EXCEPTIONS; 00129 } 00130 #endif 00131 00132 static Handle<v8::Value> 00133 createProxy(const Arguments & args) 00134 { 00135 try { 00136 00137 return JS::toJS 00138 (std::make_shared<RTBKIT::BiddingAgent> 00139 (getShared(args)->getServices(), "bidding_agent")); 00140 } HANDLE_JS_EXCEPTIONS; 00141 } 00142 00143 static Handle<v8::Value> 00144 bindAgents(const Arguments & args) 00145 { 00146 try { 00147 getShared(args) 00148 ->router.bindAgents(getArg(args, 0, "agentsUri")); 00149 return args.This(); 00150 } HANDLE_JS_EXCEPTIONS; 00151 } 00152 00153 static Handle<v8::Value> 00154 start(const Arguments & args) 00155 { 00156 try { 00157 // Make sure Node doesn't exit and we don't get GCd when the 00158 // event loop is running. 00159 00160 struct ev_loop * loop = ev_default_loop(0); 00161 ev_ref(loop); 00162 v8::Persistent<v8::Object> phandle 00163 = v8::Persistent<v8::Object>::New(args.This()); 00164 00165 auto cleanup = [=] () 00166 { 00167 //cerr << "calling cleanup" << endl; 00168 00169 v8::Persistent<v8::Object> handle = phandle; 00170 ev_unref(loop); 00171 00172 //cerr << "done loop unref" << endl; 00173 //cerr << "depth " << ev_depth(loop) << endl; 00174 //cerr << "iter " << ev_iteration(loop) << endl; 00175 //cerr << "watching " << ev_pending_count(loop) << endl; 00176 //ev_break(loop, EVBREAK_ONE); 00177 00178 handle.Clear(); 00179 handle.Dispose(); 00180 00181 }; 00182 00183 getShared(args)->start(cleanup); 00184 00185 // TODO: add something to node to stop it from returning? 00186 00187 return args.This(); 00188 } HANDLE_JS_EXCEPTIONS; 00189 } 00190 00191 static Handle<v8::Value> 00192 shutdown(const Arguments & args) 00193 { 00194 try { 00195 getShared(args)->shutdown(); 00196 //cerr << "finished shutdown" << endl; 00197 return args.This(); 00198 } HANDLE_JS_EXCEPTIONS; 00199 } 00200 00201 static Handle<v8::Value> 00202 sleepUntilIdle(const Arguments & args) 00203 { 00204 try { 00205 getShared(args)->sleepUntilIdle(); 00206 //cerr << "finished shutdown" << endl; 00207 return args.This(); 00208 } HANDLE_JS_EXCEPTIONS; 00209 } 00210 00211 struct CallbackData { 00212 Slot onAuctionFinished; 00213 std::shared_ptr<Auction> auction; 00214 v8::Persistent<v8::Object> This; 00215 00216 ~CallbackData() 00217 { 00218 if (!This.IsEmpty()) { 00219 This.Dispose(); 00220 This.Clear(); 00221 } 00222 } 00223 }; 00224 00225 static void doNothing(eio_req * req) 00226 { 00227 // TODO: don't do this; find how to use libeio properly 00228 } 00229 00230 static int finishedCallback(eio_req * req) 00231 { 00232 HandleScope scope; 00233 00234 auto_ptr<CallbackData> data((CallbackData *)req->data); 00235 00236 TryCatch try_catch; 00237 00238 try { 00239 const int argc = 1; 00240 v8::Handle<v8::Value> argv[argc] = { JS::toJS(data->auction) }; 00241 data->onAuctionFinished.call(data->This, argc, argv); 00242 } catch (const JSPassException & exc) { 00243 // Corner case... but probably shouldn't happen 00244 } catch (...) { 00245 v8::Handle<v8::Value> result = translateCurrentException(); 00246 v8::ThrowException(result); 00247 } 00248 00249 if (try_catch.HasCaught()) 00250 FatalException(try_catch); 00251 00252 return 0; 00253 } 00254 00255 00256 static Handle<v8::Value> 00257 injectAuction(const Arguments & args) 00258 { 00259 try { 00260 Slot onAuctionFinished = getArg(args, 0, "onAuctionFinished"); 00261 double startTime = getArg(args, 2, 00262 Date::now().secondsSinceEpoch(), 00263 "startTime"); 00264 double expiryTime = getArg(args, 3, startTime + 0.03, 00265 "expiryTime"); 00266 double lossTime = getArg(args, 4, 00267 Date::positiveInfinity() 00268 .secondsSinceEpoch(), 00269 "lossTime"); 00270 00271 std::shared_ptr<BidRequest> request; 00272 std::string requestStr; 00273 00274 v8::Handle<v8::Value> requestArg = args[1]; 00275 std::string requestFormat; 00276 if (requestArg->IsString()) { 00277 requestStr = getArg<string>(args, 1, "requestStr"); 00278 requestFormat = getArg<string>(args, 5, "datacratic", 00279 "requestFormat"); 00280 request.reset(BidRequest::parse(requestFormat, requestStr)); 00281 } 00282 else if (request = getBidRequestSharedPointer(requestArg)) { 00283 requestStr = request->toJsonStr(); 00284 requestFormat = "datacratic"; 00285 } 00286 else throw ML::Exception("don't know how to turn " + cstr(requestArg) 00287 + " into a bidRequest for injectAuction"); 00288 00289 CallbackData * data = new CallbackData(); 00290 Call_Guard deleteDataGuard([&] () { delete data; }); 00291 00292 data->onAuctionFinished = onAuctionFinished; 00293 data->This = v8::Persistent<v8::Object>::New(args.This()); 00294 00295 /* Create an asynchronous, libeioified wrapper that will call 00296 back to the JS callback from the correct callback thread. 00297 */ 00298 auto onAuctionFinished2 = [=] (std::shared_ptr<Auction> auction) 00299 { 00300 data->auction = auction; 00301 00302 eio_custom(doNothing, EIO_PRI_DEFAULT, finishedCallback, 00303 data); 00304 00305 return true; 00306 }; 00307 00308 getShared(args) 00309 ->router.injectAuction(onAuctionFinished2, 00310 request, requestStr, requestFormat, 00311 startTime, expiryTime, lossTime); 00312 00313 deleteDataGuard.clear(); 00314 00315 return args.This(); 00316 } HANDLE_JS_EXCEPTIONS; 00317 } 00318 00319 static Handle<v8::Value> 00320 injectWin(const Arguments & args) 00321 { 00322 try { 00323 Id auctionId = getArg(args, 0, "auctionId"); 00324 Id adSpot = getArg(args, 1, "adspot"); 00325 Amount winAmount = getArg(args, 2, "winPrice"); 00326 Date timestamp 00327 = Date::fromSecondsSinceEpoch 00328 (getArg<double>(args, 3, "timestamp")); 00329 Json::Value meta = getArg(args, 4, Json::Value(), "meta"); 00330 UserIds uids = getArg(args, 5, UserIds(), "userIds"); 00331 AccountKey account = getArg(args, 6, AccountKey(), "account"); 00332 Date bidTimestamp 00333 = Date::fromSecondsSinceEpoch 00334 (getArg<double>(args, 7, -1, "bidTimestamp")); 00335 getShared(args)->injectWin(auctionId, adSpot, winAmount, 00336 timestamp, meta, uids, 00337 account, bidTimestamp); 00338 return args.This(); 00339 } HANDLE_JS_EXCEPTIONS; 00340 } 00341 00342 static Handle<v8::Value> 00343 injectLoss(const Arguments & args) 00344 { 00345 try { 00346 Id auctionId = getArg(args, 0, "auctionId"); 00347 Id adSpot = getArg(args, 1, "adspot"); 00348 Date timestamp 00349 = Date::fromSecondsSinceEpoch 00350 (getArg<double>(args, 2, "timestamp")); 00351 Json::Value meta = getArg(args, 3, Json::Value(), "meta"); 00352 AccountKey account = getArg(args, 4, AccountKey(), "account"); 00353 Date bidTimestamp 00354 = Date::fromSecondsSinceEpoch 00355 (getArg<double>(args, 5, -1, "bidTimestamp")); 00356 getShared(args)->injectLoss(auctionId, adSpot, timestamp, 00357 meta, account, bidTimestamp); 00358 return args.This(); 00359 } HANDLE_JS_EXCEPTIONS; 00360 } 00361 00362 static Handle<v8::Value> 00363 injectCampaignEvent(const Arguments & args) 00364 { 00365 try { 00366 string label = getArg(args, 0, "label"); 00367 Id auctionId = getArg(args, 1, "auctionId"); 00368 Id adSpot = getArg(args, 2, "adspot"); 00369 Date timestamp 00370 = Date::fromSecondsSinceEpoch 00371 (getArg<double>(args, 3, "timestamp")); 00372 Json::Value meta = getArg(args, 4, Json::Value(), "meta"); 00373 UserIds uids = getArg(args, 5, UserIds(), "userIds"); 00374 getShared(args)->injectCampaignEvent(label, auctionId, adSpot, 00375 timestamp, meta, uids); 00376 return args.This(); 00377 } HANDLE_JS_EXCEPTIONS; 00378 } 00379 00380 static Handle<v8::Value> 00381 numAuctionsInProgress(const Arguments & args) 00382 { 00383 try { 00384 return JS::toJS(getShared(args)->numAuctionsInProgress()); 00385 } HANDLE_JS_EXCEPTIONS; 00386 } 00387 00388 static Handle<v8::Value> 00389 getStats(const Arguments & args) 00390 { 00391 try { 00392 return JS::toJS(getShared(args)->getStats()); 00393 } HANDLE_JS_EXCEPTIONS; 00394 } 00395 00396 #if 0 00397 static Handle<v8::Value> 00398 addBudget(const Arguments & args) 00399 { 00400 try { 00401 return JS::toJS(getShared(args) 00402 ->addBudget(getArg(args, 0, "account"), 00403 getArg(args, 1, "amount"))); 00404 } HANDLE_JS_EXCEPTIONS; 00405 } 00406 00407 static Handle<v8::Value> 00408 setBudget(const Arguments & args) 00409 { 00410 try { 00411 return JS::toJS(getShared(args) 00412 ->setBudget(getArg(args, 0, "account"), 00413 getArg(args, 1, "amount"))); 00414 } HANDLE_JS_EXCEPTIONS; 00415 } 00416 #endif 00417 00418 #if 0 00419 static v8::Handle<v8::Value> 00420 bankerGetter(v8::Local<v8::String> property, 00421 const AccessorInfo & info) 00422 { 00423 try { 00424 return JS::toJS(getShared(info.This())->getBanker()); 00425 } HANDLE_JS_EXCEPTIONS; 00426 } 00427 00428 static void 00429 bankerSetter(v8::Local<v8::String> property, 00430 v8::Local<v8::Value> value, 00431 const AccessorInfo & info) 00432 { 00433 try { 00434 getShared(info.This())->setBanker(JS::fromJS(value)); 00435 } HANDLE_JS_EXCEPTIONS_SETTER; 00436 } 00437 #endif 00438 00439 }; 00440 00441 RegisterJsOps<void (std::shared_ptr<Auction>)> reg_handleAuction; 00442 00443 } // namespace JS 00444 } // namespace Datacratic