Commit d263b936 authored by Daniel Vrátil's avatar Daniel Vrátil 🤖
Browse files

Introduce concept of database generation

Generation is an integer which is guaranteed to never change as long as
the database backend is not removed and re-created. If that happens it
is guaranteed that the new generation identifier will be higher than
the previous one.

Client applications can access it via ServerManager::generation(). The
purpose of the generation number is to make it possible for applications
to detect when the database was recreated and some of their configuration
must be invalidated (e.g. collection IDs stored in config files).
parent 2aa2d64d
......@@ -22,7 +22,7 @@ include(CheckSymbolExists)
include(AkonadiMacros)
set(PIM_VERSION "5.3.45")
set(PIM_VERSION "5.3.46")
set(QT_REQUIRED_VERSION "5.6.0")
set(AKONADI_VERSION ${PIM_VERSION})
......
......@@ -147,11 +147,14 @@ QString FakeAkonadiServer::instanceName()
TestScenario::List FakeAkonadiServer::loginScenario(const QByteArray &sessionId)
{
Protocol::HelloResponse hello;
hello.setServerName(QStringLiteral("Akonadi"));
hello.setMessage(QStringLiteral("Not really IMAP server"));
hello.setProtocolVersion(Protocol::version());
hello.setGeneration(1);
return {
TestScenario::create(0, TestScenario::ServerCmd,
Protocol::HelloResponse(QStringLiteral("Akonadi"),
QStringLiteral("Not Really IMAP server"),
Protocol::version())),
TestScenario::create(0, TestScenario::ServerCmd, hello),
TestScenario::create(1,TestScenario::ClientCmd,
Protocol::LoginCommand(sessionId.isEmpty() ? instanceName().toLatin1() : sessionId)),
TestScenario::create(1, TestScenario::ServerCmd,
......
......@@ -118,6 +118,7 @@ public:
ServerManager *instance;
static int serverProtocolVersion;
static uint generation;
ServerManager::State mState;
QScopedPointer<QTimer> mSafetyTimer;
Firstrun *mFirstRunner;
......@@ -125,6 +126,7 @@ public:
};
int ServerManagerPrivate::serverProtocolVersion = -1;
uint ServerManagerPrivate::generation = 0;
Internal::ClientType ServerManagerPrivate::clientType = Internal::User;
Q_GLOBAL_STATIC(ServerManagerPrivate, sInstance)
......@@ -351,6 +353,11 @@ QString ServerManager::addNamespace(const QString &string)
return string;
}
uint ServerManager::generation()
{
return Internal::generation();
}
int Internal::serverProtocolVersion()
{
return ServerManagerPrivate::serverProtocolVersion;
......@@ -364,6 +371,16 @@ void Internal::setServerProtocolVersion(int version)
}
}
uint Internal::generation()
{
return ServerManagerPrivate::generation;
}
void Internal::setGeneration(uint generation)
{
ServerManagerPrivate::generation = generation;
}
Internal::ClientType Internal::clientType()
{
return ServerManagerPrivate::clientType;
......
......@@ -178,6 +178,25 @@ public:
*/
static QString agentConfigFilePath(const QString &identifier);
/**
* Returns current Akonadi database generation identifier
*
* Generation is guaranteed to never change unless as long as the database
* backend is not removed and re-created. In such case it is guaranteed that
* the new generation number will be higher than the previous one.
*
* Generation can be used by applications to detect when Akonadi database
* has been recreated and thus some of the configuration (for example
* collection IDs stored in a config file) must be invalidated.
*
* @note Note that the generation number is only available if the server
* is running. If this function is called before the server starts it will
* return 0.
*
* @since 5.4
*/
static uint generation();
Q_SIGNALS:
/**
* Emitted whenever the server becomes fully operational.
......
......@@ -32,6 +32,8 @@ namespace Internal
AKONADICORE_EXPORT int serverProtocolVersion();
AKONADICORE_EXPORT void setServerProtocolVersion(int version);
AKONADICORE_EXPORT uint generation();
AKONADICORE_EXPORT void setGeneration(uint generation);
enum ClientType {
User,
......
......@@ -111,11 +111,14 @@ bool SessionPrivate::handleCommand(qint64 tag, const Protocol::Command &cmd)
}
qCDebug(AKONADICORE_LOG) << "Connected to" << hello.serverName() << ", using protocol version" << hello.protocolVersion();
qCDebug(AKONADICORE_LOG) << "Server generation:" << hello.generation();
qCDebug(AKONADICORE_LOG) << "Server says:" << hello.message();
// Version mismatch is handled in SessionPrivate::startJob() so that
// we can report the error out via KJob API
protocolVersion = hello.protocolVersion();
Internal::setServerProtocolVersion(protocolVersion);
Internal::setGeneration(hello.generation());
Protocol::LoginCommand login(sessionId);
sendCommand(nextTag(), login);
......
......@@ -49,7 +49,7 @@ namespace Akonadi {
namespace Protocol {
int version() {
return 55;
return 56;
}
}
......@@ -1533,12 +1533,7 @@ public:
HelloResponsePrivate()
: ResponsePrivate(Command::Hello)
, protocol(0)
{}
HelloResponsePrivate(const QString &server, const QString &message, int protocol)
: ResponsePrivate(Command::Hello)
, server(server)
, message(message)
, protocol(protocol)
, generation(0)
{}
HelloResponsePrivate(const HelloResponsePrivate &other)
......@@ -1546,6 +1541,7 @@ public:
, server(other.server)
, message(other.message)
, protocol(other.protocol)
, generation(other.generation)
{}
bool compare(const CommandPrivate *other) const Q_DECL_OVERRIDE
......@@ -1553,7 +1549,8 @@ public:
return ResponsePrivate::compare(other)
&& COMPARE(server)
&& COMPARE(message)
&& COMPARE(protocol);
&& COMPARE(protocol)
&& COMPARE(generation);
}
DataStream &serialize(DataStream &stream) const Q_DECL_OVERRIDE
......@@ -1561,7 +1558,8 @@ public:
return ResponsePrivate::serialize(stream)
<< server
<< message
<< protocol;
<< protocol
<< generation;
}
DataStream &deserialize(DataStream &stream) Q_DECL_OVERRIDE
......@@ -1569,7 +1567,8 @@ public:
return ResponsePrivate::deserialize(stream)
>> server
>> message
>> protocol;
>> protocol
>> generation;
}
void debugString(DebugBlock &blck) const Q_DECL_OVERRIDE
......@@ -1578,6 +1577,7 @@ public:
blck.write("Server", server);
blck.write("Protocol Version", protocol);
blck.write("Message", message);
blck.write("Generation", generation);
}
CommandPrivate *clone() const Q_DECL_OVERRIDE
......@@ -1588,6 +1588,7 @@ public:
QString server;
QString message;
int protocol;
uint generation;
};
......@@ -1602,11 +1603,6 @@ public:
AKONADI_DECLARE_PRIVATE(HelloResponse)
HelloResponse::HelloResponse(const QString &server, const QString &message, int protocol)
: Response(new HelloResponsePrivate(server, message, protocol))
{
}
HelloResponse::HelloResponse()
: Response(new HelloResponsePrivate)
{
......@@ -1648,6 +1644,16 @@ int HelloResponse::protocolVersion() const
return d_func()->protocol;
}
void HelloResponse::setGeneration(uint generation)
{
d_func()->generation = generation;
}
uint HelloResponse::generation() const
{
return d_func()->generation;
}
DataStream &operator<<(DataStream &stream, const HelloResponse &command)
{
return command.d_func()->serialize(stream);
......
......@@ -476,7 +476,6 @@ class AKONADIPRIVATE_EXPORT HelloResponse : public Response
public:
explicit HelloResponse();
explicit HelloResponse(const Command &command);
HelloResponse(const QString &server, const QString &message, int protocol);
void setServerName(const QString &server);
QString serverName() const;
......@@ -487,6 +486,9 @@ public:
void setProtocolVersion(int protocolVersion);
int protocolVersion() const;
void setGeneration(uint generation);
uint generation() const;
private:
AKONADI_DECLARE_PRIVATE(HelloResponse)
......
......@@ -121,9 +121,14 @@ void Connection::quit()
void Connection::slotSendHello()
{
sendResponse(0, Protocol::HelloResponse(QStringLiteral("Akonadi"),
QStringLiteral("Not Really IMAP server"),
Protocol::version()));
SchemaVersion version = SchemaVersion::retrieveAll().first();
Protocol::HelloResponse hello;
hello.setServerName(QStringLiteral("Akonadi"));
hello.setMessage(QStringLiteral("Not Really IMAP server"));
hello.setProtocolVersion(Protocol::version());
hello.setGeneration(version.generation());
sendResponse(0, hello);
}
DataStore *Connection::storageBackend()
......
......@@ -58,9 +58,14 @@ NotificationSubscriber::NotificationSubscriber(NotificationManager *manager, qui
this, &NotificationSubscriber::socketDisconnected);
mSocket->setSocketDescriptor(socketDescriptor);
writeCommand(0, Protocol::HelloResponse(QStringLiteral("Akonadi"),
QStringLiteral("Not-really IMAP server"),
Protocol::version()));
const SchemaVersion schema = SchemaVersion::retrieveAll().first();
Protocol::HelloResponse hello;
hello.setServerName(QStringLiteral("Akonadi"));
hello.setMessage(QStringLiteral("Not really IMAP server"));
hello.setProtocolVersion(Protocol::version());
hello.setGeneration(schema.generation());
writeCommand(0, hello);
}
NotificationSubscriber::~NotificationSubscriber()
......
......@@ -66,7 +66,8 @@
<table name="SchemaVersion">
<comment>Contains the schema version of the database.</comment>
<column name="version" type="int" default="0" allowNull="false"/>
<data columns="version" values="33"/>
<column name="generation" type="int" default="0" allowNull="false" />
<data columns="version" values="34"/>
</table>
<table name="Resource">
......
......@@ -23,7 +23,7 @@
#include "querybuilder.h"
#include "dbexception.h"
#include "schema.h"
#include "entity.h"
#include "entities.h"
#include "akonadiserver_debug.h"
#include <QtCore/QFile>
......@@ -92,6 +92,15 @@ bool DbInitializer::run()
}
}
// Now finally check and set the generation identifier if necessary
SchemaVersion version = SchemaVersion::retrieveAll().first();
if (version.generation() == 0) {
version.setGeneration(QDateTime::currentDateTimeUtc().toTime_t());
version.update();
qCDebug(AKONADISERVER_LOG) << "Generation:" << version.generation();
}
qCDebug(AKONADISERVER_LOG) << "DbInitializer::run() done";
return true;
} catch (const DbException &e) {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment