Ticket #1835: 1835-v5.patch

File 1835-v5.patch, 10.6 KB (added by danielk, 6 years ago)

Updated patch (sets a minimum delete rate to 8 MB/s)

  • programs/mythbackend/mainserver.cpp

     
    9999 
    100100}; 
    101101 
     102QMutex MainServer::truncate_and_close_lock; 
     103 
    102104class ProcessRequestThread : public QThread 
    103105{ 
    104106  public: 
     
    13831385    if (tvchain) 
    13841386        tvchain->DeleteProgram(pginfo); 
    13851387 
    1386     int err; 
    13871388    bool followLinks = gContext->GetNumSetting("DeletesFollowLinks", 0); 
    13881389 
    13891390    /* Delete recording. */ 
    1390     err = deleteFile(ds->filename, followLinks, false); 
     1391    int fd = DeleteFile(ds->filename, followLinks); 
    13911392     
    13921393    sleep(2); 
    13931394 
    1394     if (checkFile.exists()) 
     1395    if ((fd < 0) && checkFile.exists()) 
    13951396    { 
    13961397        VERBOSE(VB_IMPORTANT, 
    13971398            QString("Error deleting file: %1. Keeping metadata in database.") 
     
    14111412    } 
    14121413 
    14131414    /* Delete preview thumbnail. */ 
    1414     err = deleteFile(ds->filename + ".png", followLinks, true); 
     1415    deleteFile(ds->filename + ".png", followLinks, true); 
    14151416 
    14161417    MSqlQuery query(MSqlQuery::InitCon()); 
    14171418    query.prepare("DELETE FROM recorded WHERE chanid = :CHANID AND " 
     
    14951496    delete pginfo; 
    14961497 
    14971498    deletelock.unlock(); 
     1499 
     1500    if (fd != -1) 
     1501    { 
     1502        m_expirer->TruncatePending(); 
     1503        TruncateAndClose(m_expirer, fd, ds->filename); 
     1504        m_expirer->TruncateFinished(); 
     1505    } 
    14981506} 
    14991507 
     1508/* Return fd for success, negative number for error. */ 
     1509int MainServer::DeleteFile(const QString &filename, bool followLinks) 
     1510{ 
     1511    QFileInfo finfo(filename); 
     1512    int fd = -1, err = 0; 
     1513 
     1514    VERBOSE(VB_FILE, QString("About to unlink/delete file: '%1'") 
     1515            .arg(filename)); 
     1516 
     1517    QString errmsg = QString("Delete Error '%1'").arg(filename.local8Bit()); 
     1518    if (finfo.isSymLink()) 
     1519        errmsg += QString(" -> '%2'").arg(finfo.readLink().local8Bit()); 
     1520 
     1521    if (followLinks && finfo.isSymLink()) 
     1522    { 
     1523        if (followLinks) 
     1524            fd = OpenAndUnlink(finfo.readLink()); 
     1525        if (fd >= 0) 
     1526            err = unlink(filename.local8Bit()); 
     1527    } 
     1528    else if (!finfo.isSymLink()) 
     1529    { 
     1530        fd = OpenAndUnlink(filename); 
     1531    } 
     1532    else // just delete symlinks immediately 
     1533    { 
     1534        err = unlink(filename.local8Bit()); 
     1535        if (err == 0) 
     1536            return -1; // no error 
     1537    } 
     1538 
     1539    if (fd < 0) 
     1540        VERBOSE(VB_IMPORTANT, errmsg + ENO); 
     1541 
     1542    return fd; 
     1543} 
     1544 
     1545/** \fn MainServer::OpenAndUnlink(const QString&) 
     1546 *  \brief Opens a file, unlinks it and returns the file descriptor. 
     1547 */ 
     1548int MainServer::OpenAndUnlink(const QString &filename) 
     1549{ 
     1550    QString msg = QString("Error deleting '%1'").arg(filename.local8Bit()); 
     1551    int fd = open(filename.local8Bit(), O_WRONLY); 
     1552 
     1553    if (fd == -1) 
     1554    { 
     1555        VERBOSE(VB_IMPORTANT, msg + " could not open " + ENO); 
     1556        return -1; 
     1557    } 
     1558     
     1559    if (unlink(filename.local8Bit())) 
     1560    { 
     1561        VERBOSE(VB_IMPORTANT, msg + " could not unlink " + ENO); 
     1562        close(fd); 
     1563        return -1; 
     1564    } 
     1565 
     1566    return fd; 
     1567} 
     1568 
     1569/** \fn MainServer::TruncateAndClose(const AutoExpire*,int,const QString&) 
     1570 *  \brief Repeatedly truncate an open file in small increments. 
     1571 * 
     1572 *   When the file is small enough this closes the file and returns. 
     1573 * 
     1574 *   NOTE: This aquires a lock so that only one instance of TruncateAndClose() 
     1575 *         is running at a time. 
     1576 */ 
     1577bool MainServer::TruncateAndClose(const AutoExpire *expirer, 
     1578                                  int fd, const QString &filename) 
     1579{ 
     1580    QMutexLocker locker(&truncate_and_close_lock); 
     1581 
     1582    // Time between truncation steps in milliseconds 
     1583    const size_t sleep_time = 500; 
     1584    const size_t min_tps    = 8 * 1024 * 1024; 
     1585    const size_t min_trunc  = (size_t) (min_tps * (sleep_time * 0.001f)); 
     1586 
     1587    // Compute the truncate increment such that we delete 
     1588    // 20% faster than the maximum recording rate. 
     1589    size_t increment; 
     1590    increment = (expirer->GetMinTruncateRate() * sleep_time + 999) / 1000; 
     1591    increment = max(increment, min_trunc); 
     1592 
     1593    VERBOSE(VB_FILE, 
     1594            QString("Truncating '%1' by %2 MB every %3 milliseconds") 
     1595            .arg(filename) 
     1596            .arg(increment / (1024.0 * 1024.0), 0, 'f', 2) 
     1597            .arg(sleep_time)); 
     1598 
     1599    const QString err_msg = QString("Error truncating '%1'").arg(filename); 
     1600 
     1601    // Get the on disk file size and disk block size. 
     1602    struct stat buf; 
     1603    fstat(fd, &buf); 
     1604    size_t fsize = buf.st_blksize * buf.st_blocks; 
     1605 
     1606    // Round truncate increment up to a blocksize, w/min of 1 block. 
     1607    increment = ((increment / buf.st_blksize) + 1) * buf.st_blksize; 
     1608 
     1609    while (fsize > increment) 
     1610    { 
     1611        fsize -= increment; 
     1612 
     1613        //VERBOSE(VB_FILE, QString("Truncating '%1' to %2 MB") 
     1614        //        .arg(filename).arg(fsize / (1024.0 * 1024.0), 0, 'f', 2)); 
     1615 
     1616        int err = ftruncate(fd, fsize); 
     1617        if (err) 
     1618        { 
     1619            VERBOSE(VB_IMPORTANT, err_msg + ENO); 
     1620            return 0 == close(fd); 
     1621        } 
     1622 
     1623        usleep(sleep_time * 1000); 
     1624    } 
     1625 
     1626    bool ok = (0 == close(fd)); 
     1627 
     1628    usleep((sleep_time * fsize * 1000) / increment); 
     1629 
     1630    VERBOSE(VB_FILE, QString("Finished truncating '%1'").arg(filename)); 
     1631 
     1632    return ok; 
     1633} 
     1634 
    15001635void MainServer::HandleCheckRecordingActive(QStringList &slist,  
    15011636                                            PlaybackSock *pbs) 
    15021637{ 
  • programs/mythbackend/mainserver.h

     
    148148    void AddToChains(LiveTVChain *chain); 
    149149    void DeleteChain(LiveTVChain *chain); 
    150150 
     151    static int  DeleteFile(const QString &filename, bool followLinks); 
     152    static int  OpenAndUnlink(const QString &filename); 
     153    static bool TruncateAndClose(const AutoExpire *expirer, 
     154                                 int fd, const QString &filename); 
     155 
    151156    QPtrList<LiveTVChain> liveTVChains; 
    152157    QMutex liveTVChainsLock; 
    153158 
     
    187192    QValueList<DeferredDeleteStruct> deferredDeleteList; 
    188193 
    189194    QTimer *autoexpireUpdateTimer; 
     195 
     196    static QMutex truncate_and_close_lock; 
    190197}; 
    191198 
    192199#endif 
  • programs/mythbackend/autoexpire.cpp

     
    5353 */ 
    5454AutoExpire::AutoExpire(bool runthread, bool master) 
    5555    : record_file_prefix("/"), desired_space(3*1024*1024), 
    56       desired_freq(10), expire_thread_running(runthread), 
    57       is_master_backend(master), update_pending(false)     
     56      desired_freq(10), max_record_rate(5*1024*1024), 
     57      expire_thread_running(runthread), is_master_backend(master), 
     58      truncates_pending(0), update_pending(false) 
    5859{ 
    5960    if (runthread) 
    6061    { 
     
    179180    instance_lock.lock(); 
    180181    desired_space      = expireMinKB; 
    181182    desired_freq       = expireFreq; 
     183    max_record_rate    = (totalKBperMin * 1024)/60; 
    182184    record_file_prefix = recordFilePrefix; 
    183185    instance_lock.unlock(); 
    184186    DBG_CALC_PARAM("CalcParams() -- end"); 
     
    227229 */ 
    228230void AutoExpire::RunExpirer(void) 
    229231{ 
    230     QTime curTime; 
    231232    QTime timer; 
     233    QDateTime curTime; 
     234    QDateTime next_expire = QDateTime::currentDateTime().addSecs(60); 
    232235 
    233236    // wait a little for main server to come up and things to settle down 
    234237    sleep(20); 
     
    243246 
    244247        UpdateDontExpireSet(); 
    245248 
    246         curTime = QTime::currentTime(); 
     249        curTime = QDateTime::currentDateTime(); 
    247250 
    248251        // Expire Short LiveTV files for this backend every 2 minutes 
    249         if ((curTime.minute() % 2) == 0) 
     252        if ((curTime.time().minute() % 2) == 0) 
    250253            ExpireLiveTV(emShortLiveTVPrograms); 
    251254 
    252255        // Expire normal recordings depending on frequency calculated 
    253         if ((curTime.minute() % desired_freq) == 0) 
     256        if (curTime >= next_expire) 
    254257        { 
     258            // Wait for all pending truncates to finish 
     259            WaitForPendingTruncates(); 
     260 
     261            next_expire = 
     262                QDateTime::currentDateTime().addSecs(desired_freq * 60); 
     263 
    255264            ExpireLiveTV(emNormalLiveTVPrograms); 
    256265 
    257266            if (is_master_backend) 
     
    281290    } 
    282291} 
    283292 
     293void AutoExpire::TruncatePending(void) 
     294{ 
     295    QMutexLocker locker(&truncate_monitor_lock); 
     296 
     297    truncates_pending++; 
     298 
     299    VERBOSE(VB_FILE, LOC<<truncates_pending<<" file truncates are pending"); 
     300} 
     301 
     302void AutoExpire::TruncateFinished(void) 
     303{ 
     304    QMutexLocker locker(&truncate_monitor_lock); 
     305 
     306    truncates_pending--; 
     307 
     308    if (truncates_pending <= 0) 
     309    { 
     310        VERBOSE(VB_FILE, LOC + "All file truncates have finished"); 
     311        truncate_monitor_condition.wakeAll(); 
     312    } 
     313} 
     314 
     315void AutoExpire::WaitForPendingTruncates(void) 
     316{ 
     317    QMutexLocker locker(&truncate_monitor_lock); 
     318    while (truncates_pending > 0) 
     319    { 
     320        VERBOSE(VB_FILE, LOC + "Waiting for pending file truncates"); 
     321        truncate_monitor_condition.wait(&truncate_monitor_lock); 
     322    } 
     323} 
     324 
    284325/** \fn AutoExpire::ExpireLiveTV(int type) 
    285326 *  \brief This expires LiveTV programs. 
    286327 */ 
  • programs/mythbackend/autoexpire.h

     
    99 
    1010#include <qmap.h>  
    1111#include <qmutex.h> 
     12#include <qwaitcondition.h> 
    1213#include <qobject.h> 
    1314 
    1415using namespace std; 
     
    3536    void CalcParams(vector<EncoderLink*>); 
    3637    void FillExpireList(); 
    3738    void PrintExpireList(); 
     39    void TruncatePending(void); 
     40    void TruncateFinished(void); 
     41 
     42    size_t GetMaxRecordRate(void) const 
     43        { return max_record_rate; } 
     44 
     45    size_t GetMinTruncateRate(void) const 
     46        { return ((max_record_rate * 6) / 5) + 1; } 
     47 
    3848    void GetAllExpiring(QStringList &strList); 
    3949    void GetAllExpiring(pginfolist_t &list); 
    4050 
     
    5363                            bool deleteAll = false); 
    5464    void ClearExpireList(void); 
    5565    void Sleep(int sleepTime); 
     66    void WaitForPendingTruncates(void); 
    5667 
    5768    void UpdateDontExpireSet(void); 
    5869    bool IsInDontExpireSet(QString chanid, QDateTime starttime); 
     
    6677    QString       record_file_prefix; 
    6778    size_t        desired_space; 
    6879    uint          desired_freq; 
     80    size_t        max_record_rate; // bytes/sec 
    6981    bool          expire_thread_running; 
    7082    bool          is_master_backend; 
    7183 
     84    // Pending truncates monitor 
     85    mutable QMutex truncate_monitor_lock; 
     86    QWaitCondition truncate_monitor_condition; 
     87    int            truncates_pending; 
     88 
    7289    // update info 
    7390    bool          update_pending; 
    7491    pthread_t     update_thread;