Ticket #1835: 1835-v5.patch

File 1835-v5.patch, 10.6 KB (added by danielk, 13 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;