YapDatabaseCloudCoreOperation
@interface YapDatabaseCloudCoreOperation : NSObject <NSCoding, NSCopying>
This is the base class for concrete subclasses such as FileOperations & RecordOperations.
Do not directly create instances of this class. Instead create instances of concrete subclass.
-
Every operation has a randomly generated UUID. This is used for dependency references, and for uniquely identifying this specific operation.
Declaration
Objective-C
@property (readonly, nonatomic) NSUUID *_Nonnull uuid;
Swift
var uuid: UUID { get }
-
YapDatabase keeps track of commit numbers via its
snapshot
property.The
snapshot
can be understood as a commit number that gets incremented during every read-write transaction (for which actual changes were made to the database). Note also that thesnapshot
is stored in the database. So it’s a persistent number that continually increments across app launches. (i.e. does NOT reset to zero on app re-launch, but rather continues incrementing where it left off)The snapshot number is known for each graph. And an operation’s snapshot number is set when being added to the graph.
Declaration
Objective-C
@property (readonly, nonatomic) uint64_t snapshot;
Swift
var snapshot: UInt64 { get }
-
Every operation gets put into a single pipeline, which is in charge of scheduling & executing the operation.
You can choose to have the operation put into the default pipeline, or you can choose to have it put into a custom pipeline by specifying the registered name of the desired pipeline.
The default value is nil.
If you set a pipeline value which doesn’t match any registered pipelines (or you leave the value nil), then the operation will be placed into the default pipeline.
See
YapDatabaseOnwCloudPipelineSee
[YapDatabase registerPipeline]Declaration
Objective-C
@property (readwrite, copy, nonatomic, nullable) NSString *pipeline;
Swift
var pipeline: String? { get set }
-
At the local level, when dealing with YapDatabase, you have the benefit of atomic transactions. Thus you can make changes to multiple objects, and apply the changes in an atomic fashion. However, the cloud server may only support
transactions
involving a single file.This necessitates certain architectural decisions. One implication is that, when two objects are linked, you’ll have to decide which gets uploaded first.
Let’s look at a couple examples.
Example 1: You have a user object, and an associated avatar image (separate jpg file). So you upload the jpg file first, and then upload the user object file, which will reference the path to the jpg file on the server.
Example 2: You have a new customer object, and an associated purchase object (which references the new customer). So you upload the customer object first, and the purchase second.
In order to achieve this, you use the dependencies property, which is simply an array of UUID’s. That is, a reference to any operation.uuid that must go first.
For example 1 we might have:
- opA : /users/robbie.json
- opB : /avatars/robbie.jpg
And thus, since we want to upload the jpg first, we’d set: opA.dependencies = @[ opB.uuid ];
For example 2 we might have:
- opA : /customers/abc123.json
- opB : /purchases/xyz789.json
And thus, since we want to upload the customer first, we’d set: opB.dependencies = @[ opA.uuid ]
It’s important to understand some of the key concepts that dependencies enforce.
If there are two operations, ‘A’ & ‘B’, and B.dependencies=[A.uuid], then:
- A is always started and completed before B is started.
- This applies regardless of the priority values for A & B.
- If a conflict is encountered for A, then B is still delayed until the conflict is resolved.
This means that one of the following must occur:
- A is marked as completed
- A is marked as skipped
If you create a circular dependency, the graph system will detect it and throw an exception.
See
addDependencyDeclaration
Objective-C
@property (readwrite, copy, nonatomic, nullable) NSSet<NSUUID *> *dependencies;
Swift
var dependencies: Set<UUID>? { get set }
-
Convenience method for adding a dependency to the list.
Declaration
Objective-C
- (void)addDependency:(nonnull id)op;
Swift
func addDependency(_ op: Any)
Parameters
op
May be either a NSUUID, or a YapDatabaseCloudCoreOperation (for convenience).
-
Convenience method for adding dependencies to the list.
Declaration
Objective-C
- (void)addDependencies:(nonnull NSArray<id> *)ops;
Swift
func addDependencies(_ ops: [Any])
Parameters
ops
Each item in the array may be be either an NSUUID, or a YapDatabaseCloudCoreOperation (for convenience).
-
Every operation can optionally be assigned a priority. Operations with a higher priority will be prioritized over those with a lower priority.
There are several key concepts to keep in mind when it comes to prioritization.
Dependencies trump priority, and are the perferred mechanism to enforce a required order. For example, if you need to upload 2 files (A & B), and B.dependencies=[A], then A will always start & complete before B is started, regardless of their priority values.
Operations may be executed in parallel. If commit #34 contains operations A & B, with no dependencies, and A.priority=2 & B.priority.1, then the pipeline will start operation A before starting operation B. However, since there are no dependencies, then the pipeline may start operation B before op A has completed. And thus, operation B may actually complete before operation A. For example, if A is a large record, but B is small record.
Commit order may still be enforced. (Note: The following applies IF pipeline.algorithm == CommitGraph, but does NOT apply if pipeline.algorithm == FlatGraph):
^^ see note ^^ Let’s say you make commit #32 with operations A & B. Then you make commit #33 with operation C. Regardless of the priority of A, B & C, operations A & B will always complete before C is started. This is important to understand because it means you only have to concern yourself with the operations within a single commit. (Handling cross-commit dependencies requires a formal dependency graph, and is opt-in via the FlatGraph optimization.)
Thus it is best to think of dependencies as hard requirements, and priorities as soft hints.
Declaration
Objective-C
@property (assign, readwrite, nonatomic) int32_t priority;
Swift
var priority: Int32 { get set }
-
User-defined information to associate with the operation. This information is stored in the database along with the operation.
Typical persistent info includes things such as:
- user information needed to perform the network operation (e.g. userID)
information needed after the network operation completes (e.g. collection/key of associated database object)
See
setPersistentUserInfoObject:forKey:
Declaration
Objective-C
@property (readwrite, copy, nonatomic, nullable) NSDictionary *persistentUserInfo;
Swift
var persistentUserInfo: [AnyHashable : Any]? { get set }
-
Convenience method for modifying the persistentUserInfo dictionary.
Declaration
Objective-C
- (void)setPersistentUserInfoObject:(nonnull id)object forKey:(nonnull NSString *)key;
Swift
func setPersistentUserInfoObject(_ object: Any, forKey key: String)