Switch to unified view

a/src/mediaserver/cdplugins/plgwithslave.cxx b/src/mediaserver/cdplugins/plgwithslave.cxx
...
...
339
    // We return the total match size, the count of actually returned
339
    // We return the total match size, the count of actually returned
340
    // entries can be obtained from the vector
340
    // entries can be obtained from the vector
341
    return decoded.size();
341
    return decoded.size();
342
}
342
}
343
343
344
345
class ContentCacheEntry {
346
public:
347
    ContentCacheEntry()
348
        : m_time(time(0)) {
349
    }
350
    int toResult(const string& classfilter, int stidx, int cnt,
351
                 vector<UpSong>& entries) const;
352
    time_t m_time;
353
    vector<UpSong> m_results;
354
};
355
356
int ContentCacheEntry::toResult(const string& classfilter, int stidx, int cnt,
357
                                vector<UpSong>& entries) const
358
{
359
    const vector<UpSong>& res = m_results;
360
    LOGDEB0("searchCacheEntryToResult: filter " << classfilter << " start " <<
361
            stidx << " cnt " << cnt << " res.size " << res.size() << endl);
362
    entries.reserve(cnt);
363
    int total = 0;
364
    for (unsigned int i = 0; i < res.size(); i++) {
365
        if (!classfilter.empty() && res[i].upnpClass.find(classfilter) != 0) {
366
            continue;
367
        }
368
        total++;
369
        if (stidx > int(i)) {
370
            continue;
371
        }
372
        if (int(entries.size()) >= cnt) {
373
            break;
374
        }
375
        LOGDEB1("ContentCacheEntry::toResult: pushing class " <<
376
                res[i].upnpClass << " tt " << res[i].title << endl);
377
        entries.push_back(res[i]);
378
    }
379
    return total;
380
}
381
382
class ContentCache {
383
public:
384
    ContentCache(int retention_secs = 300);
385
    ContentCacheEntry *get(const string& query);
386
    void set(const string& query, ContentCacheEntry &entry);
387
    void purge();
388
private:
389
    time_t m_lastpurge;
390
    int m_retention_secs;
391
    unordered_map<string, ContentCacheEntry> m_cache;
392
};
393
394
ContentCache::ContentCache(int retention_secs)
395
    : m_lastpurge(time(0)), m_retention_secs(retention_secs)
396
{
397
}
398
399
void ContentCache::purge()
400
{
401
    time_t now(time(0));
402
    if (now - m_lastpurge < 5) {
403
        return;
404
    }
405
    for (auto it = m_cache.begin(); it != m_cache.end(); ) {
406
        if (now - it->second.m_time > m_retention_secs) {
407
            LOGDEB0("ContentCache::purge: erasing " << it->first << endl);
408
            it = m_cache.erase(it);
409
        } else {
410
            it++;
411
        }
412
    }
413
    m_lastpurge = now;
414
}
415
416
ContentCacheEntry *ContentCache::get(const string& key)
417
{
418
    purge();
419
    auto it = m_cache.find(key);
420
    if (it != m_cache.end()) {
421
        LOGDEB0("ContentCache::get: found " << key << endl);
422
        // we return a copy of the vector. Make our multi-access life simpler...
423
        return new ContentCacheEntry(it->second);
424
    }
425
    LOGDEB0("ContentCache::get: not found " << key << endl);
426
    return nullptr;
427
}
428
429
void ContentCache::set(const string& key, ContentCacheEntry &entry)
430
{
431
    LOGDEB0("ContentCache::set: " << key << endl);
432
    m_cache[key] = entry;
433
}
434
435
// Cache for searches
436
static ContentCache o_scache(300);
437
// Cache for browsing
438
static ContentCache o_bcache(180);
439
344
// Better return a bogus informative entry than an outright error:
440
// Better return a bogus informative entry than an outright error:
345
static int errorEntries(const string& pid, vector<UpSong>& entries)
441
static int errorEntries(const string& pid, vector<UpSong>& entries)
346
{
442
{
347
    entries.push_back(
443
    entries.push_back(
348
        UpSong::item(pid + "$bogus", pid,
444
        UpSong::item(pid + "$bogus", pid,
349
                     "Service login or communication failure"));
445
                     "Service login or communication failure"));
350
    return 1;
446
    return 1;
351
}
447
}
352
448
449
// Note that the offset and count don't get to the plugin for
450
// now. Plugins just return a (plugin-dependant) fixed number of
451
// entries from offset 0, which we cache. There is no good reason for
452
// this, beyond the fact that we have to cap the entry count anyway,
453
// else the CP is going to read to the end which might be
454
// reaaaaalllllyyyy long.
353
int PlgWithSlave::browse(const string& objid, int stidx, int cnt,
455
int PlgWithSlave::browse(const string& objid, int stidx, int cnt,
354
                         vector<UpSong>& entries,
456
                         vector<UpSong>& entries,
355
                         const vector<string>& sortcrits,
457
                         const vector<string>& sortcrits,
356
                         BrowseFlag flg)
458
                         BrowseFlag flg)
