-
Notifications
You must be signed in to change notification settings - Fork 23
RFC: Keep a cache of frequently accessed files #517
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
nephros
wants to merge
41
commits into
sailfishos-patches:master
Choose a base branch
from
nephros:hotcache-as-class
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+430
−9
Draft
Changes from all commits
Commits
Show all changes
41 commits
Select commit
Hold shift + click to select a range
ad6c7b1
Hotcache: Add a cache object to track nonexisting files
37d44e9
Hotcache: remove entry if it existed
e2fddc3
Hotcache: simplify logic
bb975f4
Hotcache: store Object, retrieve one on lookup
5fb9b3f
Hotcache: remove log line
5951671
Hotcache: use object() not take()
de94ed1
Hotcache: move debugging messages after writing to the connection
20925c1
Hotcache: set up the filter with some initial contents
4ba2081
Hotcache: Add a Settings switch
bb422ca
Hotcache: wire up settings switch
a1826a4
Hotcache: move into a class, rename to m_filter
3fd441c
Hotcache: Don't be a template class
6884b81
Hotcache: Prepare for stats collecting
b0c5479
Hotcache: Static initializer lists
bdedbeb
Hotcache: Implement a simpler stats method
969a763
Hotcache: Make the lists public
9cb1321
Hotcache: Use foreach again.
82c3901
Hotcache: Missing braces
b33ecff
Hotcache: check for active filter in startReadingLocalServer
b65f653
Hotcache: Use plain qstring for stats
8a3787d
Hotcache: Add some debug prints
7df9ddb
Hotcache: Don't copy around strings, us a bool.
475ac37
Print hotcache stats on PM exit
5f9e2c9
Resolve symlinks before adding to primed cache
1a6e64f
Fix cache returning false the false way.
18a3c44
Update src/bin/patchmanager-daemon/patchmanagerobject.cpp
nephros 64eb096
Promote stat output to info level
ec2aae2
Split out Filter into .h
8b70747
Split out Filter into .cpp
29cd51e
fixup! Split out Filter into .h
b995f30
Rework PM Filter class sources
59348ec
Print statistics on demand
0d2fd91
Rework Stats yet again
213b508
Remove stats call from destructor
59ea818
Stats: limit max output size
7421eff
Stats: lower max cost
48ccda6
Default values go into header
ebb671f
Cache: Use small data type for cache entries
e31d6bf
Add a comment about sizing the cache
27e9ad7
Fix percentage display
95f7666
Cache: drop value from signature
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
5 changes: 5 additions & 0 deletions
5
src/bin/patchmanager-daemon/dbus/org.SfietKonstantin.patchmanager.xml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,169 @@ | ||
| /* | ||
| * Copyright (C) 2025 Patchmanager for SailfishOS contributors: | ||
| * - olf "Olf0" <https://github.com/Olf0> | ||
| * - Peter G. "nephros" <sailfish@nephros.org> | ||
| * - Vlad G. "b100dian" <https://github.com/b100dian> | ||
| * | ||
| * You may use this file under the terms of the BSD license as follows: | ||
| * | ||
| * "Redistribution and use in source and binary forms, with or without | ||
| * modification, are permitted provided that the following conditions are | ||
| * met: | ||
| * * Redistributions of source code must retain the above copyright | ||
| * notice, this list of conditions and the following disclaimer. | ||
| * * Redistributions in binary form must reproduce the above copyright | ||
| * notice, this list of conditions and the following disclaimer in | ||
| * the documentation and/or other materials provided with the | ||
| * distribution. | ||
| * * The names of its contributors may not be used to endorse or promote | ||
| * products derived from this software without specific prior written | ||
| * permission. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." | ||
| */ | ||
|
|
||
|
|
||
| /*! | ||
| * The current implementation of the filter is a QCache, whole Object contents | ||
| * are not actually used, only the keys are. Once a file path has been | ||
| * identified as non-existing, it is added to the cache. | ||
| * | ||
| * Checking for presence is done using QCache::object() (or | ||
| * QCache::operator[]), not QCache::contains() in order to have the cache | ||
| * notice "usage" of the cached object. | ||
| * | ||
| * \sa m_filter | ||
| */ | ||
|
|
||
| #include "patchmanagerfilter.h" | ||
|
|
||
| #include <QtCore/QObject> | ||
| #include <QtCore/QStringList> | ||
| #include <QtCore/QFileInfo> | ||
| #include <QtCore/QDebug> | ||
|
|
||
| /* initialize the "static members", i.e. a list of very frequesntly accessed files. */ | ||
| /* only use relatively stable sonames here. */ | ||
| const QStringList libList = QStringList({ | ||
| "/usr/lib64/libpreloadpatchmanager.so", | ||
| "/lib/ld-linux-aarch64.so.1", | ||
| "/lib/ld-linux-armhf.so.3", | ||
| "/lib64/libc.so.6", | ||
| "/lib64/libdl.so.2", | ||
| "/lib64/librt.so.1", | ||
| "/lib64/libpthread.so.0", | ||
| "/lib64/libgcc_s.so.1", | ||
| "/usr/lib64/libtls-padding.so", | ||
| "/usr/lib64/libsystemd.so.0", | ||
| "/usr/lib64/libcap.so.2", | ||
| "/usr/lib64/libmount.so.1", | ||
| "/usr/lib64/libblkid.so.1", | ||
| "/usr/lib64/libgpg-error.so.0" | ||
| }); | ||
|
|
||
| const QStringList etcList = QStringList({ | ||
| "/etc/passwd", | ||
| "/etc/group", | ||
| "/etc/shadow", | ||
| "/etc/localtime", | ||
| "/etc/ld.so.preload", | ||
| "/etc/ld.so.cache", | ||
| "/usr/share/locale/locale.alias" | ||
| }); | ||
|
|
||
| PatchManagerFilter::PatchManagerFilter(QObject *parent, int maxCost) | ||
| : QObject(parent) | ||
| , QCache(maxCost) | ||
| { | ||
| } | ||
|
|
||
| void PatchManagerFilter::setup() | ||
| { | ||
| qDebug() << Q_FUNC_INFO; | ||
| // set up cache | ||
| setMaxCost(HOTCACHE_COST_MAX); | ||
|
|
||
| // use a cost of 1 here so they have less chance to be evicted | ||
| foreach(const QString &entry, etcList) { | ||
| if (QFileInfo::exists(entry)) { | ||
| insert(entry, HOTCACHE_COST_STRONG); | ||
| } | ||
| } | ||
| // they may be wrong, so use a higher cost than default | ||
| foreach(const QString &entry, libList) { | ||
| QString libentry(entry); | ||
| if (Q_PROCESSOR_WORDSIZE == 4) { // 32 bit | ||
| libentry.replace("lib64", "lib"); | ||
| } | ||
|
|
||
| if (QFileInfo::exists(libentry)) { | ||
| QFileInfo fi(libentry); | ||
| insert(fi.canonicalFilePath(),HOTCACHE_COST_WEAK); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // override QCache::insert(). | ||
| bool PatchManagerFilter::insert(const QString &key, int cost) | ||
| { | ||
| return QCache::insert(key, 1, cost); | ||
| } | ||
|
|
||
| // override QCache::contains() | ||
| bool PatchManagerFilter::contains(const QString &key) const | ||
| { | ||
| if (!m_active) | ||
| return false; | ||
|
|
||
| // we do not use QCache::contains here, because ::object() will make the cache notice usage of the object | ||
| bool ret = (QCache::object(key) != 0); // NB: returns 0 in Qt < 5.13, nullptr in later versions | ||
|
|
||
| if(ret) { m_hits+=1; } else { m_misses+=1; } | ||
|
|
||
| return ret; | ||
| }; | ||
|
|
||
|
|
||
| QString PatchManagerFilter::stats(bool verbose) const | ||
| { | ||
| qDebug() << Q_FUNC_INFO; | ||
| QStringList stats; | ||
| stats << QStringLiteral("Filter Stats:") | ||
| << QStringLiteral("===========================") | ||
| << QStringLiteral(" Hotcache entries:: ..............%1").arg(size()) | ||
| << QStringLiteral(" Hotcache cost: ..................%1/%2").arg(totalCost()).arg(maxCost()); | ||
| if (verbose) { | ||
| unsigned int sum = m_hits + m_misses; | ||
| if (sum > 0) { | ||
| QString ratio; | ||
| float ratf = (static_cast<float>(m_hits) / sum)*100.0; | ||
| ratio.setNum(ratf, 'f', 1); | ||
| stats << QStringLiteral(" Hotcache hit/miss: ..............%1/%2 (%3%)").arg(m_hits).arg(m_misses).arg(ratio); | ||
| } | ||
|
|
||
| stats << QStringLiteral("===========================") | ||
| << QStringLiteral(" Hotcache entries:"); | ||
| if (count() > HOTCACHE_LOG_MAX) { | ||
| stats << QStringLiteral("showing %1/%2").arg(HOTCACHE_LOG_MAX).arg(count()); | ||
| auto beg = keys().begin(); auto end = beg + HOTCACHE_LOG_MAX; | ||
| for (auto it = beg; it != end; ++it) { | ||
| stats << *it; | ||
| } | ||
| } else { | ||
| stats << keys(); | ||
| } | ||
| } | ||
| stats << QStringLiteral("==========================="); | ||
|
|
||
| return stats.join("\n"); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| /* | ||
| * Copyright (C) 2025 Patchmanager for SailfishOS contributors: | ||
| * - olf "Olf0" <https://github.com/Olf0> | ||
| * - Peter G. "nephros" <sailfish@nephros.org> | ||
| * - Vlad G. "b100dian" <https://github.com/b100dian> | ||
| * | ||
| * You may use this file under the terms of the BSD license as follows: | ||
| * | ||
| * "Redistribution and use in source and binary forms, with or without | ||
| * modification, are permitted provided that the following conditions are | ||
| * met: | ||
| * * Redistributions of source code must retain the above copyright | ||
| * notice, this list of conditions and the following disclaimer. | ||
| * * Redistributions in binary form must reproduce the above copyright | ||
| * notice, this list of conditions and the following disclaimer in | ||
| * the documentation and/or other materials provided with the | ||
| * distribution. | ||
| * * The names of its contributors may not be used to endorse or promote | ||
| * products derived from this software without specific prior written | ||
| * permission. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." | ||
| */ | ||
|
|
||
| #ifndef PATCHMANAGERFILTER_H | ||
| #define PATCHMANAGERFILTER_H | ||
|
|
||
| #include <QtCore/QObject> | ||
| #include <QCache> | ||
|
|
||
| /* choosing the right cost obviously is critical and difficult ;) | ||
| we want lookup times to be faster than the cost of a QFileInfo::exists() | ||
| --> smaller is better. | ||
|
|
||
| We also want it to not hold "stale" entries, i.e. files once added and | ||
| never accessed again. | ||
| --> smaller is also better | ||
|
|
||
| But we also want it to hold the most commonly accessed files, and not rotate | ||
| the entries all the time. ideally after some time, it stays somewhat | ||
| stable. | ||
| --> too small is bad | ||
|
|
||
| Some observations: | ||
|
|
||
| df -i / on SFOS 5.0 gives about 100k used inodes. | ||
| On a system with about 100 patched files, running find /usr -exec head -n 1 {} >/dev/null \; | ||
| the cost() seems to not go over 3600 or so when maxCost is 5000. | ||
|
|
||
| */ | ||
|
|
||
| static const int HOTCACHE_COST_MAX = 2500; | ||
| static const int HOTCACHE_COST_STRONG = 1; | ||
| static const int HOTCACHE_COST_DEFAULT = 2; | ||
| static const int HOTCACHE_COST_WEAK = 3; | ||
|
|
||
| // output will be a dbus message. Don't make it too long. | ||
| static const int HOTCACHE_LOG_MAX = 4096; | ||
|
|
||
| // As we do not care about the actual cached object, try to use a small one. | ||
| // quint8 should be one byte or so | ||
| class PatchManagerFilter : public QObject, public QCache<QString, quint8> | ||
| { | ||
| Q_OBJECT | ||
| Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged) | ||
| Q_PROPERTY(unsigned int hits READ hits) | ||
| Q_PROPERTY(unsigned int misses READ misses) | ||
| public: | ||
| PatchManagerFilter(QObject *parent = nullptr, int maxCost = HOTCACHE_COST_MAX); | ||
| //~PatchManagerFilter(); | ||
|
|
||
| void setup(); | ||
|
|
||
| // override QCache::insert() | ||
| bool insert(const QString &key, int cost = HOTCACHE_COST_DEFAULT); | ||
|
|
||
| // override QCache::contains() | ||
| bool contains(const QString &key) const; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is our compiler so old? Pretty sure |
||
|
|
||
| void setActive(bool active) { | ||
| if (m_active != active) { | ||
| m_active = active; | ||
| emit activeChanged(active); | ||
| } | ||
| }; | ||
| bool active() const { return m_active; }; | ||
|
|
||
| unsigned int hits() const { return m_hits; }; | ||
| unsigned int misses() const { return m_misses; }; | ||
|
|
||
| //QList<QPair<QString, QVariant>> stats() const; | ||
| QString stats(bool verbose) const; | ||
|
|
||
| signals: | ||
| void activeChanged(bool); | ||
|
|
||
| private: | ||
| bool m_active; | ||
|
|
||
| // need to be mutable so we can count from const method. | ||
| mutable unsigned int m_hits = 0; | ||
| mutable unsigned int m_misses = 0; | ||
| }; | ||
|
|
||
| #endif // PATCHMANAGERFILTER_H | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this looks a little bit over specified compared to the others ;) Maybe its needed