Skip to content

Frequently Asked Questions

Robbie Hanson edited this page Sep 10, 2013 · 24 revisions

Are there any "best practices" I should know about?

There are a few best practices you should follow in order to prevent "shooting yourself in the foot". These best practices follow naturally from a basic understanding of how YapDatabase works. Thus, I'll highlight the basics first, and then list the associated best practices.

For more detail, see the Performance Primer article.

  • Every YapDatabaseConnection only supports a single transaction at a time.

Essentially, each YapDatabaseConnection has an internal serial queue. All transactions on the connection must go through the connection's serial queue. This includes both read-only transactions and read-write transactions. It also includes async transactions.

This means that connections are thread-safe. That is, you can use safely use a single connection in multiple threads. But do not mistake thread-safety for concurrency. Thread-safe != concurrency.

A read-write transaction on connectionA will block read-only transactions on connectionA until the read-write transaction completes. (Even if its an asyncReadWrite transaction.)

You can get a similar effect if you have a really really expensive read-only transaction. For example, you loop over every object in the database and perform some expensive complex math for each one. If you do this expensive read-only transaction on connectionA in a background thread, you're still blocking connectionA for other threads.

  • Concurrency comes through using multiple connections.

Concurrency in YapDatabase is incredibly simple to achieve. You just create and use multiple connections. And creating a new connection is a one-liner.

This brings us to Best Practice #1 :

  • Be mindful of read-write transactions & expensive read-only transactions
  • Perform such transactions on dedicated connections

And speaking of read-write transactions...

  • A database can only perform a single read-write transaction at a time.

This is an inherit limitation of sqlite. And it means that even if you have multiple YapDatabaseConnection's, all your readWrite transactions will execute in a serial fashion.

Recall that each YapDatabaseConnection has a serial queue, and that all transactions on that connection go through the connection's serial queue. In a similar fashion, YapDatabase has a serial queue for read-write transactions, and all read-write transactions (regardless of connection) must go through this "global" serial queue.

Which brings us to Best Practice #2 :

  • Never use a read-write transaction if a read-only transaction will do.

A read-only transaction is more lightweight than a read-write transaction. Plus read-only transactions increase concurrency.

The great thing about a read-only transaction on connectionA is that it can execute in parallel with a read-write transaction on connectionB.

And this means that you can easily avoid blocking your main thread.

Which brings us to Best Practice #3 :

  • Use a dedicated connection for the main thread
  • Do not use this connection anywhere but on your main thread
  • Do not execute any readWrite transactions with this connection
  • Only execute read-only transactions with this connection
  • Create separate YapDatabaseConnection(s) for background operations
  • Use these separate connections to do your readWrite transactions

The rationale behind this last "best practice" should be understandable. You don't want to block the UI thread. So you have a dedicated read-only connection for it. Which means that it only executes read-only transactions. Which means it won't ever block due to "expensive" read-write transactions.

Now having a read-only connection means you're going to need a way to notify the main thread when changes have occurred that require updating UI components (such as a UITableView).

Which brings us to Best Practice #4 :

Follow these best practices and you'll enjoy just how simple and powerful YapDatabase can be.


Can I use KVO on objects?

Key-Value Observing in a database system is dependent upon several things. First, the objects that you fetch from the database must be mutable. Second, the mutable objects would need to be automatically updated by something. As in, changes to objects made on other threads/connections must get merged into the objects you already have in your hand (the objects you've already fetched from the database) on your thread. In order for this to happen:

  • objects must be tied to a specific connection (so the connection knows what objects to update)
  • objects must be mutable (so the connection can update them)

In order to satisfy these conditions we wind up going down the same road that has made Core Data such a pain. That is, our objects become non-thread-safe, and tied to a specific connection. Furthermore, it becomes mandatory (not optional) to use KVO as our objects might get changed underneath us at any time.

In addition to this, all objects that get stored in the database would need to support some kind of merge operation. At first this might seem feasible. But the feasibility comes into question when we realize YapDatabase can store basic objects such as NSString's, NSNumber's, etc. And this is why Core Data requires "object wrappers" for everything. Even if you just want to store a simple string in the database, it has to be wrapped in some NSManagedObject wrapper.

The fundamental architecture and philosophy behind YapDatabase is radically different from Core Data.

YapDatabase:

  • Key/value oriented with extensions
  • Connections are thread safe
  • Fetched objects are "bare" objects
  • Straightforward concurrency

Core Data

  • Object & relationship oriented
  • NSManagedObjectContext is not thread safe
  • Fetched objects are subclasses of NSManagedObject wrapper class
  • Fetched objects are tied to a specific context and are thus not thread-safe
  • Each context monitors and manages every fetched object
  • Concurrency requires manual merges and conflict resolution

Long story short, pure KVO observing is not supported by YapDatabase. Doing so would require us to make concessions that would defeat the original purpose of the project. However, YapDatabase does support a method of observing changes to specific keys / objects.

You can register for the YapDatabaseModifiedNotification. When you receive notification(s), simply pass the notification object to the connection to see if any particular keys (which you may be "observing") have changed.

See the above linked wiki page for some code samples. See also the YapDatabaseConnection & YapCollectionsDatabaseConnection header files for more API information.

From YapDatabaseConnection.h :

- (BOOL)hasChangeForKey:(NSString *)key inNotifications:(NSArray *)notifications;
- (BOOL)hasObjectChangeForKey:(NSString *)key inNotifications:(NSArray *)notifications;
- (BOOL)hasMetadataChangeForKey:(NSString *)key inNotifications:(NSArray *)notifications;

- (BOOL)hasChangeForAnyKeys:(NSSet *)key inNotifications:(NSArray *)notifications;
- (BOOL)hasObjectChangeForAnyKeys:(NSSet *)keys inNotifications:(NSArray *)notifications;
- (BOOL)hasMetadataChangeForAnyKeys:(NSSet *)keys inNotifications:(NSArray *)notifications;
Clone this wiki locally