357
{
459
{
...
...
369
    default:
471
    default:
370
        sbflg = "children";
472
        sbflg = "children";
371
        break;
473
        break;
372
    }
474
    }
373
475
476
    string cachekey(m_name + ":" + objid);
477
    if (flg == CDPlugin::BFChildren) {
478
        // Check cache
479
        ContentCacheEntry *cep;
480
        if ((cep = o_bcache.get(cachekey)) != nullptr) {
481
            int total = cep->toResult("", stidx, cnt, entries);
482
            delete cep;
483
            return total;
484
        }
485
    }
486
    
374
    unordered_map<string, string> res;
487
    unordered_map<string, string> res;
375
    if (!m->cmd.callproc("browse", {{"objid", objid}, {"flag", sbflg}}, res)) {
488
    if (!m->cmd.callproc("browse", {{"objid", objid}, {"flag", sbflg}}, res)) {
376
    LOGERR("PlgWithSlave::browse: slave failure\n");
489
    LOGERR("PlgWithSlave::browse: slave failure\n");
377
    return errorEntries(objid, entries);
490
    return errorEntries(objid, entries);
378
    }
491
    }
...
...
380
    auto it = res.find("entries");
493
    auto it = res.find("entries");
381
    if (it == res.end()) {
494
    if (it == res.end()) {
382
    LOGERR("PlgWithSlave::browse: no entries returned\n");
495
    LOGERR("PlgWithSlave::browse: no entries returned\n");
383
        return errorEntries(objid, entries);
496
        return errorEntries(objid, entries);
384
    }
497
    }
498
499
    if (flg == CDPlugin::BFChildren) {
500
        ContentCacheEntry e;
501
        resultToEntries(it->second, 0, 0, e.m_results);
502
        o_bcache.set(cachekey, e);
503
        return e.toResult("", stidx, cnt, entries);
504
    } else {
385
    return resultToEntries(it->second, stidx, cnt, entries);
505
        return resultToEntries(it->second, stidx, cnt, entries);
386
}
387
388
389
class SearchCacheEntry {
390
public:
391
    SearchCacheEntry()
392
        : m_time(time(0)) {
393
    }
506
    }
394
    time_t m_time;
395
    vector<UpSong> m_results;
396
};
507
}
397
508
398
const int retention_secs = 300;
509
// Note that the offset and count don't get to the plugin for
399
class SearchCache {
510
// now. Plugins just return a (plugin-dependant) fixed number of
400
public:
511
// entries from offset 0, which we cache. There is no good reason for
401
    SearchCache();
512
// this, beyond the fact that we have to cap the entry count anyway,
402
    SearchCacheEntry *get(const string& query);
513
// else the CP is going to read to the end which might be
403
    void set(const string& query, SearchCacheEntry &entry);
514
// reaaaaalllllyyyy long.
404
    void flush();
405
private:
406
    time_t m_lastflush;
407
    unordered_map<string, SearchCacheEntry> m_cache;
408
};
409
410
SearchCache::SearchCache()
411
        : m_lastflush(time(0))
