diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2017-07-03 14:02:27 +0200 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2017-07-03 14:02:27 +0200 |
commit | 55fe06979ceebe67553135b43aa47e70d931304b (patch) | |
tree | 16b10a744879cc1872d6c07624b59ae64469ddbf /common/storage/entitystore.cpp | |
parent | 56fae95f49a1ca8ca614bd9f89b0ea5f872765e9 (diff) | |
parent | 288946f1694c2abe1d2c5800c87339d1e8780e4b (diff) | |
download | sink-55fe06979ceebe67553135b43aa47e70d931304b.tar.gz sink-55fe06979ceebe67553135b43aa47e70d931304b.zip |
Merge branch 'develop'
Diffstat (limited to 'common/storage/entitystore.cpp')
-rw-r--r-- | common/storage/entitystore.cpp | 121 |
1 files changed, 85 insertions, 36 deletions
diff --git a/common/storage/entitystore.cpp b/common/storage/entitystore.cpp index b7309ab..22e5ae3 100644 --- a/common/storage/entitystore.cpp +++ b/common/storage/entitystore.cpp | |||
@@ -36,9 +36,75 @@ | |||
36 | using namespace Sink; | 36 | using namespace Sink; |
37 | using namespace Sink::Storage; | 37 | using namespace Sink::Storage; |
38 | 38 | ||
39 | static QMap<QByteArray, int> baseDbs() | ||
40 | { | ||
41 | return {{"revisionType", 0}, | ||
42 | {"revisions", 0}, | ||
43 | {"uids", 0}, | ||
44 | {"default", 0}, | ||
45 | {"__flagtable", 0}}; | ||
46 | } | ||
47 | |||
48 | template <typename T, typename First> | ||
49 | void mergeImpl(T &map, First f) | ||
50 | { | ||
51 | for (auto it = f.constBegin(); it != f.constEnd(); it++) { | ||
52 | map.insert(it.key(), it.value()); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | template <typename T, typename First, typename ... Tail> | ||
57 | void mergeImpl(T &map, First f, Tail ...maps) | ||
58 | { | ||
59 | for (auto it = f.constBegin(); it != f.constEnd(); it++) { | ||
60 | map.insert(it.key(), it.value()); | ||
61 | } | ||
62 | mergeImpl<T, Tail...>(map, maps...); | ||
63 | } | ||
64 | |||
65 | template <typename First, typename ... Tail> | ||
66 | First merge(First f, Tail ...maps) | ||
67 | { | ||
68 | First map; | ||
69 | mergeImpl(map, f, maps...); | ||
70 | return map; | ||
71 | } | ||
72 | |||
73 | template <class T> | ||
74 | struct DbLayoutHelper { | ||
75 | void operator()(QMap<QByteArray, int> map) const { | ||
76 | mergeImpl(map, ApplicationDomain::TypeImplementation<T>::typeDatabases()); | ||
77 | } | ||
78 | }; | ||
79 | |||
80 | static Sink::Storage::DbLayout dbLayout(const QByteArray &instanceId) | ||
81 | { | ||
82 | static auto databases = [] { | ||
83 | QMap<QByteArray, int> map; | ||
84 | mergeImpl(map, ApplicationDomain::TypeImplementation<ApplicationDomain::Mail>::typeDatabases()); | ||
85 | mergeImpl(map, ApplicationDomain::TypeImplementation<ApplicationDomain::Folder>::typeDatabases()); | ||
86 | mergeImpl(map, ApplicationDomain::TypeImplementation<ApplicationDomain::Contact>::typeDatabases()); | ||
87 | mergeImpl(map, ApplicationDomain::TypeImplementation<ApplicationDomain::Addressbook>::typeDatabases()); | ||
88 | mergeImpl(map, ApplicationDomain::TypeImplementation<ApplicationDomain::Event>::typeDatabases()); | ||
89 | return merge(baseDbs(), map); | ||
90 | }(); | ||
91 | return {instanceId, databases}; | ||
92 | } | ||
93 | |||
94 | |||
39 | class EntityStore::Private { | 95 | class EntityStore::Private { |
40 | public: | 96 | public: |
41 | Private(const ResourceContext &context, const Sink::Log::Context &ctx) : resourceContext(context), logCtx(ctx.subContext("entitystore")) {} | 97 | Private(const ResourceContext &context, const Sink::Log::Context &ctx) : resourceContext(context), logCtx(ctx.subContext("entitystore")) |
98 | { | ||
99 | static bool initialized = false; | ||
100 | if (!initialized) { | ||
101 | if (QDir{}.mkpath(entityBlobStorageDir())) { | ||
102 | initialized = true; | ||
103 | } else { | ||
104 | SinkWarningCtx(logCtx) << "Failed to create the directory: " << entityBlobStorageDir(); | ||
105 | } | ||
106 | } | ||
107 | } | ||
42 | 108 | ||
43 | ResourceContext resourceContext; | 109 | ResourceContext resourceContext; |
44 | DataStore::Transaction transaction; | 110 | DataStore::Transaction transaction; |
@@ -56,7 +122,7 @@ public: | |||
56 | return transaction; | 122 | return transaction; |
57 | } | 123 | } |
58 | 124 | ||
59 | Sink::Storage::DataStore store(Sink::storageLocation(), resourceContext.instanceId(), DataStore::ReadOnly); | 125 | Sink::Storage::DataStore store(Sink::storageLocation(), dbLayout(resourceContext.instanceId()), DataStore::ReadOnly); |
60 | transaction = store.createTransaction(DataStore::ReadOnly); | 126 | transaction = store.createTransaction(DataStore::ReadOnly); |
61 | return transaction; | 127 | return transaction; |
62 | } | 128 | } |
@@ -93,9 +159,14 @@ public: | |||
93 | return ApplicationDomain::ApplicationDomainType{resourceContext.instanceId(), uid, revision, adaptor}; | 159 | return ApplicationDomain::ApplicationDomainType{resourceContext.instanceId(), uid, revision, adaptor}; |
94 | } | 160 | } |
95 | 161 | ||
162 | QString entityBlobStorageDir() | ||
163 | { | ||
164 | return Sink::resourceStorageLocation(resourceContext.instanceId()) + "/blob"; | ||
165 | } | ||
166 | |||
96 | QString entityBlobStoragePath(const QByteArray &id) | 167 | QString entityBlobStoragePath(const QByteArray &id) |
97 | { | 168 | { |
98 | return Sink::resourceStorageLocation(resourceContext.instanceId()) + "/blob/" + id; | 169 | return entityBlobStorageDir() +"/"+ id; |
99 | } | 170 | } |
100 | 171 | ||
101 | }; | 172 | }; |
@@ -110,9 +181,8 @@ void EntityStore::startTransaction(Sink::Storage::DataStore::AccessMode accessMo | |||
110 | { | 181 | { |
111 | SinkTraceCtx(d->logCtx) << "Starting transaction: " << accessMode; | 182 | SinkTraceCtx(d->logCtx) << "Starting transaction: " << accessMode; |
112 | Q_ASSERT(!d->transaction); | 183 | Q_ASSERT(!d->transaction); |
113 | Sink::Storage::DataStore store(Sink::storageLocation(), d->resourceContext.instanceId(), accessMode); | 184 | Sink::Storage::DataStore store(Sink::storageLocation(), dbLayout(d->resourceContext.instanceId()), accessMode); |
114 | d->transaction = store.createTransaction(accessMode); | 185 | d->transaction = store.createTransaction(accessMode); |
115 | Q_ASSERT(d->transaction.validateNamedDatabases()); | ||
116 | } | 186 | } |
117 | 187 | ||
118 | void EntityStore::commitTransaction() | 188 | void EntityStore::commitTransaction() |
@@ -138,9 +208,6 @@ bool EntityStore::hasTransaction() const | |||
138 | void EntityStore::copyBlobs(ApplicationDomain::ApplicationDomainType &entity, qint64 newRevision) | 208 | void EntityStore::copyBlobs(ApplicationDomain::ApplicationDomainType &entity, qint64 newRevision) |
139 | { | 209 | { |
140 | const auto directory = d->entityBlobStoragePath(entity.identifier()); | 210 | const auto directory = d->entityBlobStoragePath(entity.identifier()); |
141 | if (!QDir().mkpath(directory)) { | ||
142 | SinkWarningCtx(d->logCtx) << "Failed to create the directory: " << directory; | ||
143 | } | ||
144 | 211 | ||
145 | for (const auto &property : entity.changedProperties()) { | 212 | for (const auto &property : entity.changedProperties()) { |
146 | const auto value = entity.getProperty(property); | 213 | const auto value = entity.getProperty(property); |
@@ -149,7 +216,7 @@ void EntityStore::copyBlobs(ApplicationDomain::ApplicationDomainType &entity, qi | |||
149 | //Any blob that is not part of the storage yet has to be moved there. | 216 | //Any blob that is not part of the storage yet has to be moved there. |
150 | if (blob.isExternal) { | 217 | if (blob.isExternal) { |
151 | auto oldPath = blob.value; | 218 | auto oldPath = blob.value; |
152 | auto filePath = directory + QString("/%1%2.blob").arg(QString::number(newRevision)).arg(QString::fromLatin1(property)); | 219 | auto filePath = directory + QString("_%1%2.blob").arg(QString::number(newRevision)).arg(QString::fromLatin1(property)); |
153 | //In case we hit the same revision again due to a rollback. | 220 | //In case we hit the same revision again due to a rollback. |
154 | QFile::remove(filePath); | 221 | QFile::remove(filePath); |
155 | QFile origFile(oldPath); | 222 | QFile origFile(oldPath); |
@@ -320,6 +387,11 @@ void EntityStore::cleanupEntityRevisionsUntil(qint64 revision) | |||
320 | { | 387 | { |
321 | const auto uid = DataStore::getUidFromRevision(d->transaction, revision); | 388 | const auto uid = DataStore::getUidFromRevision(d->transaction, revision); |
322 | const auto bufferType = DataStore::getTypeFromRevision(d->transaction, revision); | 389 | const auto bufferType = DataStore::getTypeFromRevision(d->transaction, revision); |
390 | if (bufferType.isEmpty() || uid.isEmpty()) { | ||
391 | SinkErrorCtx(d->logCtx) << "Failed to find revision during cleanup: " << revision; | ||
392 | Q_ASSERT(false); | ||
393 | return; | ||
394 | } | ||
323 | SinkTraceCtx(d->logCtx) << "Cleaning up revision " << revision << uid << bufferType; | 395 | SinkTraceCtx(d->logCtx) << "Cleaning up revision " << revision << uid << bufferType; |
324 | DataStore::mainDatabase(d->transaction, bufferType) | 396 | DataStore::mainDatabase(d->transaction, bufferType) |
325 | .scan(uid, | 397 | .scan(uid, |
@@ -337,10 +409,10 @@ void EntityStore::cleanupEntityRevisionsUntil(qint64 revision) | |||
337 | DataStore::mainDatabase(d->transaction, bufferType).remove(key); | 409 | DataStore::mainDatabase(d->transaction, bufferType).remove(key); |
338 | } | 410 | } |
339 | if (isRemoval) { | 411 | if (isRemoval) { |
340 | const auto directory = d->entityBlobStoragePath(uid); | 412 | QDir dir{d->entityBlobStorageDir()}; |
341 | QDir dir(directory); | 413 | const auto infoList = dir.entryInfoList(QStringList{} << QString{uid + "*"}); |
342 | if (!dir.removeRecursively()) { | 414 | for (const auto &fileInfo : infoList) { |
343 | SinkErrorCtx(d->logCtx) << "Failed to cleanup: " << directory; | 415 | QFile::remove(fileInfo.filePath()); |
344 | } | 416 | } |
345 | } | 417 | } |
346 | //Don't cleanup more than specified | 418 | //Don't cleanup more than specified |
@@ -614,29 +686,6 @@ qint64 EntityStore::maxRevision() | |||
614 | return DataStore::maxRevision(d->getTransaction()); | 686 | return DataStore::maxRevision(d->getTransaction()); |
615 | } | 687 | } |
616 | 688 | ||
617 | /* DataStore::Transaction getTransaction() */ | ||
618 | /* { */ | ||
619 | /* Sink::Storage::DataStore::Transaction transaction; */ | ||
620 | /* { */ | ||
621 | /* Sink::Storage::DataStore storage(Sink::storageLocation(), mResourceInstanceIdentifier); */ | ||
622 | /* if (!storage.exists()) { */ | ||
623 | /* //This is not an error if the resource wasn't started before */ | ||
624 | /* SinkLogCtx(d->logCtx) << "Store doesn't exist: " << mResourceInstanceIdentifier; */ | ||
625 | /* return Sink::Storage::DataStore::Transaction(); */ | ||
626 | /* } */ | ||
627 | /* storage.setDefaultErrorHandler([this](const Sink::Storage::DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Error during query: " << error.store << error.message; }); */ | ||
628 | /* transaction = storage.createTransaction(Sink::Storage::DataStore::ReadOnly); */ | ||
629 | /* } */ | ||
630 | |||
631 | /* //FIXME this is a temporary measure to recover from a failure to open the named databases correctly. */ | ||
632 | /* //Once the actual problem is fixed it will be enough to simply crash if we open the wrong database (which we check in openDatabase already). */ | ||
633 | /* while (!transaction.validateNamedDatabases()) { */ | ||
634 | /* Sink::Storage::DataStore storage(Sink::storageLocation(), mResourceInstanceIdentifier); */ | ||
635 | /* transaction = storage.createTransaction(Sink::Storage::DataStore::ReadOnly); */ | ||
636 | /* } */ | ||
637 | /* return transaction; */ | ||
638 | /* } */ | ||
639 | |||
640 | Sink::Log::Context EntityStore::logContext() const | 689 | Sink::Log::Context EntityStore::logContext() const |
641 | { | 690 | { |
642 | return d->logCtx; | 691 | return d->logCtx; |