Ticket #1660: asyncdb.13130.patch

File asyncdb.13130.patch, 13.3 KB (added by ajlill@…, 5 years ago)

Updated patch with retry logic

  • libs/libmythtv/NuppelVideoPlayer.cpp

     
    54295429    using_null_videoout = true; 
    54305430 
    54315431    // clear out any existing seektables 
    5432     m_playbackinfo->ClearPositionMap(MARK_KEYFRAME); 
    5433     m_playbackinfo->ClearPositionMap(MARK_GOP_START); 
    5434     m_playbackinfo->ClearPositionMap(MARK_GOP_BYFRAME); 
     5432    m_playbackinfo->ClearPositionMap(MARK_KEYFRAME,true); 
     5433    m_playbackinfo->ClearPositionMap(MARK_GOP_START,true); 
     5434    m_playbackinfo->ClearPositionMap(MARK_GOP_BYFRAME,true); 
    54355435 
    54365436    if (OpenFile() < 0) 
    54375437        return(0); 
  • libs/libmythtv/programinfo.h

     
    237237    // Keyframe positions Map 
    238238    void GetPositionMap(frm_pos_map_t &, int type) const; 
    239239    void ClearPositionMap(int type) const; 
     240    void ClearPositionMap(int type, bool sync) const; 
    240241    void SetPositionMap(frm_pos_map_t &, int type, 
    241242                        long long min_frm = -1, long long max_frm = -1) const; 
    242     void SetPositionMapDelta(frm_pos_map_t &, int type) const; 
     243    void SetPositionMapDelta(frm_pos_map_t &, int type); 
    243244 
    244  
    245245    // GUI stuff 
    246246    void showDetails(void) const; 
    247247    void EditRecording(void); 
     
    344344  private: 
    345345    bool ignoreBookmark; 
    346346    mutable class ScheduledRecording* record; 
     347     
     348    QString inUseForWhat; 
    347349 
    348     QString inUseForWhat; 
    349350}; 
    350351 
    351352/** \class ProgramList 
  • libs/libmythtv/programinfo.cpp

     
    1717#include "util.h" 
    1818#include "mythcontext.h" 
    1919#include "dialogbox.h" 
     20#include "asyncdb.h" 
    2021#include "remoteutil.h" 
    2122#include "jobqueue.h" 
    2223#include "mythdbcon.h" 
     
    18281829{ 
    18291830    filesize = fsize; 
    18301831 
    1831     MSqlQuery query(MSqlQuery::InitCon()); 
    1832     query.prepare("UPDATE recorded SET filesize = :FILESIZE" 
    1833                   " WHERE chanid = :CHANID" 
    1834                   " AND starttime = :STARTTIME ;"); 
    1835     query.bindValue(":FILESIZE", longLongToString(fsize)); 
    1836     query.bindValue(":CHANID", chanid); 
    1837     query.bindValue(":STARTTIME", recstartts); 
     1832    QString query = QString("UPDATE recorded SET filesize = %1" 
     1833                            " WHERE chanid = %2" 
     1834                            " AND starttime = \"%3\" ;") 
     1835      .arg(longLongToString(fsize)) 
     1836      .arg(chanid) 
     1837      .arg(recstartts.toString("yyyyMMddhhmmss")); 
    18381838     
    1839     if (!query.exec() || !query.isActive()) 
    1840         MythContext::DBError("File size update",  
    1841                              query); 
     1839    gAsyncDB->AddCommand(query); 
    18421840} 
    18431841 
    18441842/** \fn ProgramInfo::GetFilesize(void) 
     
    26052603 
    26062604void ProgramInfo::ClearPositionMap(int type) const 
    26072605{ 
     2606    QString query; 
     2607   
     2608    if (isVideo) 
     2609    { 
     2610        query = QString("DELETE FROM filemarkup" 
     2611                        " WHERE filename = \"%1\"") 
     2612        .arg(pathname); 
     2613    } 
     2614    else 
     2615    { 
     2616        query = QString("DELETE FROM recordedseek" 
     2617                      " WHERE chanid = %1" 
     2618                      " AND starttime = \"%2\"") 
     2619        .arg(chanid) 
     2620        .arg(recstartts.toString("yyyyMMddhhmmss")); 
     2621    } 
     2622    query += QString(" AND type = %1 ;").arg(type); 
     2623                                
     2624    /* Hand it to the async thread */ 
     2625    gAsyncDB->AddCommand(query); 
     2626} 
     2627 
     2628void ProgramInfo::ClearPositionMap(int type, bool sync) const 
     2629{ 
    26082630    MSqlQuery query(MSqlQuery::InitCon()); 
    26092631   
    26102632    if (isVideo) 
     
    27102732} 
    27112733 
    27122734void ProgramInfo::SetPositionMapDelta(frm_pos_map_t &posMap, 
    2713                                       int type) const 
     2735                                      int type) 
    27142736{ 
    27152737    QMap<long long, long long>::Iterator i; 
    2716     MSqlQuery query(MSqlQuery::InitCon()); 
     2738    QString insert; 
    27172739 
     2740    if( posMap.isEmpty() ) 
     2741      return; 
     2742 
     2743    if (isVideo) 
     2744      { 
     2745        insert = QString("INSERT INTO filemarkup" 
     2746                         " (filename, mark, type, offset)" 
     2747                         " VALUES"); 
     2748      }  
     2749    else  
     2750      { 
     2751        insert = QString("INSERT INTO recordedseek" 
     2752                         " (chanid, starttime, mark, type, offset)" 
     2753                         " VALUES"); 
     2754      } 
     2755    QString sep = " "; 
     2756 
     2757    /* Assemble these these updates into a single multi-row insert  
     2758     * statement -- much more efficient */ 
    27182759    for (i = posMap.begin(); i != posMap.end(); ++i) 
    27192760    { 
    27202761        long long frame = i.key(); 
    27212762        long long offset = i.data(); 
    27222763 
    27232764        if (isVideo) 
    2724         { 
    2725             query.prepare("INSERT INTO filemarkup" 
    2726                           " (filename, mark, type, offset)" 
    2727                           " VALUES" 
    2728                           " ( :PATH , :MARK , :TYPE , :OFFSET );"); 
    2729             query.bindValue(":PATH", pathname); 
    2730         } 
    2731         else 
    2732         { 
    2733             query.prepare("INSERT INTO recordedseek" 
    2734                           " (chanid, starttime, mark, type, offset)" 
    2735                           " VALUES" 
    2736                           " ( :CHANID , :STARTTIME , :MARK , :TYPE , :OFFSET );"); 
    2737             query.bindValue(":CHANID", chanid); 
    2738             query.bindValue(":STARTTIME", recstartts); 
    2739         } 
    2740         query.bindValue(":MARK", frame); 
    2741         query.bindValue(":TYPE", type); 
    2742         query.bindValue(":OFFSET", offset); 
    2743          
    2744         if (!query.exec() || !query.isActive()) 
    2745             MythContext::DBError("delta position map insert",  
    2746                                  query); 
    2747     } 
     2765          { 
     2766            insert += sep + QString("( \"%1\", %2, %3, %4 )") 
     2767              .arg(pathname).arg(frame).arg(type).arg(offset); 
     2768          } 
     2769          else 
     2770          { 
     2771            insert += sep + QString("( %1 , \"%2\" , %3 , %4 , %5 )") 
     2772              .arg(chanid).arg(recstartts.toString("yyyyMMddhhmmss")) 
     2773              .arg(frame).arg(type).arg(offset); 
     2774          } 
     2775        sep = ","; 
     2776      } 
     2777 
     2778    insert += QString(";"); 
     2779    /* Hand it to the async thread */ 
     2780    gAsyncDB->AddCommand(insert); 
    27482781} 
    27492782 
    27502783/** \fn ProgramInfo::ReactivateRecording(void) 
  • libs/libmyth/exitcodes.h

     
    5757#define BACKEND_BUGGY_EXIT_NO_CAP_CARD            GENERIC_EXIT_START-11 
    5858#define BACKEND_BUGGY_EXIT_NO_CHAN_DATA           GENERIC_EXIT_START-12 
    5959#define BACKEND_EXIT_START                        GENERIC_EXIT_START-12 
     60#define BACKEND_EXIT_ASYNCDB_ERROR                GENERIC_EXIT_START-13 
    6061 
    6162// mythtranscode 
    6263#define TRANSCODE_EXIT_OK                         GENERIC_EXIT_OK 
  • libs/libmyth/libmyth.pro

     
    2020HEADERS += langsettings.h audiooutputnull.h mythsocket.h 
    2121HEADERS += DisplayResScreen.h util-x11.h mythdeque.h qmdcodec.h 
    2222HEADERS += exitcodes.h virtualkeyboard.h mythobservable.h mythevent.h 
     23HEADERS += asyncdb.h 
    2324HEADERS += mythexp.h mythpluginapi.h 
    2425 
    2526SOURCES += dialogbox.cpp lcddevice.cpp mythcontext.cpp mythwidgets.cpp  
     
    3334SOURCES += langsettings.cpp mythdbcon.cpp audiooutputnull.cpp 
    3435SOURCES += DisplayResScreen.cpp util-x11.cpp qmdcodec.cpp 
    3536SOURCES += virtualkeyboard.cpp mythobservable.cpp mythsocket.cpp 
     37SOURCES += asyncdb.cpp 
    3638 
    3739INCLUDEPATH += ../libmythsamplerate ../libmythsoundtouch ../.. ../ 
    3840DEPENDPATH += ../libmythsamplerate ../libmythsoundtouch ../ ../libmythui 
     
    5961inc.files += visual.h volumebase.h output.h langsettings.h qmdcodec.h 
    6062inc.files += exitcodes.h mythconfig.h mythconfig.mak virtualkeyboard.h 
    6163inc.files += mythevent.h mythobservable.h mythsocket.h 
     64inc.files += asyncdb.h 
    6265inc.files += mythexp.h mythpluginapi.h 
    6366 
    6467using_oss { 
  • libs/libmyth/asyncdb.h

     
     1#ifndef ASYNCDB_H_ 
     2#define ASYNCDB_H_ 
     3 
     4// ANSI C headers 
     5#include <cstdio> 
     6#include <cstdlib> 
     7#include <cerrno> 
     8 
     9#include "mythdbcon.h" 
     10 
     11#include <qstring.h> 
     12 
     13using namespace std; 
     14 
     15class AsyncDB 
     16{ 
     17 public: 
     18    AsyncDB(void); 
     19    ~AsyncDB(); 
     20    bool Init(void); 
     21    void AddCommand(QString);   
     22 
     23 private: 
     24    pthread_t thread; 
     25    QStringList list; 
     26    QMutex listLock; 
     27    int retry; 
     28    bool threadRunning; 
     29 
     30 protected: 
     31    static void *StartThread(void *); 
     32    void Worker(void); 
     33}; 
     34 
     35extern AsyncDB *gAsyncDB; 
     36#endif 
  • libs/libmyth/asyncdb.cpp

     
     1#include "asyncdb.h" 
     2#include <unistd.h> 
     3#include <sys/time.h> 
     4#include <sys/resource.h> 
     5#include <sys/mman.h> 
     6 
     7AsyncDB *gAsyncDB = NULL; 
     8#define ADB QString("AsyncDB: ") 
     9 
     10/** \class AsyncDB 
     11 *  \brief This class supports asynchronous database inserts. 
     12 * 
     13 *   This class allows us to toss those database queries for which 
     14 *   we do not require values or status to be run by a separate thread. 
     15 *   This helps the enocder threads to keep up with the datastream. 
     16 */ 
     17 
     18/** \fn AsyncDB 
     19 *  \brief Initialize the class 
     20 */ 
     21AsyncDB::AsyncDB(void): 
     22  retry(0), threadRunning(false) 
     23{ 
     24  list.clear(); 
     25} 
     26 
     27/** \fn Init() 
     28 *  \brief Starts the thread 
     29 * 
     30 *   Create a thread and return false if this fails 
     31 */ 
     32bool AsyncDB::Init(void) 
     33{ 
     34  int rv; 
     35  if(  ( rv = pthread_create(&thread, NULL, StartThread, this) ) ) { 
     36    VERBOSE(VB_IMPORTANT, ADB + QString("Can't start thread")); 
     37    return false; 
     38  } 
     39  return(true); 
     40} 
     41 
     42/** \fn StartThread(AsyncDB *) 
     43 *  \brief Runs the worker function 
     44 * 
     45 *   Drop the priority on this thread and invoke the worker function 
     46 */ 
     47void *AsyncDB::StartThread(void *wotzit) 
     48{ 
     49  AsyncDB *pi = (AsyncDB *)wotzit; 
     50  VERBOSE(VB_IMPORTANT,QString("Starting async db thread")); 
     51  // Loser priority, to avoid problems with recordings. 
     52  pi->threadRunning = true; 
     53  setpriority(PRIO_PROCESS, 0, 3); 
     54  do { 
     55    pi->Worker(); 
     56  } while(pi->retry <= 1 );     // A second retry means we're hosed 
     57  pi->threadRunning = false; 
     58  return(NULL); 
     59} 
     60 
     61/** \fn AddCommand(QString) 
     62 *  \brief Adds a database command to the list 
     63 * 
     64 *  \param QString   The sql command 
     65 */ 
     66void AsyncDB::AddCommand(QString cmd) 
     67{ 
     68  if( threadRunning ) { 
     69    listLock.lock(); 
     70    list.append(cmd); 
     71    listLock.unlock(); 
     72  } 
     73} 
     74 
     75/** \fn ~AsyncDB 
     76 *  \brief Shut down the thread 
     77 * 
     78 *   Add a "done" command to the list to make the thread  
     79 *   shutdown and reap it. 
     80 */ 
     81AsyncDB::~AsyncDB(void) 
     82{ 
     83  AddCommand(QString("done")); 
     84  pthread_join(thread, NULL); 
     85  VERBOSE(VB_IMPORTANT,QString("Ending async db thread")); 
     86} 
     87 
     88/** \fn Worker() 
     89 *  \brief Run the lists of commands 
     90 * 
     91 *   Swap the list for an empty one and call ListRunner 
     92 *   to execute the commands. 
     93 */ 
     94void AsyncDB::Worker(void) 
     95{ 
     96  bool done = false; 
     97  bool errored = false; 
     98  MSqlQuery query(MSqlQuery::InitCon()); 
     99 
     100  while( ! done && ! errored ) { 
     101    if( list.empty() ) { 
     102      sleep(1); 
     103    } else { 
     104      listLock.lock(); 
     105      QStringList mylist = list; 
     106      list.clear(); 
     107      listLock.unlock(); 
     108      for ( QStringList::Iterator it = mylist.begin(); it != mylist.end(); ++it ) { 
     109        if( *it == QString("done") ) { 
     110          done = true; 
     111          retry = 2;                    // Make calling function exit. 
     112          break; 
     113        } 
     114        if( errored ) { 
     115          /* If we experienece a database error, we stuff the commands back onto the list, 
     116             and exit this function. We get called again which refreshes the DB connection. 
     117             We count these retries, so we don't loop, but only do it once without some 
     118             success */ 
     119          AddCommand(*it); 
     120        } else { 
     121          query.prepare(*it); 
     122          if (!query.exec() || !query.isActive()) { 
     123            MythContext::DBError("delta position map insert",  
     124                                 query); 
     125            retry++; 
     126            errored = true; 
     127            AddCommand(*it); 
     128          } else 
     129            retry = 0; 
     130        } 
     131      } 
     132      mylist.clear();  
     133    } 
     134  } 
     135} 
     136 
     137 
  • programs/mythtranscode/main.cpp

     
    561561{ 
    562562    if (pginfo && ! mapfile) 
    563563    { 
    564         pginfo->ClearPositionMap(MARK_KEYFRAME); 
    565         pginfo->ClearPositionMap(MARK_GOP_START); 
     564        pginfo->ClearPositionMap(MARK_KEYFRAME, true); 
     565        pginfo->ClearPositionMap(MARK_GOP_START, true); 
    566566        pginfo->SetPositionMap(posMap, MARK_GOP_BYFRAME); 
    567567    } 
    568568    else if (mapfile) 
  • programs/mythbackend/main.cpp

     
    3535#include "libmythtv/dbcheck.h" 
    3636#include "libmythtv/jobqueue.h" 
    3737#include "libmythtv/storagegroup.h" 
     38#include "libmyth/asyncdb.h" 
    3839 
    3940#include "mediaserver.h" 
    4041#include "httpstatus.h" 
     
    199200 
    200201void cleanup(void)  
    201202{ 
     203    delete gAsyncDB; 
    202204    delete gContext; 
    203205 
    204206    if (sched) 
     
    518520    }     
    519521    gContext->ActivateSettingsCache(true); 
    520522 
     523    gAsyncDB = new AsyncDB(); 
     524    if( gAsyncDB->Init() == false ) 
     525      { 
     526        VERBOSE(VB_IMPORTANT, "Couldn't start async database thread"); 
     527        return BACKEND_EXIT_ASYNCDB_ERROR; 
     528      } 
     529 
    521530    if (printsched || testsched) 
    522531    { 
    523532        gContext->SetBackend(false);