412
{
413
}
414
415
void SearchCache::flush()
416
{
417
    time_t now(time(0));
418
    if (now - m_lastflush < 5) {
419
        return;
420
    }
421
    for (unordered_map<string, SearchCacheEntry>::iterator it = m_cache.begin();
422
         it != m_cache.end(); ) {
423
        if (now - it->second.m_time > retention_secs) {
424
            LOGDEB0("SearchCache::flush: erasing " << it->first << endl);
425
            it = m_cache.erase(it);
426
        } else {
427
            it++;
428
        }
429
    }
430
    m_lastflush = now;
431
}
432
433
SearchCacheEntry *SearchCache::get(const string& key)
434
{
435
    flush();
436
    auto it = m_cache.find(key);
437
    if (it != m_cache.end()) {
438
        LOGDEB0("SearchCache::get: found " << key << endl);
439
        // we return a copy of the vector. Make our multi-access life simpler...
440
        return new SearchCacheEntry(it->second);
441
    }
442
    LOGDEB0("SearchCache::get: not found " << key << endl);
443
    return nullptr;
444
}
445
446
void SearchCache::set(const string& key, SearchCacheEntry &entry)
447
{
448
    LOGDEB0("SearchCache::set: " << key << endl);
449
    m_cache[key] = entry;
450
}
451
452
static SearchCache o_scache;
453
454
int resultFromCacheEntry(const string& classfilter, int stidx, int cnt,
455
                         const SearchCacheEntry& e,
456
                         vector<UpSong>& entries)
457
{
458
    const vector<UpSong>& res = e.m_results;
459
    LOGDEB0("resultFromCacheEntry: filter " << classfilter << " start " <<
460
            stidx << " cnt " << cnt << " res.size " << res.size() << endl);
461
    entries.reserve(cnt);
462
    int total = 0;
463
    for (unsigned int i = 0; i < res.size(); i++) {
464
        if (!classfilter.empty() && res[i].upnpClass.find(classfilter) != 0) {
465
            continue;
466
        }
467
        total++;
468
        if (stidx > int(i)) {
469
            continue;
470
        }
471
        if (int(entries.size()) >= cnt) {
472
            continue;
473
        }
474
        LOGDEB1("resultFromCacheEntry: pushing class "  << res[i].upnpClass <<
475
               " tt " << res[i].title << endl);
476
        entries.push_back(res[i]);
477
    }
478
    return total;
479
}
480
481
int PlgWithSlave::search(const string& ctid, int stidx, int cnt,
515
int PlgWithSlave::search(const string& ctid, int stidx, int cnt,
482
                         const string& searchstr,
516
                         const string& searchstr,
483
                         vector<UpSong>& entries,
517
                         vector<UpSong>& entries,
484
                         const vector<string>& sortcrits)
518
                         const vector<string>& sortcrits)
485
{
519
{
...
...
552
        LOGERR("PlgWithSlave: unsupported search: [" << searchstr << "]\n");
586
        LOGERR("PlgWithSlave: unsupported search: [" << searchstr << "]\n");
553
        return errorEntries(ctid, entries);
587
        return errorEntries(ctid, entries);
554
    }
588
    }
555
589
556
    // In cache ?
590
    // In cache ?
557
    SearchCacheEntry *cep;
591
    ContentCacheEntry *cep;
558
    string cachekey(m_name + ":" + objkind + ":" + slavefield + ":" + value);
592
    string cachekey(m_name + ":" + objkind + ":" + slavefield + ":" + value);
559
    if ((cep = o_scache.get(cachekey)) != nullptr) {
593
    if ((cep = o_scache.get(cachekey)) != nullptr) {
560
        int total = resultFromCacheEntry(classfilter, stidx, cnt, *cep, entries);
594
        int total = cep->toResult(classfilter, stidx, cnt, entries);
561
        delete cep;
595
        delete cep;
562
        return total;
596
        return total;
563
    }
597
    }
564
598
565
    // Run query
599
    // Run query
...
...
577
    if (it == res.end()) {
611
    if (it == res.end()) {
578
    LOGERR("PlgWithSlave::search: no entries returned\n");
612
    LOGERR("PlgWithSlave::search: no entries returned\n");
579
    return errorEntries(ctid, entries);
613
    return errorEntries(ctid, entries);
580
    }
614
    }
581
    // Convert the whole set and store in cache
615
    // Convert the whole set and store in cache
582
    SearchCacheEntry e;
616
    ContentCacheEntry e;
583
    resultToEntries(it->second, 0, 0, e.m_results);
617
    resultToEntries(it->second, 0, 0, e.m_results);
584
    o_scache.set(cachekey, e);
618
    o_scache.set(cachekey, e);
585
    return resultFromCacheEntry(classfilter, stidx, cnt, e, entries);
619
    return e.toResult(classfilter, stidx, cnt, entries);
586
}
620
}