![]() |
RTBKit
0.9
Open-source framework to create real-time ad bidding systems.
|
00001 #include <iostream> 00002 #include <string> 00003 #include <unordered_map> 00004 #include <vector> 00005 00006 #include "soa/service/redis.h" 00007 00008 #include "redis_utils.h" 00009 00010 #include "redis_old_types.h" 00011 00012 00013 namespace { 00014 00015 bool IsMoreOrLess(long long int value, long long int acceptedDelta) 00016 { 00017 int absolute(acceptedDelta >= 0 ? acceptedDelta : -acceptedDelta); 00018 00019 return (value >= 0 00020 ? (value <= absolute) 00021 : (-value <= absolute)); 00022 } 00023 00024 } 00025 00026 00027 namespace RTBKIT { 00028 00029 using namespace std; 00030 using namespace RTBKIT; 00031 using namespace Redis; 00032 00033 00034 const string CampaignsPrefix = "campaigns:"; 00035 00036 00037 /* CAMPAIGN */ 00038 Campaign:: 00039 Campaign(const string & key, 00040 long long available, long long transferred) 00041 : key_(key), 00042 available_(available), transferred_(transferred) 00043 { 00044 } 00045 00046 /* validate campaign numbers with strategies */ 00047 bool 00048 Campaign:: 00049 validateAll(int acceptedDelta) 00050 const 00051 { 00052 long long int transferred(0); 00053 bool allValid(true); 00054 00055 for (const Strategy & strategy: strategies) { 00056 transferred += strategy.transferred_; 00057 allValid &= strategy.valid_; 00058 } 00059 00060 string campaignKey(CampaignsPrefix + key_); 00061 if (!allValid) { 00062 cerr << "! campaign '" << campaignKey 00063 << "' contains invalid strategies" 00064 << endl; 00065 return false; 00066 } 00067 00068 if (!IsMoreOrLess(transferred - transferred_, 00069 (acceptedDelta * strategies.size()))) { 00070 cerr << "! campaign '" << campaignKey 00071 << "' has invalid 'transferred' value:" 00072 << endl 00073 << " (computed) " << transferred 00074 << " != (stored) " << transferred_ 00075 << endl; 00076 return false; 00077 } 00078 00079 cerr << "- campaign '" << campaignKey 00080 << "' is valid and ready for conversion" 00081 << endl; 00082 00083 return true; 00084 } 00085 00086 void 00087 Campaign:: 00088 load(AsyncConnection & redis) 00089 { 00090 string redisKey(CampaignsPrefix + key_); 00091 Command hmget = HMGET(redisKey); 00092 hmget.addArg("available"); 00093 hmget.addArg("transferred"); 00094 00095 Result result = redis.exec(hmget); 00096 if (!result.ok()) { 00097 cerr << "! HMGET " + redisKey 00098 << ": error fetching result" << endl; 00099 return; 00100 } 00101 const Reply & reply = result.reply(); 00102 if (reply.type() != ARRAY) { 00103 cerr << "! HMGET " + redisKey 00104 << ": unexpected reply type" << endl; 00105 return; 00106 } 00107 if (!GetRedisReplyAsInt(reply[0], available_)) { 00108 cerr << "! HMGET " + redisKey 00109 << ": value for 'available' cannot be converted to int" 00110 << endl; 00111 return; 00112 } 00113 if (!GetRedisReplyAsInt(reply[1], transferred_)) { 00114 cerr << "! HMGET " + redisKey 00115 << ": value for 'transferred' cannot be converted to int" 00116 << endl; 00117 return; 00118 }; 00119 cerr << "- campaign '" + redisKey + "' properly loaded" 00120 << endl; 00121 } 00122 00123 void 00124 Campaign:: 00125 save(AsyncConnection & redis) 00126 const 00127 { 00128 string redisKey(CampaignsPrefix + key_); 00129 00130 Command hmset = HMSET(redisKey); 00131 hmset.addArg("available"); 00132 hmset.addArg(available_); 00133 hmset.addArg("transferred"); 00134 hmset.addArg(transferred_); 00135 00136 Result result = redis.exec(hmset); 00137 if (!result.ok()) { 00138 cerr << "! HMSET " + redisKey 00139 << ": error storing campaign keys" << endl; 00140 return; 00141 } 00142 00143 cerr << "- campaign '" + redisKey + "' properly saved" << endl; 00144 } 00145 00146 /* STRATEGY */ 00147 Strategy:: 00148 Strategy(const string & key, const string & campaignKey, 00149 long long available, long long spent, long long transferred) 00150 : key_(key), campaignKey_(campaignKey), valid_(false), 00151 available_(available), spent_(spent), 00152 transferred_(transferred) 00153 { 00154 } 00155 00156 void 00157 Strategy:: 00158 load(AsyncConnection & redis, int acceptedDelta) 00159 { 00160 string redisKey(CampaignsPrefix + campaignKey_ + ":" + key_); 00161 Command hmget = HMGET(redisKey); 00162 hmget.addArg("available"); 00163 hmget.addArg("spent"); 00164 hmget.addArg("transferred"); 00165 Result result = redis.exec(hmget); 00166 if (!result.ok()) { 00167 cerr << "! HMGET " + redisKey 00168 << ": error fetching result" << endl; 00169 return; 00170 } 00171 const Reply & reply = result.reply(); 00172 if (reply.type() != ARRAY) { 00173 cerr << "! HMGET " + redisKey 00174 << ": unexpected reply type" << endl; 00175 return; 00176 } 00177 if (!GetRedisReplyAsInt(reply[0], available_)) { 00178 cerr << "! HMGET " + redisKey 00179 << ": value for 'available' cannot be converted to int" 00180 << endl; 00181 return; 00182 } 00183 if (!GetRedisReplyAsInt(reply[1], spent_)) { 00184 cerr << "! HMGET " + redisKey 00185 << ": value for 'spent' cannot be converted to int" 00186 << endl; 00187 return; 00188 }; 00189 if (!GetRedisReplyAsInt(reply[2], transferred_)) { 00190 cerr << "! HMGET " + redisKey 00191 << ": value for 'transferred' cannot be converted to int" 00192 << endl; 00193 return; 00194 }; 00195 00196 /* validation */ 00197 if (transferred_ < 0 00198 // || !IsMoreOrLess(available_, acceptedDelta) 00199 || spent_ < 0) { 00200 cerr << "! strategy '" + redisKey + "' has one or more negative values:" 00201 << endl 00202 << " transferred = " << transferred_ 00203 << "; available = " << available_ 00204 << "; spent = " << spent_ 00205 << endl; 00206 } 00207 #if 0 00208 else if (!IsMoreOrLess((transferred_ - spent_) - available_, 00209 acceptedDelta)) { 00210 long long int delta = (available_ + spent_) - transferred_; 00211 cerr << "! strategy '" + redisKey + "' is in an inconsistent state:" 00212 << endl 00213 << " transferred = " << transferred_ 00214 << "; available = " << available_ 00215 << "; spent = " << spent_ 00216 << endl 00217 << " delta: " << delta 00218 << endl; 00219 } 00220 #endif 00221 else { 00222 cerr << "- strategy '" + redisKey + "' properly loaded and consistent" 00223 << endl; 00224 valid_ = true; 00225 } 00226 } 00227 00228 void 00229 Strategy:: 00230 save(AsyncConnection & redis) 00231 const 00232 { 00233 string redisKey(CampaignsPrefix + campaignKey_ + ":" + key_); 00234 00235 if (!valid_) { 00236 cerr << "! skipped the saving of 'invalid' strategy '" + redisKey + "'" 00237 << endl; 00238 return; 00239 } 00240 00241 Command hmset = HMSET(redisKey); 00242 hmset.addArg("available"); 00243 hmset.addArg(available_); 00244 hmset.addArg("spent"); 00245 hmset.addArg(spent_); 00246 hmset.addArg("transferred"); 00247 hmset.addArg(transferred_); 00248 00249 Result result = redis.exec(hmset); 00250 if (!result.ok()) { 00251 cerr << "! HMSET " + redisKey 00252 << ": error storing campaign keys" << endl; 00253 return; 00254 } 00255 00256 cerr << "- strategy '" + redisKey + "' properly saved" << endl; 00257 } 00258 00259 /* assign strategy to the right campaign */ 00260 void Strategy:: 00261 assignToParent(Campaigns & campaigns) const 00262 { 00263 if (campaigns.count(campaignKey_) == 0) { 00264 cerr << "! ignored strategy '" 00265 << CampaignsPrefix << campaignKey_ << ":" << key_ 00266 << "' without corresponding campaign" 00267 << endl; 00268 } 00269 else { 00270 Campaign & campaign = campaigns.at(campaignKey_); 00271 campaign.strategies.push_back(*this); 00272 } 00273 } 00274 00275 } // namespace RTBKIT