Example #1
0
bool HeaderReadWorker::cacheFlush(uint size)
{
    Dbt key, data;
    int ret;
    RawHeader* h = 0;
    QString cIndex;
    NewsGroup* ng = job->ng;
    Db* pdb = ng->getPartsDb(); // get the parts Db
    MultiPartHeader* mph = 0;
    SinglePartHeader* sph = 0;
    HeaderBase* hb = 0;
    quint16 capPart = 0;
    quint16 capTotal = 0;

    if (size == 0)
        size = headerCache.count();

    qDebug() << "Flushing cache";
    qDebug() << "cacheIndex is " << headerCache.count() << " .Flushing " << size
            << " records";

    //Flush "size" elements, with a FIFO policy
    if ((int) size > headerCache.count())
    {
        qDebug() << "wrong flush size!";
        size = headerCache.count();
    }

    uint partsAdded = size;

    QMutexLocker locker(headerDbLock);

    quint64 mPHKey = ng->getMultiPartSequence();
    //qDebug() << "Multi Part Sequence in cache is " << mPHKey << " for host " << hostId;

    for (uint i = 0; i < size; i++)
    {
        h = headerCache.dequeue();

        if (h == 0) // didn't exist
        {
            //qDebug() << "Failed to find header in cache flush !! i = " << i << ", size = " << size;
            continue;
        }

        pos = 0;
        capPart = 0;
        capTotal = 0;
        index = -1;

        //qDebug() << "Subject in flush is " << h->m_subj;

        while ((pos = rx.indexIn(h->m_subj, pos)) != -1)
        {
            index = pos;
            pos += rx.matchedLength();
            capPart = rx.cap(1).toInt();
            capTotal = rx.cap(2).toInt();
        }

        if (index == -1) // It looks like a single part header
        {
            cIndex = h->m_subj.left(index).simplified() % '\n' % h->m_mid; // This is the common part of the subject + separator + msgId

            sph = (SinglePartHeader*)(cache.value(cIndex));
            if (sph) // This is a duplicate (We've already seen this subj and msgId for this server ...
            {
                partsAdded--;
                //qDebug() << "Duplicate article in cache : " << sph->getSubj() << "for server " << hostId;
            }
            else
            {
                //Try to get the header from the db...
                sph = (SinglePartHeader*)dbBinHeaderGet(cIndex);
                if (sph)
                {
                    if (!sph->isServerPresent(hostId)) // Article not currently registered to this server
                    {
                        sph->setServerPartNum(hostId, h->m_num);
                        cache.insert(cIndex, sph);
                        //qDebug() << "Server " << hostId << ", db find";
                    }
                    else // This is a duplicate (We've already seen this subj and msgId for this server ...
                    {
                        partsAdded--;
                        //qDebug() << "Duplicate article in db : " << sph->getSubj() << "for server " << hostId;
                    }
                }
                else
                {
                    //qDebug() << "Server " << hostId << ", sph create";

                    //Create new header and put it in the cache...
                    sph = new SinglePartHeader;
                    groupArticles++;
                    //The new article is, of course, unread...
                    unreadArticles++;

                    sph->setSubj(h->m_subj.left(index).simplified());
                    sph->setFrom(h->m_from);
                    sph->setStatus(MultiPartHeader::bh_new);
                    sph->setLines(h->m_lines.toLong());
                    sph->setSize(h->m_bytes.toLongLong());

                    if (h->m_date.isNull())
                        qDebug() << "Date is null!!!";
                    else
                        sph->setPostingDate(createDateTime(h->m_date.split(" ")));

                    sph->setDownloadDate(QDateTime::currentDateTime().toTime_t());

                    sph->setMessageId(h->m_mid);
                    sph->setServerPartNum(hostId, h->m_num);

                    cache.insert(cIndex, sph);

                    ng->setHeadersNeedGrouping(true);
                }
            }
        }
        else
        {
            cIndex = h->m_subj.left(index).simplified() % '\n' % h->m_from; // This is the common part of the subject + separator + sender

            mph = (MultiPartHeader*)(cache.value(cIndex));

            if (mph == 0)
            {
                //Try to get the header from the db...
                //mph = dynamic_cast<MultiPartHeader*>(dbBinHeaderGet(db, cIndex));
                mph = (MultiPartHeader*)(dbBinHeaderGet(cIndex));
                if (mph)
                {
                    //qDebug() << "Server " << hostId << ", db find";
                    //qDebug() << "Using mph with rec type " << mph->getHeaderType();
                    //qDebug() << "Using mph with key " << mph->multiPartKey;

                    //update the header in the cache...
                    switch (mph->addPart(pdb, capPart, h, hostId))
                    {
                        case MultiPartHeader::Duplicate_Part:
                        case MultiPartHeader::Error_Part:
                            partsAdded--;
                            break;
                        case MultiPartHeader::Unread_Status:
                            //Added a part that changed the status of the post...
                            unreadArticles++;
                            break;
                        case MultiPartHeader::New_Part:
                        case MultiPartHeader::Updated_Part:
                            break;
                    }
                    cache.insert(cIndex, mph);
                }
            }
            else
            {
                //qDebug() << "Server " << hostId << ", cache find";
                //qDebug() << "Using mph with key " << mph->multiPartKey;

                //update the header in the cache...
                switch (mph->addPart(pdb, capPart, h, hostId))
                {
                    case MultiPartHeader::Duplicate_Part:
                    case MultiPartHeader::Error_Part:
                        partsAdded--;
                        break;
                    case MultiPartHeader::Unread_Status:
                        //Added a part that changed the status of the post...
                        unreadArticles++;
                        break;
                    case MultiPartHeader::New_Part:
                    case MultiPartHeader::Updated_Part:
                        break;
                }
            }

            if (mph == 0)
            {
                //qDebug() << "Server " << hostId << ", mph create";

                //Create new header and put it in the cache...
                mph = new MultiPartHeader;
                groupArticles++;
                //The new article is, of course, unread...
                unreadArticles++;

                mph->setSubj(h->m_subj.left(index).simplified());
                mph->setFrom(h->m_from);
                mph->setStatus(MultiPartHeader::bh_new);

                mph->setKey(++mPHKey);
                //qDebug() << "Created mph with key " << mPHKey;

                if (h->m_date.isNull())
                    qDebug() << "Date is null!!!";
                else
                    mph->setPostingDate(createDateTime(h->m_date.split(" ")));

                mph->setDownloadDate(QDateTime::currentDateTime().toTime_t());
                mph->setNumParts(capTotal); // Also sets missing parts to capTotal
                mph->addPart(pdb, capPart, h, hostId);

                cache.insert(cIndex, mph);
                ng->setHeadersNeedGrouping(true);
            }
        }

        Q_DELETE(h);
    }

    // Multiple threads are still controlled by mutex at this point

    ng->servLocalParts[hostId] += size;
    qDebug() << "Server " << hostId << ", total articles = " << ng->totalArticles << ", adding " << groupArticles;
    ng->totalArticles += groupArticles;
    ng->unreadArticles += unreadArticles;
    qDebug() << "Server " << hostId << ", total articles = " << ng->totalArticles;
    ng->setMultiPartSequence(mPHKey);

    groupArticles = 0;
    unreadArticles = 0;

    // sync the parts db
    pdb->sync(0);

    //Flush the cache that we've just built
    QHash<QString, HeaderBase*>::iterator it = cache.begin();

    QByteArray ba;
    const char *cIndexCharArr = 0;

    while (it != cache.end())
    {
        cIndex = it.key();
        hb = (HeaderBase*)(it.value());
        if (hb->getHeaderType() == 'm')
        {
            mph = (MultiPartHeader*)(it.value());
            data.set_data(mph->data());
            data.set_size(mph->getRecordSize());
            //qDebug() << "Just saved mph with key " << mph->multiPartKey;
        }
        else
        {
            sph = (SinglePartHeader*)(it.value());
            data.set_data(sph->data());
            data.set_size(sph->getRecordSize());
        }

        ba = cIndex.toLocal8Bit();
        cIndexCharArr = ba.constData();
        key.set_data((void*) cIndexCharArr);
        key.set_size(cIndex.length());
        ret = db->put(NULL, &key, &data, 0);
        if (ret != 0)
        {
            qDebug() << "Error flushing cache: " << ret;
            // errorString=g_dbenv->strerror(ret);
            errorString = "Failure whilst writing header record to database";
            return false;
        }

        void *ptr = data.get_data();
        Q_FREE(ptr);
        if (hb->getHeaderType() == 'm')
        {
            Q_DELETE(mph);
        }
        else
        {
            Q_DELETE(sph);
        }
        ++it;
    }

    locker.unlock(); // *************** this is a massive hold ***********************

    cache.clear();

    qDebug() << "Ok, cache flushed";

    cacheFlushed = true;

    return true;
}