Share
Embed
Pragmatic Core Data
Florian Kugler
January 20, 2016
Transcript
58 slides
-
I want to show you how to reason about it
for yourself -
┌─────────────────────────────────┐ │ Managed Object Context │ │ │ │ ┌───────────┐
┌───────────┐ │ │ │ Managed │ │ Managed │ │ │ │ Object │ │ Object │ │ │ └───────────┘ └───────────┘ │ └─────────────────────────────────┘ ▲ │ ▼ ┌─────────────────────────────────┐ │ Persistent Store Coordinator │ │ │ │ ┌─────────────────────────┐ │ │ │ Persistent Store │ │ │ └─────────────────────────┘ │ │ ▲ │ └────────────────┼────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ SQLite │ └─────────────────────────────────┘ -
┌─────────────────────────────────┐ │ Managed Object Context │ │ │ │ ┌───────────┐
┌───────────┐ │ │ │ Managed │ │ Managed │ │ Context Tier │ │ Object │ │ Object │ │ │ └───────────┘ └───────────┘ │ └─────────────────────────────────┘ ▲ ─ ─ ─ ─ ─ ─ ─ ─ ─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ▼ ┌─────────────────────────────────┐ │ Persistent Store Coordinator │ │ │ │ ┌─────────────────────────┐ │ Coordinator Tier │ │ Persistent Store │ │ │ └─────────────────────────┘ │ │ ▲ │ └────────────────┼────────────────┘ ─ ─ ─ ─ ─ ─ ─ ─ ─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ▼ ┌─────────────────────────────────┐ │ SQLite │ SQLite Tier └─────────────────────────────────┘ -
Working in the Context Tier → Testing for faults →
Accessing properties (on materialized objects) → Traversing to-one relationships (on materialized objects) → Traversing to-many relationships (on materialized relationships) -
Working in the Context Tier → Faulting in objects with
objectWithID(_:) → objectRegisteredForID(_:) → Making changes → (Saving a child context) -
The Coordinator Tier → In-memory → Thread safe → Potential
lock contention -
Working in the Coordinator Tier → Accessing properties (on faults
whose data is in the row cache) -
The SQLite Tier → Disk I/O → Thread-safe → Potential
lock contention -
Working with the SQLite Tier → Fetch requests → resultType
→ includesPropertyValues → returnsObjectsAsFaults → shouldRefreshRefetchedObjects → includesPendingChanges -
Application │ ▲ │ │ │ Object │ Faults ───────────────────────────────┼──────────────────────────────────────┼──────────
Context ▼ │ ┌─────────────────────────────────────────────────────┼─────────┐ │ executeFetchRequest(_:) │ │ │ Accounts for │ │ pending changes │ └───────────────────────────────────────────────────────────────┘ │ ▲ │ │ │ Object Faults ───────────────────────────────┼──────────────────────────────────────┼────────── Coordinator │ │ ┌ ─ ─ ─ ─ ─ ─ ─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┼ ─ ─ ─ ─ ┐ Store ▼ │ │ ┌────────────────────────────────────────────────────────┐ │ │ executeRequest(_:withContext:) │ │ └────────────────────────────────────────────────────────┘ │ │ ▲ │ │ ┌───────────────┐ │ │ │ │ Row Cache │⾢─────────┤ │ SQL statement └───────────────┘ │ │ ─ ─ ─ ─ ─ ─ ─ ┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│─ ─ ─ ─ ─ │ │ ───────────────────────────────┼──────────────────────────────────────┼────────── SQLite │ │ │ Returns raw data │ │ └──────────▶ Executes Query ───────────┘ -
Working with the SQLite Tier → Saving (root contexts) →
Accessing properties (on faults whose data isn't in the row cache) -
SQLDebug Output sql: SELECT t0.Z_ENT, t0.Z_PK, t0.Z_OPT, t0.ZMARKEDFORDELETIONDATE, t0.ZNUMBEROFMOODS, t0.ZNUMERICISO3166CODE,
t0.ZUNIQUENESSDUMMY, t0.ZUPDATEDAT, t0.ZNUMBEROFCOUNTRIES, t0.ZCONTINENT FROM ZGEOGRAPHICREGION t0 WHERE t0.Z_PK = ? annotation: sql connection fetch time: 0.0005s annotation: total fetch execution time: 0.0008s for 1 rows. annotation: fault fulfilled from database for : 0xd000000000100002 <x-coredata://DE6497F9-8B94-420D-81B7-E25B992E28C2/Country/p4> -
Understand Core Data's performance characteristics Reason about your app's behavior
Make the right choice for your project -
Parallel Contexts ┌─────────────────────────┐ ┌─────────────────────────┐ │ Main Queue Context │ │
Private Queue Context │ └────────────▲────────────┘ └────────────▲────────────┘ └─────────────┬─────────────┘ │ ┌──────────────────────────▼──────────────────────────┐ │ Persistent Store Coordinator │ │ │ │ ┌───────────────────────────────────────────────┐ │ │ │ Persistent Store │ │ │ └───────────────────────▲───────────────────────┘ │ └──────────────────────────┼──────────────────────────┘ │ ┌──────────────────────────▼──────────────────────────┐ │ SQLite │ └─────────────────────────────────────────────────────┘ -
Parallel Contexts Advantages → Requests don't block the other context's
queue → Merging changes can ignore objects that are not used -
Multiple Coordinators ┌──────────────────────────────────┐ ┌──────────────────────────────────┐ │ Main Queue Context │ │
Private Queue Context │ └─────────────────▲────────────────┘ └─────────────────▲────────────────┘ │ │ ┌─────────────────▼────────────────┐ ┌─────────────────▼────────────────┐ │ Persistent Store Coordinator │ │ Persistent Store Coordinator │ │ │ │ │ │ ┌────────────────────────────┐ │ │ ┌────────────────────────────┐ │ │ │ Persistent Store │ │ │ │ Persistent Store │ │ │ └──────────────▲─────────────┘ │ │ └─────────────▲──────────────┘ │ └─────────────────┼────────────────┘ └────────────────┼─────────────────┘ │ │ ┌─────────────────▼────────────────────────────────────▼─────────────────┐ │ SQLite │ └────────────────────────────────────────────────────────────────────────┘ -
Multiple Coordinators Advantages → Context and coordinator tier are independent
→ Point of contention pushed down to SQLite -
Multiple Coordinators Disadvantages → Stacks don't share the row cache
→ Can't share object IDs between stacks (Use URIRepresentation) -
Nested Contexts ┌─────────────────────────────────────────────────────┐ │ Private Queue Context │ └──────────────────────────▲──────────────────────────┘ │
┌──────────────────────────▼──────────────────────────┐ │ Main Queue Context │ └──────────────────────────▲──────────────────────────┘ │ ┌──────────────────────────▼──────────────────────────┐ │ Private Queue Context │ └──────────────────────────▲──────────────────────────┘ │ ┌──────────────────────────▼──────────────────────────┐ │ Persistent Store Coordinator │ │ │ │ ┌───────────────────────────────────────────────┐ │ │ │ Persistent Store │ │ │ └───────────────────────▲───────────────────────┘ │ └──────────────────────────┼──────────────────────────┘ │ ┌──────────────────────────▼──────────────────────────┐ │ SQLite │ └─────────────────────────────────────────────────────┘ -
Nested Contexts Advantages → Save I/O happens on background queue
→ No race conditions to handle when deleting objects -
Nested Contexts Disadvantages → Requests in worker contexts block main
queue → Saving a child pushes all changes into the parent → Every save has to go through the main context → No merge policies → Complexity of temporary vs. permanent IDs -
Two-Step Optimistic Locking 1. Between the snapshot and the data
in the persistent store's row cache 2. Between the persistent store's row cache and the data in SQLite -
Context-Coordinator Conflict ┌─────────────────────────────────┐ ┌─────────────────────────────────┐ │ Managed Object Context │ │
Managed Object Context │ │ │ │ │ │ ┌─────────────┌────────────┐ │ │ ┌──────────────┌────────────┐ │ │ │ Snapshot V1 │ Foo │ │ │ │ Snapshot V1 │ Foo │ │ │ └─────────────└────────────┘ │ │ └──────────────└────────────┘ │ └─────────────────────────────────┘ └─────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────────────┐ │ Persistent Store Coordinator │ │ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ ┌─────────────┐ │ │ │ │ │ Snapshot V1 │ Row Cache │ │ │ │ └─────────────┘ │ │ │ └────────────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────────────┘ -
Context-Coordinator Conflict ┌─────────────────────────────────┐ ┌─────────────────────────────────┐ │ Managed Object Context │ │
Managed Object Context │ │ │ │ │ │ ┌─────────────┌────────────┐ │ │ ┌──────────────┌────────────┐ │ │ │ Snapshot V1 │ Foo │ │ │ │ Snapshot V2 │ Foo │ │ │ └─────────────└────────────┘ │ │ └──────────────└────────────┘ │ └─────────────────────────────────┘ └─────────────────────────────────┘ │ ┌───────save──────┘ ▼ ┌──────────────────────────────────────────────────────────────────────┐ │ Persistent Store Coordinator │ │ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ ┌─────────────┐ │ │ │ │ │ Snapshot V2 │ Row Cache │ │ │ │ └─────────────┘ │ │ │ └────────────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────────────┘ -
Context-Coordinator Conflict ┌─────────────────────────────────┐ ┌─────────────────────────────────┐ │ Managed Object Context │ │
Managed Object Context │ │ │ │ │ │ ┌─────────────┌────────────┐ │ │ ┌──────────────┌────────────┐ │ │ │ Snapshot V1 │ Foo │ │ │ │ Snapshot V2 │ Foo │ │ │ └─────────────└────────────┘ │ │ └──────────────└────────────┘ │ └─────────────────────────────────┘ └─────────────────────────────────┘ │ └──────fail!───────┐ ▼ ┌──────────────────────────────────────────────────────────────────────┐ │ Persistent Store Coordinator │ │ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ ┌─────────────┐ │ │ │ │ │ Snapshot V2 │ Row Cache │ │ │ │ └─────────────┘ │ │ │ └────────────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────────────┘ -
Coordinator-SQLite Conflict ┌─────────────────────────────────────────────┐ ┌─────────────────────────────────────────────┐ │ Persistent Store Coordinator │ │
Persistent Store Coordinator │ │ │ │ │ │ ┌───────────────────────────────────────┐ │ │ ┌───────────────────────────────────────┐ │ │ │ ┌─────────────┐ │ │ │ │ ┌─────────────┐ │ │ │ │ │ Snapshot V1 │ Row Cache │ │ │ │ │ Snapshot V1 │ Row Cache │ │ │ │ └─────────────┘ │ │ │ │ └─────────────┘ │ │ │ └───────────────────────────────────────┘ │ │ └───────────────────────────────────────┘ │ └─────────────────────────────────────────────┘ └─────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────────────────────────────────────┐ │ SQLite │ │ │ │ ┌────────────────────────────────────────────────────────────────────────────────────────┐ │ │ │ Row V1 │ │ │ └────────────────────────────────────────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────────────────────────────────────┘ -
Coordinator-SQLite Conflict ┌─────────────────────────────────────────────┐ ┌─────────────────────────────────────────────┐ │ Persistent Store Coordinator │ │
Persistent Store Coordinator │ │ │ │ │ │ ┌───────────────────────────────────────┐ │ │ ┌───────────────────────────────────────┐ │ │ │ ┌─────────────┐ │ │ │ │ ┌─────────────┐ │ │ │ │ │ Snapshot V1 │ Row Cache │ │ │ │ │ Snapshot V2 │ Row Cache │ │ │ │ └─────────────┘ │ │ │ │ └─────────────┘ │ │ │ └───────────────────────────────────────┘ │ │ └───────────────────────────────────────┘ │ └─────────────────────────────────────────────┘ └─────────────────────────────────────────────┘ │ ┌─────────save──────────┘ ▼ ┌──────────────────────────────────────────────────────────────────────────────────────────────┐ │ SQLite │ │ │ │ ┌────────────────────────────────────────────────────────────────────────────────────────┐ │ │ │ Row V2 │ │ │ └────────────────────────────────────────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────────────────────────────────────┘ -
Coordinator-SQLite Conflict ┌─────────────────────────────────────────────┐ ┌─────────────────────────────────────────────┐ │ Persistent Store Coordinator │ │
Persistent Store Coordinator │ │ │ │ │ │ ┌───────────────────────────────────────┐ │ │ ┌───────────────────────────────────────┐ │ │ │ ┌─────────────┐ │ │ │ │ ┌─────────────┐ │ │ │ │ │ Snapshot V1 │ Row Cache │ │ │ │ │ Snapshot V2 │ Row Cache │ │ │ │ └─────────────┘ │ │ │ │ └─────────────┘ │ │ │ └───────────────────────────────────────┘ │ │ └───────────────────────────────────────┘ │ └─────────────────────────────────────────────┘ └─────────────────────────────────────────────┘ │ └─────────fail!──────────┐ ▼ ┌──────────────────────────────────────────────────────────────────────────────────────────────┐ │ SQLite │ │ │ │ ┌────────────────────────────────────────────────────────────────────────────────────────┐ │ │ │ Row V2 │ │ │ └────────────────────────────────────────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────────────────────────────────────┘