FSTDatastore.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import <Foundation/Foundation.h>
  17. #import "FSTTypes.h"
  18. @class FSTDatabaseInfo;
  19. @class FSTDocumentKey;
  20. @class FSTDispatchQueue;
  21. @class FSTMutation;
  22. @class FSTMutationResult;
  23. @class FSTQueryData;
  24. @class FSTSnapshotVersion;
  25. @class FSTWatchChange;
  26. @class FSTWatchStream;
  27. @class FSTWriteStream;
  28. @class GRPCCall;
  29. @class GRXWriter;
  30. @protocol FSTCredentialsProvider;
  31. @protocol FSTWatchStreamDelegate;
  32. @protocol FSTWriteStreamDelegate;
  33. NS_ASSUME_NONNULL_BEGIN
  34. /**
  35. * FSTDatastore represents a proxy for the remote server, hiding details of the RPC layer. It:
  36. *
  37. * - Manages connections to the server
  38. * - Authenticates to the server
  39. * - Manages threading and keeps higher-level code running on the worker queue
  40. * - Serializes internal model objects to and from protocol buffers
  41. *
  42. * The FSTDatastore is generally not responsible for understanding the higher-level protocol
  43. * involved in actually making changes or reading data, and aside from the connections it manages
  44. * is otherwise stateless.
  45. */
  46. @interface FSTDatastore : NSObject
  47. /** Creates a new Datastore instance with the given database info. */
  48. + (instancetype)datastoreWithDatabase:(FSTDatabaseInfo *)database
  49. workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue
  50. credentials:(id<FSTCredentialsProvider>)credentials;
  51. - (instancetype)init __attribute__((unavailable("Use a static constructor method.")));
  52. - (instancetype)initWithDatabaseInfo:(FSTDatabaseInfo *)databaseInfo
  53. workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue
  54. credentials:(id<FSTCredentialsProvider>)credentials
  55. NS_DESIGNATED_INITIALIZER;
  56. /** Converts the error to a FIRFirestoreErrorDomain error. */
  57. + (NSError *)firestoreErrorForError:(NSError *)error;
  58. /** Returns YES if the given error indicates the RPC associated with it may not be retried. */
  59. + (BOOL)isPermanentWriteError:(NSError *)error;
  60. /** Returns YES if the given error is a GRPC ABORTED error. **/
  61. + (BOOL)isAbortedError:(NSError *)error;
  62. /** Looks up a list of documents in datastore. */
  63. - (void)lookupDocuments:(NSArray<FSTDocumentKey *> *)keys
  64. completion:(FSTVoidMaybeDocumentArrayErrorBlock)completion;
  65. /** Commits data to datastore. */
  66. - (void)commitMutations:(NSArray<FSTMutation *> *)mutations
  67. completion:(FSTVoidErrorBlock)completion;
  68. /** Creates a new watch stream. */
  69. - (FSTWatchStream *)createWatchStreamWithDelegate:(id<FSTWatchStreamDelegate>)delegate;
  70. /** Creates a new write stream. */
  71. - (FSTWriteStream *)createWriteStreamWithDelegate:(id<FSTWriteStreamDelegate>)delegate;
  72. /** The name of the database and the backend. */
  73. @property(nonatomic, strong, readonly) FSTDatabaseInfo *databaseInfo;
  74. @end
  75. /**
  76. * An FSTStream is an abstract base class that represents a restartable streaming RPC to the
  77. * Firestore backend. It's built on top of GRPC's own support for streaming RPCs, and adds several
  78. * critical features for our clients:
  79. *
  80. * - Restarting a stream is allowed (after failure)
  81. * - Exponential backoff on failure (independent of the underlying channel)
  82. * - Authentication via FSTCredentialsProvider
  83. * - Dispatching all callbacks into the shared worker queue
  84. *
  85. * Subclasses of FSTStream implement serialization of models to and from bytes (via protocol
  86. * buffers) for a specific streaming RPC and emit events specific to the stream.
  87. *
  88. * ## Starting and Stopping
  89. *
  90. * Streaming RPCs are stateful and need to be started before messages can be sent and received.
  91. * The FSTStream will call its delegate's specific streamDidOpen method once the stream is ready
  92. * to accept requests.
  93. *
  94. * Should a `start` fail, FSTStream will call its delegate's specific streamDidClose method with an
  95. * NSError indicating what went wrong. The delegate is free to call start again.
  96. *
  97. * An FSTStream can also be explicitly stopped which indicates that the caller has discarded the
  98. * stream and no further events should be emitted. Once explicitly stopped, a stream cannot be
  99. * restarted.
  100. *
  101. * ## Subclassing Notes
  102. *
  103. * An implementation of FSTStream needs to implement the following methods:
  104. * - `createRPCWithRequestsWriter`, should create the specific RPC (a GRPCCall object).
  105. * - `handleStreamOpen`, should call through to the stream-specific streamDidOpen method.
  106. * - `handleStreamMessage`, receives protocol buffer responses from GRPC and must deserialize and
  107. * delegate to some stream specific response method.
  108. * - `handleStreamClose`, calls through to the stream-specific streamDidClose method.
  109. *
  110. * Additionally, beyond these required methods, subclasses will want to implement methods that
  111. * take request models, serialize them, and write them to using writeRequest:.
  112. *
  113. * ## RPC Message Type
  114. *
  115. * FSTStream intentionally uses the GRPCCall interface to GRPC directly, bypassing both GRPCProtoRPC
  116. * and GRXBufferedPipe for sending data. This has been done to avoid race conditions that come out
  117. * of a loosely specified locking contract on GRXWriter. There's essentially no way to safely use
  118. * any of the wrapper objects for GRXWriter (that perform buffering or conversion to/from protos).
  119. *
  120. * See https://github.com/grpc/grpc/issues/10957 for the kinds of things we're trying to avoid.
  121. */
  122. @interface FSTStream : NSObject
  123. - (instancetype)initWithDatabase:(FSTDatabaseInfo *)database
  124. workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue
  125. credentials:(id<FSTCredentialsProvider>)credentials
  126. responseMessageClass:(Class)responseMessageClass NS_DESIGNATED_INITIALIZER;
  127. - (instancetype)init NS_UNAVAILABLE;
  128. /**
  129. * An abstract method used by `start` to create a streaming RPC specific to this type of stream.
  130. * The RPC should be created such that requests are taken from `self`.
  131. *
  132. * Note that the returned GRPCCall must not be a GRPCProtoRPC, since the rest of the streaming
  133. * mechanism assumes it is dealing in bytes-level requests and responses.
  134. */
  135. - (GRPCCall *)createRPCWithRequestsWriter:(GRXWriter *)requestsWriter;
  136. /**
  137. * Returns YES if `start` has been called and no error has occurred. YES indicates the stream is
  138. * open or in the process of opening (which encompasses respecting backoff, getting auth tokens,
  139. * and starting the actual RPC). Use `isOpen` to determine if the stream is open and ready for
  140. * outbound requests.
  141. */
  142. - (BOOL)isStarted;
  143. /** Returns YES if the underlying RPC is open and the stream is ready for outbound requests. */
  144. - (BOOL)isOpen;
  145. /**
  146. * Starts the RPC. Only allowed if isStarted returns NO. The stream is not immediately ready for
  147. * use: the delegate's watchStreamDidOpen method will be invoked when the RPC is ready for outbound
  148. * requests, at which point `isOpen` will return YES.
  149. *
  150. * When start returns, -isStarted will return YES.
  151. */
  152. - (void)start;
  153. /**
  154. * Stops the RPC. This call is idempotent and allowed regardless of the current isStarted state.
  155. *
  156. * Unlike a transient stream close, stopping a stream is permanent. This is guaranteed NOT to emit
  157. * any further events on the stream-specific delegate, including the streamDidClose method.
  158. *
  159. * NOTE: This no-events contract may seem counter-intuitive but allows the caller to
  160. * straightforwardly sequence stream tear-down without having to worry about when the delegate's
  161. * streamDidClose methods will get called. For example if the stream must be exchanged for another
  162. * during a user change this allows `stop` to be called eagerly without worrying about the
  163. * streamDidClose method accidentally restarting the stream before the new one is ready.
  164. *
  165. * When stop returns, -isStarted and -isOpen will both return NO.
  166. */
  167. - (void)stop;
  168. /**
  169. * After an error the stream will usually back off on the next attempt to start it. If the error
  170. * warrants an immediate restart of the stream, the sender can use this to indicate that the
  171. * receiver should not back off.
  172. *
  173. * Each error will call the stream-specific streamDidClose method. That method can decide to
  174. * inhibit backoff if required.
  175. */
  176. - (void)inhibitBackoff;
  177. @end
  178. #pragma mark - FSTWatchStream
  179. /** A protocol defining the events that can be emitted by the FSTWatchStream. */
  180. @protocol FSTWatchStreamDelegate <NSObject>
  181. /** Called by the FSTWatchStream when it is ready to accept outbound request messages. */
  182. - (void)watchStreamDidOpen;
  183. /**
  184. * Called by the FSTWatchStream with changes and the snapshot versions included in in the
  185. * WatchChange responses sent back by the server.
  186. */
  187. - (void)watchStreamDidChange:(FSTWatchChange *)change
  188. snapshotVersion:(FSTSnapshotVersion *)snapshotVersion;
  189. /**
  190. * Called by the FSTWatchStream when the underlying streaming RPC is closed for whatever reason,
  191. * usually because of an error, but possibly due to an idle timeout. The error passed to this
  192. * method may be nil, in which case the stream was closed without attributable fault.
  193. *
  194. * NOTE: This will not be called after `stop` is called on the stream. See "Starting and Stopping"
  195. * on FSTStream for details.
  196. */
  197. - (void)watchStreamDidClose:(NSError *_Nullable)error;
  198. @end
  199. /**
  200. * An FSTStream that implements the StreamingWatch RPC.
  201. *
  202. * Once the FSTWatchStream has called the streamDidOpen method, any number of watchQuery and
  203. * unwatchTargetId calls can be sent to control what changes will be sent from the server for
  204. * WatchChanges.
  205. */
  206. @interface FSTWatchStream : FSTStream
  207. /**
  208. * Initializes the watch stream with its dependencies.
  209. */
  210. - (instancetype)initWithDatabase:(FSTDatabaseInfo *)database
  211. workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue
  212. credentials:(id<FSTCredentialsProvider>)credentials
  213. responseMessageClass:(Class)responseMessageClass
  214. delegate:(id<FSTWatchStreamDelegate>)delegate NS_DESIGNATED_INITIALIZER;
  215. - (instancetype)initWithDatabase:(FSTDatabaseInfo *)database
  216. workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue
  217. credentials:(id<FSTCredentialsProvider>)credentials
  218. responseMessageClass:(Class)responseMessageClass NS_UNAVAILABLE;
  219. - (instancetype)init NS_UNAVAILABLE;
  220. /**
  221. * Registers interest in the results of the given query. If the query includes a resumeToken it
  222. * will be included in the request. Results that affect the query will be streamed back as
  223. * WatchChange messages that reference the targetID included in |query|.
  224. */
  225. - (void)watchQuery:(FSTQueryData *)query;
  226. /** Unregisters interest in the results of the query associated with the given target ID. */
  227. - (void)unwatchTargetID:(FSTTargetID)targetID;
  228. @property(nonatomic, weak, readonly) id<FSTWatchStreamDelegate> delegate;
  229. @end
  230. #pragma mark - FSTWriteStream
  231. @protocol FSTWriteStreamDelegate <NSObject>
  232. /** Called by the FSTWriteStream when it is ready to accept outbound request messages. */
  233. - (void)writeStreamDidOpen;
  234. /**
  235. * Called by the FSTWriteStream upon a successful handshake response from the server, which is the
  236. * receiver's cue to send any pending writes.
  237. */
  238. - (void)writeStreamDidCompleteHandshake;
  239. /**
  240. * Called by the FSTWriteStream upon receiving a StreamingWriteResponse from the server that
  241. * contains mutation results.
  242. */
  243. - (void)writeStreamDidReceiveResponseWithVersion:(FSTSnapshotVersion *)commitVersion
  244. mutationResults:(NSArray<FSTMutationResult *> *)results;
  245. /**
  246. * Called when the FSTWriteStream's underlying RPC is closed for whatever reason, usually because
  247. * of an error, but possibly due to an idle timeout. The error passed to this method may be nil, in
  248. * which case the stream was closed without attributable fault.
  249. *
  250. * NOTE: This will not be called after `stop` is called on the stream. See "Starting and Stopping"
  251. * on FSTStream for details.
  252. */
  253. - (void)writeStreamDidClose:(NSError *_Nullable)error;
  254. @end
  255. /**
  256. * An FSTStream that implements the StreamingWrite RPC.
  257. *
  258. * The StreamingWrite RPC requires the caller to maintain special `streamToken` state in between
  259. * calls, to help the server understand which responses the client has processed by the time the
  260. * next request is made. Every response may contain a `streamToken`; this value must be passed to
  261. * the next request.
  262. *
  263. * After calling `start` on this stream, the next request must be a handshake, containing whatever
  264. * streamToken is on hand. Once a response to this request is received, all pending mutations may
  265. * be submitted. When submitting multiple batches of mutations at the same time, it's okay to use
  266. * the same streamToken for the calls to `writeMutations:`.
  267. */
  268. @interface FSTWriteStream : FSTStream
  269. /**
  270. * Initializes the write stream with its dependencies.
  271. */
  272. - (instancetype)initWithDatabase:(FSTDatabaseInfo *)database
  273. workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue
  274. credentials:(id<FSTCredentialsProvider>)credentials
  275. responseMessageClass:(Class)responseMessageClass
  276. delegate:(id<FSTWriteStreamDelegate>)delegate NS_DESIGNATED_INITIALIZER;
  277. - (instancetype)initWithDatabase:(FSTDatabaseInfo *)database
  278. workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue
  279. credentials:(id<FSTCredentialsProvider>)credentials
  280. responseMessageClass:(Class)responseMessageClass NS_UNAVAILABLE;
  281. - (instancetype)init NS_UNAVAILABLE;
  282. /**
  283. * Sends an initial streamToken to the server, performing the handshake required to make the
  284. * StreamingWrite RPC work. Subsequent `writeMutations:` calls should wait until a response has
  285. * been delivered to the delegate's writeStreamDidCompleteHandshake method.
  286. */
  287. - (void)writeHandshake;
  288. /** Sends a group of mutations to the Firestore backend to apply. */
  289. - (void)writeMutations:(NSArray<FSTMutation *> *)mutations;
  290. @property(nonatomic, weak, readonly) id<FSTWriteStreamDelegate> delegate;
  291. /**
  292. * Tracks whether or not a handshake has been successfully exchanged and the stream is ready to
  293. * accept mutations.
  294. */
  295. @property(nonatomic, assign, readwrite, getter=isHandshakeComplete) BOOL handshakeComplete;
  296. /**
  297. * The last received stream token from the server, used to acknowledge which responses the client
  298. * has processed. Stream tokens are opaque checkpoint markers whose only real value is their
  299. * inclusion in the next request.
  300. *
  301. * FSTWriteStream manages propagating this value from responses to the next request.
  302. */
  303. @property(nonatomic, strong, nullable) NSData *lastStreamToken;
  304. @end
  305. NS_ASSUME_NONNULL_END