|
MythTV
0.26-pre
|
00001 // -*- Mode: c++ -*- 00002 /* 00003 * Copyright 2006 (C) Stuart Auchterlonie <stuarta at squashedfrog.net> 00004 * Copyright 2006 (C) Janne Grunau <janne-mythtv at grunau.be> 00005 * License: GPL v2 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 // Highest version number. version is 5bits 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 // 24 hours ago 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 // inserting statistics 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; // mark as synced 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 // don't readd pruned entries 00322 if (endtime < lastPruneTime) 00323 { 00324 prunedHitCnt++; 00325 return false; 00326 } 00327 // validity check, reject events with endtime over 7 weeks in the future 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 // EIT from lower (ie. better) table number 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 // EIT updated version on current table 00358 verChgCnt++; 00359 } 00360 else 00361 { 00362 // EIT data previously seen 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 // Write all modified entries to DB and start with a clean cache 00392 WriteToDB(); 00393 00394 // Prune old entries in the DB 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 /* vim: set expandtab tabstop=4 shiftwidth=4: */
1.7.6.1