00001
00002
00003
00004
00005
00006
00007
00008 #include <QDateTime>
00009
00010 #include "eitcache.h"
00011 #include "mythcontext.h"
00012 #include "mythdb.h"
00013 #include "mythlogging.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().toUTC().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 MythDB::DBError("Error updating eitcache", query);
00105
00106 return;
00107 }
00108
00109 static void delete_in_db(uint endtime)
00110 {
00111 LOG(VB_EIT, LOG_INFO, 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 MythDB::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 MythDB::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 LOG(VB_EIT, LOG_INFO,
00159 LOC + QString("Ignoring channel %1 since it is locked.")
00160 .arg(chanid));
00161 return false;
00162 }
00163 else
00164 {
00165 uint now = QDateTime::currentDateTime().toTime_t();
00166 qstr = "INSERT INTO eit_cache "
00167 " ( chanid, endtime, status) "
00168 "VALUES (:CHANID, :ENDTIME, :STATUS)";
00169
00170 query.prepare(qstr);
00171 query.bindValue(":CHANID", chanid);
00172 query.bindValue(":ENDTIME", now);
00173 query.bindValue(":STATUS", CHANNEL_LOCK);
00174
00175 if (!query.exec())
00176 {
00177 MythDB::DBError("Error inserting channel lock", query);
00178 return false;
00179 }
00180 }
00181
00182 return true;
00183 }
00184
00185 static void unlock_channel(int chanid, uint updated)
00186 {
00187 MSqlQuery query(MSqlQuery::InitCon());
00188
00189 QString qstr =
00190 "DELETE FROM eit_cache "
00191 "WHERE chanid = :CHANID AND "
00192 " status = :STATUS";
00193
00194 query.prepare(qstr);
00195 query.bindValue(":CHANID", chanid);
00196 query.bindValue(":STATUS", CHANNEL_LOCK);
00197
00198 if (!query.exec())
00199 MythDB::DBError("Error deleting channel lock", query);
00200
00201
00202 uint now = QDateTime::currentDateTime().toTime_t();
00203 qstr = "REPLACE INTO eit_cache "
00204 " ( chanid, eventid, endtime, status) "
00205 "VALUES (:CHANID, :EVENTID, :ENDTIME, :STATUS)";
00206
00207 query.prepare(qstr);
00208 query.bindValue(":CHANID", chanid);
00209 query.bindValue(":EVENTID", updated);
00210 query.bindValue(":ENDTIME", now);
00211 query.bindValue(":STATUS", STATISTIC);
00212
00213 if (!query.exec())
00214 MythDB::DBError("Error inserting eit statistics", query);
00215 }
00216
00217
00218 event_map_t * EITCache::LoadChannel(uint chanid)
00219 {
00220 if (!lock_channel(chanid, lastPruneTime))
00221 return NULL;
00222
00223 MSqlQuery query(MSqlQuery::InitCon());
00224
00225 QString qstr =
00226 "SELECT eventid,tableid,version,endtime "
00227 "FROM eit_cache "
00228 "WHERE chanid = :CHANID AND "
00229 " endtime > :ENDTIME AND "
00230 " status = :STATUS";
00231
00232 query.prepare(qstr);
00233 query.bindValue(":CHANID", chanid);
00234 query.bindValue(":ENDTIME", lastPruneTime);
00235 query.bindValue(":STATUS", EITDATA);
00236
00237
00238 if (!query.exec() || !query.isActive())
00239 {
00240 MythDB::DBError("Error loading eitcache", query);
00241 return NULL;
00242 }
00243
00244 event_map_t * eventMap = new event_map_t();
00245
00246 while (query.next())
00247 {
00248 uint eventid = query.value(0).toUInt();
00249 uint tableid = query.value(1).toUInt();
00250 uint version = query.value(2).toUInt();
00251 uint endtime = query.value(3).toUInt();
00252
00253 (*eventMap)[eventid] = construct_sig(tableid, version, endtime, false);
00254 }
00255
00256 if (eventMap->size())
00257 LOG(VB_EIT, LOG_INFO, LOC + QString("Loaded %1 entries for channel %2")
00258 .arg(eventMap->size()).arg(chanid));
00259
00260 entryCnt += eventMap->size();
00261 return eventMap;
00262 }
00263
00264 void EITCache::WriteChannelToDB(uint chanid)
00265 {
00266 event_map_t * eventMap = channelMap[chanid];
00267
00268 if (!eventMap)
00269 {
00270 channelMap.remove(chanid);
00271 return;
00272 }
00273
00274 uint size = eventMap->size();
00275 uint updated = 0;
00276
00277 event_map_t::iterator it = eventMap->begin();
00278 while (it != eventMap->end())
00279 {
00280 if (modified(*it) && extract_endtime(*it) > lastPruneTime)
00281 {
00282 replace_in_db(chanid, it.key(), *it);
00283 updated++;
00284 *it &= ~(uint64_t)0 >> 1;
00285 }
00286 ++it;
00287 }
00288 unlock_channel(chanid, updated);
00289
00290 if (updated)
00291 LOG(VB_EIT, LOG_INFO, LOC + QString("Wrote %1 modified entries of %2 "
00292 "for channel %3 to database.")
00293 .arg(updated).arg(size).arg(chanid));
00294 }
00295
00296 void EITCache::WriteToDB(void)
00297 {
00298 QMutexLocker locker(&eventMapLock);
00299
00300 key_map_t::iterator it = channelMap.begin();
00301 while (it != channelMap.end())
00302 {
00303 WriteChannelToDB(it.key());
00304 ++it;
00305 }
00306 }
00307
00308
00309
00310 bool EITCache::IsNewEIT(uint chanid, uint tableid, uint version,
00311 uint eventid, uint endtime)
00312 {
00313 accessCnt++;
00314
00315 if (accessCnt % 500000 == 50000)
00316 {
00317 LOG(VB_EIT, LOG_INFO, GetStatistics());
00318 WriteToDB();
00319 }
00320
00321
00322 if (endtime < lastPruneTime)
00323 {
00324 prunedHitCnt++;
00325 return false;
00326 }
00327
00328 if (endtime > lastPruneTime + 50 * 86400)
00329 return false;
00330
00331 QMutexLocker locker(&eventMapLock);
00332 if (!channelMap.contains(chanid))
00333 {
00334 channelMap[chanid] = LoadChannel(chanid);
00335 }
00336
00337 if (!channelMap[chanid])
00338 {
00339 wrongChannelHitCnt++;
00340 return false;
00341 }
00342
00343 event_map_t * eventMap = channelMap[chanid];
00344 event_map_t::iterator it = eventMap->find(eventid);
00345 if (it != eventMap->end())
00346 {
00347 if (extract_table_id(*it) > tableid)
00348 {
00349
00350 tblChgCnt++;
00351 }
00352 else if ((extract_table_id(*it) == tableid) &&
00353 ((extract_version(*it) < version) ||
00354 ((extract_version(*it) == kVersionMax) &&
00355 version < kVersionMax)))
00356 {
00357
00358 verChgCnt++;
00359 }
00360 else
00361 {
00362
00363 hitCnt++;
00364 return false;
00365 }
00366 }
00367
00368 eventMap->insert(eventid, construct_sig(tableid, version, endtime, true));
00369 entryCnt++;
00370
00371 return true;
00372 }
00373
00378 uint EITCache::PruneOldEntries(uint timestamp)
00379 {
00380 if (VERBOSE_LEVEL_CHECK(VB_EIT, LOG_INFO))
00381 {
00382 QDateTime tmptime;
00383 tmptime.setTime_t(timestamp);
00384 LOG(VB_EIT, LOG_INFO,
00385 LOC + "Pruning all entries that ended before UTC " +
00386 tmptime.toString(Qt::ISODate));
00387 }
00388
00389 lastPruneTime = timestamp;
00390
00391
00392 WriteToDB();
00393
00394
00395 delete_in_db(timestamp);
00396
00397 return 0;
00398 }
00399
00400
00404 void EITCache::ClearChannelLocks(void)
00405 {
00406 MSqlQuery query(MSqlQuery::InitCon());
00407
00408 QString qstr =
00409 "DELETE FROM eit_cache "
00410 "WHERE status = :STATUS";
00411
00412 query.prepare(qstr);
00413 query.bindValue(":STATUS", CHANNEL_LOCK);
00414
00415 if (!query.exec())
00416 MythDB::DBError("Error clearing channel locks", query);
00417 }
00418
00419