added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / core / realm / sync / history.hpp
1 /*************************************************************************
2  *
3  * REALM CONFIDENTIAL
4  * __________________
5  *
6  *  [2011] - [2015] Realm Inc
7  *  All Rights Reserved.
8  *
9  * NOTICE:  All information contained herein is, and remains
10  * the property of Realm Incorporated and its suppliers,
11  * if any.  The intellectual and technical concepts contained
12  * herein are proprietary to Realm Incorporated
13  * and its suppliers and may be covered by U.S. and Foreign Patents,
14  * patents in process, and are protected by trade secret or copyright law.
15  * Dissemination of this information or reproduction of this material
16  * is strictly forbidden unless prior written permission is obtained
17  * from Realm Incorporated.
18  *
19  **************************************************************************/
20
21 #include <memory>
22 #include <string>
23
24 #include <realm/impl/cont_transact_hist.hpp>
25 #include <realm/sync/instruction_replication.hpp>
26 #include <realm/sync/transform.hpp>
27
28 #ifndef REALM_SYNC_HISTORY_HPP
29 #define REALM_SYNC_HISTORY_HPP
30
31 namespace realm {
32 namespace sync {
33
34 /// SyncProgress is the progress sent by the server in the download message. The
35 /// server scans through its history in connection with every download message.
36 /// scan_server_version is the server_version of the changeset at which the
37 /// server ended the scan. scan_client_version is the client_version for this
38 /// client that was last integrated before scan_server_version.
39 /// latest_server_version is the end of the server history, and
40 /// latest_server_session_ident is the server_session_ident corresponding to
41 /// latest_sever_version.  latest_client_version is the corresponding
42 /// client_version.  In other words, latest_client_version is the very latest
43 /// version of a changeset originating from this client.
44 ///
45 /// The client persists the entire progress. It is not very important to persist
46 /// latest_server_version, but for consistency the entire progress is persisted.
47 struct SyncProgress {
48     using version_type = HistoryEntry::version_type;
49
50     version_type scan_server_version = 0;
51     version_type scan_client_version = 0;
52     version_type latest_server_version = 0;
53     std::int_fast64_t latest_server_session_ident = 0;
54     version_type latest_client_version = 0;
55     int_fast64_t downloadable_bytes = 0;
56 };
57
58
59 class ClientHistoryBase :
60         public InstructionReplication {
61 public:
62     using version_type    = TrivialReplication::version_type;
63     using file_ident_type = HistoryEntry::file_ident_type;
64     using salt_type       = std::int_fast64_t;
65     using SyncTransactCallback = void(VersionID old_version, VersionID new_version);
66
67     /// Get the version of the latest snapshot of the associated Realm, as well
68     /// as the file identifier pair and the synchronization progress pair as
69     /// they are stored in that snapshot.
70     ///
71     /// The returned current client version is the version of the latest
72     /// snapshot of the associated SharedGroup object, and is guaranteed to be
73     /// zero for the initial empty Realm state.
74     ///
75     /// The returned file identifier pair (server, client) is the one that was
76     /// last stored by set_file_ident_pair(). If no identifier pair has been
77     /// stored yet, \a client_file_ident is set to zero.
78     ///
79     /// The returned SyncProgress is the one that was last stored by
80     /// set_sync_progress(), or {} if set_sync_progress() has never been called
81     /// for the associated Realm file.
82     virtual void get_status(version_type& current_client_version,
83                             file_ident_type& server_file_ident,
84                             file_ident_type& client_file_ident,
85                             salt_type& client_file_ident_salt,
86                             SyncProgress& progress) const = 0;
87
88     /// Stores the server assigned file identifier pair (server, client) in the
89     /// associated Realm file, such that it is available via get_status() during
90     /// future synchronization sessions. It is an error to set this identifier
91     /// pair more than once per Realm file.
92     ///
93     /// \param server_file_ident The server assigned server-side file
94     /// identifier. This can be any non-zero integer strictly less than 2**64.
95     /// The server is supposed to choose a cryptic value that cannot easily be
96     /// guessed by clients (intentionally or not), and its only purpose is to
97     /// provide a higher level of fidelity during subsequent identification of
98     /// the server Realm. The server does not have to guarantee that this
99     /// identifier is unique, but in almost all cases it will be. Since the
100     /// client will also always specify the path name when referring to a server
101     /// file, the lack of a uniqueness guarantee is effectively not a problem.
102     ///
103     /// \param client_file_ident The server assigned client-side file
104     /// identifier. A client-side file identifier is a non-zero positive integer
105     /// strictly less than 2**64. The server guarantees that all client-side
106     /// file identifiers generated on behalf of a particular server Realm are
107     /// unique with respect to each other. The server is free to generate
108     /// identical identifiers for two client files if they are associated with
109     /// different server Realms.
110     ///
111     /// The client is required to obtain the file identifiers before engaging in
112     /// synchronization proper, and it must store the identifiers and use them
113     /// to reestablish the connection between the client file and the server
114     /// file when engaging in future synchronization sessions.
115     virtual void set_file_ident_pair(file_ident_type server_file_ident,
116                                      file_ident_type client_file_ident,
117                                      salt_type client_file_ident_salt) = 0;
118
119     /// Stores the SyncProgress progress in the associated Realm file in a way
120     /// that makes it available via get_status() during future synchronization
121     /// sessions. Progress is reported by the server in the DOWNLOAD message.
122     ///
123     /// See struct SyncProgress for a description of \param progress.
124     ///
125     /// `progress.scan_client_version` has an effect on the process by which old
126     /// history entries are discarded.
127     ///
128     /// `progress.scan_client_version` The version produced on this client by
129     /// the last changeset, that was sent to, and integrated by the server at
130     /// the time `progress.scan_server_version was produced, or zero if
131     /// `progress.scan_server_version` is zero.
132     ///
133     /// Since all changesets produced after `progress.scan_client_version` are
134     /// potentially needed during operational transformation of the next
135     /// changeset received from the server, the implementation of this class
136     /// must promise to retain all history entries produced after
137     /// `progress.scan_client_version`. That is, a history entry with a
138     /// changeset, that produces version V, is guaranteed to be retained as long
139     /// as V is strictly greater than `progress.scan_client_version`.
140     ///
141     /// It is an error to specify a client version that is less than the
142     /// currently stored version, since there is no way to get discarded history
143     /// back.
144     virtual void set_sync_progress(SyncProgress progress) = 0;
145
146 /*
147     /// Get the first history entry whose changeset produced a version that
148     /// succeeds `begin_version` and, and does not succeed `end_version`, whose
149     /// changeset was not produced by integration of a changeset received from
150     /// the server, and whose changeset was not empty.
151     ///
152     /// \param begin_version, end_version The range of versions to consider. If
153     /// `begin_version` is equal to `end_version`, this is the empty range. If
154     /// `begin_version` is zero, it means that everything preceding
155     /// `end_version` is to be considered, which is again the empty range if
156     /// `end_version` is also zero. Zero is a special value in that no changeset
157     /// produces that version. It is an error if `end_version` precedes
158     /// `begin_version`, or if `end_version` is zero and `begin_version` is not.
159     ///
160     /// \param buffer Owner of memory referenced by entry.changeset upon return.
161     ///
162     /// \return The version produced by the changeset of the located history
163     /// entry, or zero if no history entry exists matching the specified
164     /// criteria.
165     virtual version_type find_history_entry_for_upload(version_type begin_version,
166                                                        version_type end_version,
167                                                        HistoryEntry& entry,
168                                                        std::unique_ptr<char[]>& buffer) const = 0;
169 */
170
171
172     struct UploadChangeset {
173         HistoryEntry::timestamp_type timestamp;
174         version_type client_version;
175         version_type server_version;
176         ChunkedBinaryData changeset;
177         std::unique_ptr<char[]> buffer;
178     };
179
180     /// find_uploadable_changesets() returns a vector of history entries. The
181     /// history entries are returned in order and starts from the first available
182     /// entry. The number of entries returned is at least one, if possible, and
183     /// is size limited by a soft limit. Returned changesets produced a version
184     /// that succeeds `begin_version` and, and does not succeed `end_version`.
185     /// Returned changesets are also locally produced and non-empty.
186     virtual std::vector<UploadChangeset> find_uploadable_changesets(version_type begin_version,
187                                                                     version_type end_version) const = 0;
188
189     using RemoteChangeset = Transformer::RemoteChangeset;
190
191     // FIXME: Apparently, this feature is expected by object store, but why?
192     // What is it ultimately used for? (@tgoyne)
193     class SyncTransactReporter {
194     public:
195         virtual void report_sync_transact(VersionID old_version, VersionID new_version) = 0;
196     protected:
197         ~SyncTransactReporter() {}
198     };
199
200     /// \brief Integrate a sequence of remote changesets using a single Realm
201     /// transaction.
202     ///
203     /// Each changeset will be transformed as if by a call to
204     /// Transformer::transform_remote_changeset(), and then applied to the
205     /// associated Realm.
206     ///
207     /// As a final step, each changeset will be added to the local history (list
208     /// of applied changesets).
209     ///
210     /// \param progress is the SyncProgress received in the download message.
211     /// Progress will be persisted along with the changesets.
212     ///
213     /// \param num_changesets The number of passed changesets. Must be non-zero.
214     ///
215     /// \param callback An optional callback which will be called with the
216     /// version immediately processing the sync transaction and that of the sync
217     /// transaction.
218     ///
219     /// \return The new local version produced by the application of the
220     /// transformed changeset.
221     virtual version_type integrate_remote_changesets(SyncProgress progress,
222                                                      const RemoteChangeset* changesets,
223                                                      std::size_t num_changesets,
224                                                      util::Logger* replay_logger,
225                                                      SyncTransactReporter* = nullptr) = 0;
226
227 protected:
228     ClientHistoryBase(const std::string& realm_path);
229 };
230
231
232
233 class ClientHistory : public ClientHistoryBase {
234 public:
235     class ChangesetCooker;
236     class Config;
237
238     /// Get the persisted upload/download progress in bytes.
239     virtual void get_upload_download_bytes(uint_fast64_t& downloaded_bytes,
240                                            uint_fast64_t& downloadable_bytes,
241                                            uint_fast64_t& uploaded_bytes,
242                                            uint_fast64_t& uploadable_bytes,
243                                            uint_fast64_t& snapshot_version) = 0;
244
245     /// See set_cooked_progress().
246     struct CookedProgress {
247         std::int_fast64_t changeset_index = 0;
248         std::int_fast64_t intrachangeset_progress = 0;
249     };
250
251     /// Returns the persisted progress that was last stored by
252     /// set_cooked_progress().
253     ///
254     /// Initially, until explicitly modified, both
255     /// `CookedProgress::changeset_index` and
256     /// `CookedProgress::intrachangeset_progress` are zero.
257     virtual CookedProgress get_cooked_progress() const = 0;
258
259     /// Persistently stores the point of progress of the consumer of cooked
260     /// changesets.
261     ///
262     /// As well as allowing for later retrieval, the specification of the point
263     /// of progress of the consumer of cooked changesets also has the effect of
264     /// trimming obsolete cooked changesets from the Realm file. Indeed, if this
265     /// function is never called, but cooked changesets are continually being
266     /// produced, then the Realm file will grow without bounds.
267     ///
268     /// Behavior is undefined if the specified index
269     /// (CookedProgress::changeset_index) is lower than the index returned by
270     /// get_cooked_progress().
271     ///
272     /// The intrachangeset progress field
273     /// (CookedProgress::intrachangeset_progress) will be faithfully persisted,
274     /// but will otherwise be treated as an opaque object by the history
275     /// internals.
276     virtual void set_cooked_progress(CookedProgress) = 0;
277
278     /// Get the number of cooked changesets so far produced for this Realm. This
279     /// is the number of cooked changesets that are currently in the Realm file
280     /// plus the number of cooked changesets that have been trimmed off so far.
281     virtual std::int_fast64_t get_num_cooked_changesets() const = 0;
282
283     /// Fetch the cooked changeset at the specified index.
284     ///
285     /// Cooked changesets are made available in the order they are produced by
286     /// the changeset cooker (ChangesetCooker).
287     ///
288     /// Behaviour is undefined if the specified index is less than the index
289     /// (CookedProgress::changeset_index) returned by get_cooked_progress(), or
290     /// if it is greater than, or equal to the toal number of cooked changesets
291     /// (as returned by get_num_cooked_changesets()).
292     ///
293     /// The callee must append the bytes of the located cooked changeset to the
294     /// specified buffer, which does not have to be empty initially.
295     virtual void get_cooked_changeset(std::int_fast64_t index,
296                                       util::AppendBuffer<char>&) const = 0;
297
298 protected:
299     ClientHistory(const std::string& realm_path);
300 };
301
302
303 /// \brief Abstract interface for changeset cookers.
304 ///
305 /// Note, it is completely up to the application to decide what a cooked
306 /// changeset is. History objects (instances of ClientHistory) are required to
307 /// treat cooked changesets as opaque entities. For an example of a concrete
308 /// changeset cooker, see TrivialChangesetCooker which defines the cooked
309 /// changesets to be identical copies of the raw changesets.
310 class ClientHistory::ChangesetCooker {
311 public:
312     /// \brief An opportunity to produce a cooked changeset.
313     ///
314     /// When the implementation chooses to produce a cooked changeset, it must
315     /// write the cooked changeset to the specified buffer, and return
316     /// true. When the implementation chooses not to produce a cooked changeset,
317     /// it must return false. The implementation is allowed to write to the
318     /// buffer, and return false, and in that case, the written data will be
319     /// ignored.
320     ///
321     /// \param prior_state The state of the local Realm on which the specified
322     /// raw changeset is based.
323     ///
324     /// \param changeset, changeset_size The raw changeset.
325     ///
326     /// \param buffer The buffer to which the cooked changeset must be written.
327     ///
328     /// \return True if a cooked changeset was produced. Otherwise false.
329     virtual bool cook_changeset(const Group& prior_state,
330                                 const char* changeset, std::size_t changeset_size,
331                                 util::AppendBuffer<char>& buffer) = 0;
332 };
333
334
335 class ClientHistory::Config {
336 public:
337     Config() {}
338
339     /// Must be set to true if, and only if the created history object
340     /// represents (is owned by) the sync agent of the specified Realm file. At
341     /// most one such instance is allowed to participate in a Realm file access
342     /// session at any point in time. Ordinarily the sync agent is encapsulated
343     /// by the sync::Client class, and the history instance representing the
344     /// agent is created transparently by sync::Client (one history instance per
345     /// sync::Session object).
346     bool owner_is_sync_agent = false;
347
348     /// If a changeset cooker is specified, then the created history object will
349     /// allow for a cooked changeset to be produced for each changeset of remote
350     /// origin; that is, for each changeset that is integrated during the
351     /// execution of ClientHistory::integrate_remote_changesets(). If no
352     /// changeset cooker is specified, then no cooked changesets will be
353     /// produced on behalf of the created history object.
354     ///
355     /// ClientHistory::integrate_remote_changesets() will pass each incoming
356     /// changeset to the cooker after operational transformation; that is, when
357     /// the chageset is ready to be applied to the local Realm state.
358     std::shared_ptr<ChangesetCooker> changeset_cooker;
359 };
360
361 /// \brief Create a "sync history" implementation of the realm::Replication
362 /// interface.
363 ///
364 /// The intended role for such an object is as a plugin for new
365 /// realm::SharedGroup objects.
366 std::unique_ptr<ClientHistory> make_client_history(const std::string& realm_path,
367                                                    ClientHistory::Config = {});
368
369
370
371 // Implementation
372
373 inline ClientHistoryBase::ClientHistoryBase(const std::string& realm_path):
374     InstructionReplication{realm_path} // Throws
375 {
376 }
377
378 inline ClientHistory::ClientHistory(const std::string& realm_path):
379     ClientHistoryBase{realm_path} // Throws
380 {
381 }
382
383 } // namespace sync
384 } // namespace realm
385
386 #endif // REALM_SYNC_HISTORY_HPP