00001
00002
00003
00004
00005
00006
00007
00008
00009 #include <qdatetime.h>
00010
00011 #include "eitcache.h"
00012 #include "mythcontext.h"
00013 #include "mythdbcon.h"
00014
00015 #define LOC QString("EITCache: ")
00016
00017
00018 const uint EITCache::kVersionMax = 31;
00019
00020 EITCache::EITCache()
00021 : accessCnt(0), hitCnt(0), tblChgCnt(0), verChgCnt(0),
00022 entryCnt(0), pruneCnt(0), prunedHitCnt(0), wrongChannelHitCnt(0)
00023 {
00024
00025 lastPruneTime = QDateTime::currentDateTime(Qt::UTC).toTime_t() - 86400;
00026 }
00027
00028 EITCache::~EITCache()
00029 {
00030 WriteToDB();
00031 }
00032
00033 void EITCache::ResetStatistics(void)
00034 {
00035 accessCnt = 0;
00036 hitCnt = 0;
00037 tblChgCnt = 0;
00038 verChgCnt = 0;
00039 entryCnt = 0;
00040 pruneCnt = 0;
00041 prunedHitCnt = 0;
00042 wrongChannelHitCnt = 0;
00043 }
00044
00045 QString EITCache::GetStatistics(void) const
00046 {
00047 QMutexLocker locker(&eventMapLock);
00048 return QString(
00049 "EITCache::statistics: Accesses: %1, Hits: %2, "
00050 "Table Upgrades %3, New Versions: %4, Entries: %5 "
00051 "Pruned entries: %6, pruned Hits: %7 Discard channel Hit %8 "
00052 "Hit Ratio %9.")
00053 .arg(accessCnt).arg(hitCnt).arg(tblChgCnt).arg(verChgCnt)
00054 .arg(entryCnt).arg(pruneCnt).arg(prunedHitCnt)
00055 .arg(wrongChannelHitCnt)
00056 .arg((hitCnt+prunedHitCnt+wrongChannelHitCnt)/(double)accessCnt);
00057 }
00058
00059 static inline uint64_t construct_sig(uint tableid, uint version,
00060 uint endtime, bool modified)
00061 {
00062 return (((uint64_t) modified << 63) | ((uint64_t) tableid << 40) |
00063 ((uint64_t) version << 32) | ((uint64_t) endtime));
00064 }
00065
00066 static inline uint extract_table_id(uint64_t sig)
00067 {
00068 return (sig >> 40) & 0xff;
00069 }
00070
00071 static inline uint extract_version(uint64_t sig)
00072 {
00073 return (sig >> 32) & 0x1f;
00074 }
00075
00076 static inline uint extract_endtime(uint64_t sig)
00077 {
00078 return sig & 0xffffffff;
00079 }
00080
00081 static inline bool modified(uint64_t sig)
00082 {
00083 return sig >> 63;
00084 }
00085
00086 static void replace_in_db(int chanid, uint eventid, uint64_t sig)
00087 {
00088
00089 MSqlQuery query(MSqlQuery::InitCon());
00090
00091 QString qstr =
00092 "REPLACE INTO eit_cache "
00093 " ( chanid, eventid, tableid, version, endtime) "
00094 "VALUES (:CHANID, :EVENTID, :TABLEID, :VERSION, :ENDTIME)";
00095
00096 query.prepare(qstr);
00097 query.bindValue(":CHANID", chanid);
00098 query.bindValue(":EVENTID", eventid);
00099 query.bindValue(":TABLEID", extract_table_id(sig));
00100 query.bindValue(":VERSION", extract_version(sig));
00101 query.bindValue(":ENDTIME", extract_endtime(sig));
00102
00103 if (!query.exec())
00104 MythContext::DBError("Error updating eitcache", query);
00105
00106 return;
00107 }
00108
00109 static void delete_in_db(uint endtime)
00110 {
00111 VERBOSE(VB_EIT, LOC + "Deleting old cache entries from the database");
00112 MSqlQuery query(MSqlQuery::InitCon());
00113
00114 QString qstr =
00115 "DELETE FROM eit_cache "
00116 "WHERE endtime < :ENDTIME";
00117
00118 query.prepare(qstr);
00119 query.bindValue(":ENDTIME", endtime);
00120
00121 if (!query.exec())
00122 MythContext::DBError("Error deleting old eitcache entries.", query);
00123
00124 return;
00125 }
00126
00127 #define EITDATA 0
00128 #define CHANNEL_LOCK 1
00129 #define STATISTIC 2
00130
00131 static bool lock_channel(int chanid, uint lastPruneTime)
00132 {
00133 int lock = 1;
00134 MSqlQuery query(MSqlQuery::InitCon());
00135
00136 QString qstr = "SELECT COUNT(*) "
00137 "FROM eit_cache "
00138 "WHERE chanid = :CHANID AND "
00139 " endtime > :ENDTIME AND "
00140 " status = :STATUS";
00141
00142 query.prepare(qstr);
00143 query.bindValue(":CHANID", chanid);
00144 query.bindValue(":ENDTIME", lastPruneTime);
00145 query.bindValue(":STATUS", CHANNEL_LOCK);
00146
00147 if (!query.exec() || !query.isActive())
00148 {
00149 MythContext::DBError("Error checking for channel lock", query);
00150 return false;
00151 }
00152
00153 if (query.next())
00154 lock = query.value(0).toInt();
00155
00156 if (lock)
00157 {
00158 VERBOSE(VB_EIT, LOC + QString("Ignoring channel %1 since it is locked.")
00159 .arg(chanid));
00160 return false;
00161 }
00162 else
00163 {
00164 uint now = QDateTime::currentDateTime().toTime_t();
00165 qstr = "INSERT INTO eit_cache "
00166 " ( chanid, endtime, status) "
00167 "VALUES (:CHANID, :ENDTIME, :STATUS)";
00168
00169 query.prepare(qstr);
00170 query.bindValue(":CHANID", chanid);
00171 query.bindValue(":ENDTIME", now);
00172 query.bindValue(":STATUS", CHANNEL_LOCK);
00173
00174 if (!query.exec())
00175 {
00176 MythContext::DBError("Error inserting channel lock", query);
00177 return false;
00178 }
00179 }
00180
00181 return true;
00182 }
00183
00184 static void unlock_channel(int chanid, uint updated)
00185 {
00186 MSqlQuery query(MSqlQuery::InitCon());
00187
00188 QString qstr =
00189 "DELETE FROM eit_cache "
00190 "WHERE chanid = :CHANID AND "
00191 " status = :STATUS";
00192
00193 query.prepare(qstr);
00194 query.bindValue(":CHANID", chanid);
00195 query.bindValue(":STATUS", CHANNEL_LOCK);
00196
00197 if (!query.exec())
00198 MythContext::DBError("Error deleting channel lock", query);
00199
00200
00201 uint now = QDateTime::currentDateTime().toTime_t();
00202 qstr = "REPLACE INTO eit_cache "
00203 " ( chanid, eventid, endtime, status) "
00204 "VALUES (:CHANID, :EVENTID, :ENDTIME, :STATUS)";
00205
00206 query.prepare(qstr);
00207 query.bindValue(":CHANID", chanid);
00208 query.bindValue(":EVENTID", updated);
00209 query.bindValue(":ENDTIME", now);
00210 query.bindValue(":STATUS", STATISTIC);
00211
00212 if (!query.exec())
00213 MythContext::DBError("Error inserting eit statistics", query);
00214 }
00215
00216
00217 event_map_t * EITCache::LoadChannel(uint chanid)
00218 {
00219 if (!lock_channel(chanid, lastPruneTime))
00220 return NULL;
00221
00222 MSqlQuery query(MSqlQuery::InitCon());
00223
00224 QString qstr =
00225 "SELECT eventid,tableid,version,endtime "
00226 "FROM eit_cache "
00227 "WHERE chanid = :CHANID AND "
00228 " endtime > :ENDTIME AND "
00229 " status = :STATUS";
00230
00231 query.prepare(qstr);
00232 query.bindValue(":CHANID", chanid);
00233 query.bindValue(":ENDTIME", lastPruneTime);
00234 query.bindValue(":STATUS", EITDATA);
00235
00236
00237 if (!query.exec() || !query.isActive())
00238 {
00239 MythContext::DBError("Error loading eitcache", query);
00240 return NULL;
00241 }
00242
00243 event_map_t * eventMap = new event_map_t();
00244
00245 while (query.next())
00246 {
00247 uint eventid = query.value(0).toUInt();
00248 uint tableid = query.value(1).toUInt();
00249 uint version = query.value(2).toUInt();
00250 uint endtime = query.value(3).toUInt();
00251
00252 (*eventMap)[eventid] = construct_sig(tableid, version, endtime, false);
00253 }
00254
00255 if (eventMap->size())
00256 VERBOSE(VB_EIT, LOC + QString("Loaded %1 entries for channel %2")
00257 .arg(eventMap->size()).arg(chanid));
00258
00259 entryCnt += eventMap->size();
00260 return eventMap;
00261 }
00262
00263 void EITCache::WriteChannelToDB(uint chanid)
00264 {
00265 event_map_t * eventMap = channelMap[chanid];
00266
00267 if (!eventMap)
00268 {
00269 channelMap.erase(chanid);
00270 return;
00271 }
00272
00273 uint size = eventMap->size();
00274 uint updated = 0;
00275
00276 event_map_t::iterator it = eventMap->begin();
00277 while (it != eventMap->end())
00278 {
00279 if (modified(*it) && extract_endtime(*it) > lastPruneTime)
00280 {
00281 replace_in_db(chanid, it.key(), *it);
00282 updated++;
00283 *it &= ~(uint64_t)0 >> 1;
00284 }
00285 it++;
00286 }
00287 unlock_channel(chanid, updated);
00288
00289 if (updated)
00290 VERBOSE(VB_EIT, LOC + QString("Wrote %1 modified entries of %2 "
00291 "for channel %3 to database.")
00292 .arg(updated).arg(size).arg(chanid));
00293 }
00294
00295 void EITCache::WriteToDB(void)
00296 {
00297 QMutexLocker locker(&eventMapLock);
00298
00299 key_map_t::iterator it = channelMap.begin();
00300 while (it != channelMap.end())
00301 {
00302 WriteChannelToDB(it.key());
00303 ++it;
00304 }
00305 }
00306
00307
00308
00309 bool EITCache::IsNewEIT(uint chanid, uint tableid, uint version,
00310 uint eventid, uint endtime)
00311 {
00312 accessCnt++;
00313
00314 if (accessCnt % 500000 == 50000)
00315 {
00316 VERBOSE(VB_EIT, endl << GetStatistics());
00317 WriteToDB();
00318 }
00319
00320
00321 if (endtime < lastPruneTime)
00322 {
00323 prunedHitCnt++;
00324 return false;
00325 }
00326
00327 if (endtime > lastPruneTime + 50 * 86400)
00328 return false;
00329
00330 QMutexLocker locker(&eventMapLock);
00331 if (!channelMap.contains(chanid))
00332 {
00333 channelMap[chanid] = LoadChannel(chanid);
00334 }
00335
00336 if (!channelMap[chanid])
00337 {
00338 wrongChannelHitCnt++;
00339 return false;
00340 }
00341
00342 event_map_t * eventMap = channelMap[chanid];
00343 event_map_t::iterator it = eventMap->find(eventid);
00344 if (it != eventMap->end())
00345 {
00346 if (extract_table_id(*it) > tableid)
00347 {
00348
00349 tblChgCnt++;
00350 }
00351 else if ((extract_table_id(*it) == tableid) &&
00352 ((extract_version(*it) < version) ||
00353 ((extract_version(*it) == kVersionMax) &&
00354 version < kVersionMax)))
00355 {
00356
00357 verChgCnt++;
00358 }
00359 else
00360 {
00361
00362 hitCnt++;
00363 return false;
00364 }
00365 }
00366
00367 eventMap->insert(eventid, construct_sig(tableid, version, endtime, true));
00368 entryCnt++;
00369
00370 return true;
00371 }
00372
00377 uint EITCache::PruneOldEntries(uint timestamp)
00378 {
00379 if (print_verbose_messages & VB_EIT)
00380 {
00381 QDateTime tmptime;
00382 tmptime.setTime_t(timestamp);
00383 VERBOSE(VB_EIT, LOC + "Pruning all entries that ended before UTC " +
00384 tmptime.toString(Qt::ISODate));
00385 }
00386
00387 lastPruneTime = timestamp;
00388
00389
00390 WriteToDB();
00391
00392
00393 delete_in_db(timestamp);
00394
00395 return 0;
00396 }
00397
00398
00402 void EITCache::ClearChannelLocks(void)
00403 {
00404 MSqlQuery query(MSqlQuery::InitCon());
00405
00406 QString qstr =
00407 "DELETE FROM eit_cache "
00408 "WHERE status = :STATUS";
00409
00410 query.prepare(qstr);
00411 query.bindValue(":STATUS", CHANNEL_LOCK);
00412
00413 if (!query.exec())
00414 MythContext::DBError("Error clearing channel locks", query);
00415 }
00416
00417