Universal message storage #993
Open
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.
Introduction
This work is provided by Erlang Solutions. Our core product in the XMPP area is the MongooseIM platform and prior related framework contributions include the MUC Light implementation by Inaka.
While we are focused on the backend components at the moment, it is in our best interest to make the client-side development easier. This is particularly relevant to those of our customers whose systems do not have XMPP messaging as the core functionality and who may be therefore lacking technical expertise in that area.
Motivation
We have been developing a demo/tech showcase iOS chat app based on XMPPFramework. Among the observations we gathered during that time are the following:
When researching other open-source clients based on the framework, we found out there is a common usage pattern where an app provides its own custom storage and a message processing module that attempts to implement some custom processing path specific to a particular set of XMPP extensions.
We believe this goes against the framework's goal, i.e. shifting the app developer's focus from providing actual XMPP processing logic towards simply configuring the framework to suit their usage scenario.
Proposed solution
General
The aim of this pull request is to provide client message storage functionality with the following features:
Description
Top level implementation
The implementation (
XMPPMessageCoreDataStorage) is based onXMPPCoreDataStorage. Its object model can be divided into two sections:XMPPMessageCoreDataStorageObjectentityXMPPMessageContextCoreDataStorageObjectand severalXMPPMessageContextXXXItemCoreDataStorageObjectentitiesThe base section models the core XMPP message properties while all other information, including extension data stored by modules and metadata like local timestamps, belongs to the extensible context.
The extensible context is a weakly typed structure where each message can be associated with several
XMPPMessageContextCoreDataStorageObjectinstances, which in turn aggregateXMPPMessageContextXXXItemCoreDataStorageObjectinstances containing primitive tagged values.The tags and aggregation structure are defined for individual XMPP extensions by the respective module implementations. From application's point of view this all becomes an implementation detail as the API is provided via categories on
XMPPMessageContextCoreDataStorageObject.The main trait of the above scheme is that it enables local database queries involving extensible context attributes, while still giving significant degree of flexibility.
Transactions
As message processing in the framework is highly asynchronous with individual modules running on different queues, there has to be a way to ensure that applications do not witness partially processed message storage objects.
To do so, when processing a module delegate callback, an application obtains an
XMPPMessageCoreDataStorageTransactionobject and uses it to perform storage updates, as opposed to scheduling them directly. The transaction object then batches all the updates related to a given message and executes them as a single action.The transaction API is provided via categories on
XMPPMessageCoreDataStorageTransactionimplemented by module authors and operates on the object model internally, just like in case of theXMPPMessageContextCoreDataStorageObjectAPI mentioned above.XMPPStream element events
The section about transactional processing mentions the application obtaining a transaction from within a message processing module delegate callback and later the transaction batching storage actions. That would not be possible with the current
XMPPStreamimplementation due to the following shortcomings:willReceiveMessage:stream delegate callback, which is heavily dependent on the delegate order.XMPPMessageinstances for pointer equality, which we do not consider appropriate for a class conforming toNSCopying.To address the issue, we introduced the concept of stream element events. It is now possible to obtain an
XMPPElementEventinstance while in the context of a stream delegate element send/receive callback invocation. This event serves as handle that:The important point here is that this change is transparent w.r.t. the stream-module interface, i.e. even the existing modules will see the events propagated in their delegate callbacks.
Currently supported XMPP extensions
This pull request introduces several message processing modules together with their respective storage implementation helpers:
XMPPOneToOneChat- a very basic one-to-one chat handling module, provided as an equivalent to the existing groupchat modules.XMPPDelayedDelivery- in the existing storage implementations XEP-0203 handling used to be hardcoded, now it can be implemented on par with the remaining XEPs.XMPPManagedMessaging- handles XEP-0198 message acknowledgement statuses.XMPPLastMessageCorrection- implements XEP-0308 message corrections.XMPPOutOfBandResourceMessaging- manages XEP-0066 auxiliary resource URIs embedded in messages.Storage handling has also been added for certain existing modules:
XMPPRoomLightXMPPMessageArchiveManagementXMPPMessageDeliveryReceiptsAlternatives to this pull request
We understand that this is a rather large-scale pull request. If you believe that it extends beyond the framework's scope, please let us know if you are interested in merging some portions of it. The un-squashed branches are as follows:
wip/stream-delegate-context-tracing- implements the stream element events feature.wip/one-to-one-chat,wip/delayed-delivery-module,wip/managed-messaging-module,wip/last-message-correction-module,wip/out-of-band-resource-messaging- these implement the new modules.wip/universal-message-storage-base- implements the storage, but does not include the helpers for any modules.