Multi-Content Revisions/Page Update Controller

Updating a revision is a complex process, with complicated requirements with regards to the usage of transactional logic and deferred updates. To honor these requirements, stateful "interactor" objects are defined in addition to the stateless storage service:

(Code experiment: https://gerrit.wikimedia.org/r/#/c/217710/)
 * PageUpdateController shall be used to create new revisions when a page is edited (or created). RevisionUpdateController can be used to update derived content of an existing revision. There is no need to implement RevisionUpdateController initially, but the concept should be kept in mind.
 * Application code can acquire an PageUpdateController from a factory. As a first step, WikiPage can act as that factory.
 * The logic for updating the page and revision tables will move from WikiPage (and Revision) to PageUpdateController. The save method will take the place of WikiPage::doEditConent.
 * PageUpdateController will rely on the RevisionSlotStore to store content meta-data, and on BlobStore (or ContentStore) to store the actual content.

Page Update Process

 * When a page is edited, the content of at least one primary slot is updated. It does not matter whether a slot with the same role existed in the previous revision.
 * One edit (user interaction) creates one revision, regardless of how many slots were updated (see also Content Meta-Data).
 * Unchanged content of primary slots is re-used from previous revisions. E.g.:
 * Revision 1 has two slots, A and B, with content ( A1, B1 )
 * Now, slot A is edited, but slot B is untouched. Then revision 2 is ( A2, B1 ). That is, slot B in revision two is the same content as slot B of revision 1.
 * Derived slots are re-calculated when primary slots change (similarly to how we already handle secondary data updates like LinksUpdate, and pre-cached data like ParserOutput).
 * Derived (but not primary) content of a revision can be updated without creating a new revision.


 * SecondaryDataUpdates are created and executed for all content objects of a revision, old or new, primary or derived.

Challanges

 * Maintaining backwards compatibility for hooks may be a major challange. For the initial implementation, the PageUpdateController will have to know the WikiPage instance, so it can provide it to hooks as a parameter.
 * SecondaryDataUpdates (and other secondsray updates, deferred, updates, queued jobs, etc) need to be handled for all Content objects.
 * a transaction context should be maintained across the saving of all slots, see Transaction Management. The UpdateController can either controll a transaction context, or can inherit a transaction context from the factory that creates it.
 * Refactoring the code that deals with "preparing" the content for saving and the amorphous "edit" object returned from prepareContentForEdit seems tricky. Allowing the same optimizations to function when preparing multiple Content objects for saving at the same time needs thought.
 * Which code knows what derived content shall be generated upon saving a new revision?
 * It may be useful to have PageUpdateControllers on two levels: a high level PageUpdateController which does permission checks, generates derived content, and takes care of secondary data updates; and a low level PageUpdateController which does nothing but store Content objects and update meta-data.
 * the idea of derived content is tightly bound to dependency tracking: when storing derived content, we should also store what it was derived from. And when the base content changes, we need to update the derived content. Thus, dependency tracking should be implemented on the level of slots, not pages: the wikitext of a page doesn't depend on the templates it uses, but the rendered HTML of the page does.