diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2018-02-15 10:19:08 +0100 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2018-02-15 11:08:52 +0100 |
commit | 531972042d4b610258c8af8a17ec3a99cd063dda (patch) | |
tree | ef4356ec141f0f1ccd756e8610b08553b866bf78 /sinksh | |
parent | f51963f057bcbdd175114433913a1c5f0eebd546 (diff) | |
download | sink-531972042d4b610258c8af8a17ec3a99cd063dda.tar.gz sink-531972042d4b610258c8af8a17ec3a99cd063dda.zip |
Fixed crashes due to concurrently running queries.
A single QueryRunner should never have multiple workers running at the
same time. We did not properly enforce this in case of incremental
updates coming in.
The only way I managed to reproduce the crash:
* Open a large folder with lots of unread mail in kube
* Select a mail in the maillist and hold the down button
* This will:
* Repeatedly call fetch more
* Trigger lot's of mark as read modifications that result in
notifications.
* Eventually it crashes somewhere in EntityStore, likely because
of concurrent access of the filter structure which is shared through
the state.
We now ensure in the single threaded portion of the code that we only
ever run one worker at a time. If we did receive an update during,
we remember that change and fetch more once we're done.
To be able to call fetch again that portion was also factored out into a
separate function.
Diffstat (limited to 'sinksh')
-rw-r--r-- | sinksh/syntax_modules/sink_selftest.cpp | 84 |
1 files changed, 75 insertions, 9 deletions
diff --git a/sinksh/syntax_modules/sink_selftest.cpp b/sinksh/syntax_modules/sink_selftest.cpp index 21dfbff..8ad4f60 100644 --- a/sinksh/syntax_modules/sink_selftest.cpp +++ b/sinksh/syntax_modules/sink_selftest.cpp | |||
@@ -46,28 +46,94 @@ bool selfTest(const QStringList &args_, State &state) | |||
46 | return false; | 46 | return false; |
47 | } | 47 | } |
48 | 48 | ||
49 | using namespace Sink::ApplicationDomain; | ||
49 | auto options = SyntaxTree::parseOptions(args_); | 50 | auto options = SyntaxTree::parseOptions(args_); |
50 | if (options.positionalArguments.contains("stresstest")) { | 51 | if (options.positionalArguments.contains("stresstest")) { |
51 | auto resource = SinkshUtils::parseUid(options.options.value("resource").first().toUtf8()); | 52 | auto resource = SinkshUtils::parseUid(options.options.value("resource").first().toUtf8()); |
52 | qWarning() << "Stresstest on resource: " << resource; | 53 | qWarning() << "Stresstest on resource: " << resource; |
53 | Sink::Query query; | ||
54 | query.resourceFilter(resource); | ||
55 | query.limit(100); | ||
56 | |||
57 | auto models = QSharedPointer<QList<QSharedPointer<QAbstractItemModel>>>::create(); | 54 | auto models = QSharedPointer<QList<QSharedPointer<QAbstractItemModel>>>::create(); |
58 | for (int i = 0; i < 50; i++) { | 55 | |
56 | //Simulate the maillist, where we scroll down and trigger lots of fetchMore calls | ||
57 | { | ||
58 | Sink::Query query; | ||
59 | query.resourceFilter(resource); | ||
60 | query.limit(100); | ||
61 | query.request<Mail::Subject>(); | ||
62 | query.request<Mail::Sender>(); | ||
63 | query.request<Mail::To>(); | ||
64 | query.request<Mail::Cc>(); | ||
65 | query.request<Mail::Bcc>(); | ||
66 | query.request<Mail::Date>(); | ||
67 | query.request<Mail::Unread>(); | ||
68 | query.request<Mail::Important>(); | ||
69 | query.request<Mail::Draft>(); | ||
70 | query.request<Mail::Sent>(); | ||
71 | query.request<Mail::Trash>(); | ||
72 | query.request<Mail::Folder>(); | ||
73 | query.sort<Mail::Date>(); | ||
74 | query.reduce<Mail::ThreadId>(Sink::Query::Reduce::Selector::max<Mail::Date>()) | ||
75 | .count("count") | ||
76 | .collect<Mail::Unread>("unreadCollected") | ||
77 | .collect<Mail::Important>("importantCollected"); | ||
78 | |||
59 | auto model = Sink::Store::loadModel<Sink::ApplicationDomain::Mail>(query); | 79 | auto model = Sink::Store::loadModel<Sink::ApplicationDomain::Mail>(query); |
60 | *models << model; | 80 | models->append(model); |
61 | QObject::connect(model.data(), &QAbstractItemModel::dataChanged, [models, model, &state](const QModelIndex &start, const QModelIndex &end, const QVector<int> &roles) { | 81 | QObject::connect(model.data(), &QAbstractItemModel::dataChanged, [models, model, &state](const QModelIndex &start, const QModelIndex &end, const QVector<int> &roles) { |
62 | if (roles.contains(Sink::Store::ChildrenFetchedRole)) { | 82 | if (roles.contains(Sink::Store::ChildrenFetchedRole)) { |
63 | models->removeAll(model); | 83 | if (!model->canFetchMore({})) { |
64 | qWarning() << "Model complete: " << models->count(); | 84 | qWarning() << "Model complete: " << models->count(); |
85 | models->removeAll(model); | ||
86 | } else { | ||
87 | qWarning() << "Fetching more"; | ||
88 | //Simulate superfluous fetchMore calls | ||
89 | for (int i = 0; i < 10; i++) { | ||
90 | model->fetchMore({}); | ||
91 | } | ||
92 | return; | ||
93 | } | ||
65 | if (models->isEmpty()) { | 94 | if (models->isEmpty()) { |
66 | state.commandFinished(); | 95 | state.commandFinished(); |
67 | } | 96 | } |
68 | } | 97 | } |
69 | }); | 98 | }); |
99 | |||
70 | } | 100 | } |
101 | |||
102 | //Simluate lot's of mailviewers doing a bunch of queries | ||
103 | { | ||
104 | Sink::Query query; | ||
105 | query.resourceFilter(resource); | ||
106 | query.limit(10); | ||
107 | query.request<Mail::Subject>(); | ||
108 | query.request<Mail::Sender>(); | ||
109 | query.request<Mail::To>(); | ||
110 | query.request<Mail::Cc>(); | ||
111 | query.request<Mail::Bcc>(); | ||
112 | query.request<Mail::Date>(); | ||
113 | query.request<Mail::Unread>(); | ||
114 | query.request<Mail::Important>(); | ||
115 | query.request<Mail::Draft>(); | ||
116 | query.request<Mail::Sent>(); | ||
117 | query.request<Mail::Trash>(); | ||
118 | query.request<Mail::Folder>(); | ||
119 | query.sort<Sink::ApplicationDomain::Mail::Date>(); | ||
120 | query.bloom<Sink::ApplicationDomain::Mail::ThreadId>(); | ||
121 | |||
122 | for (int i = 0; i < 50; i++) { | ||
123 | auto model = Sink::Store::loadModel<Sink::ApplicationDomain::Mail>(query); | ||
124 | *models << model; | ||
125 | QObject::connect(model.data(), &QAbstractItemModel::dataChanged, [models, model, &state](const QModelIndex &start, const QModelIndex &end, const QVector<int> &roles) { | ||
126 | if (roles.contains(Sink::Store::ChildrenFetchedRole)) { | ||
127 | models->removeAll(model); | ||
128 | qWarning() << "Model complete: " << models->count(); | ||
129 | if (models->isEmpty()) { | ||
130 | state.commandFinished(); | ||
131 | } | ||
132 | } | ||
133 | }); | ||
134 | } | ||
135 | } | ||
136 | |||
71 | return true; | 137 | return true; |
72 | } | 138 | } |
73 | return false; | 139 | return false; |