Brion VIBBER has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/350097 )
Change subject: WIP - provisional revision table restructure ...................................................................... WIP - provisional revision table restructure Revision table compaction and prep work for MCR Currently includes (untested) tables.sql and a partial patch file for offline restructure (also untested). Still todo: * apply enough changes to archive to support deletion * decide on content_text_id vs content_address * hook up the updater * start prepping code to work with this schema! * online migration state management Open questions: * migrate format of existing archive stuff? * apply 'actor', 'comment' split-out change to non-revision stuff too, or wait to later? * is internal API on Revision enough to cover many changes, or... * ...do we need better API abstractions first? Bug: T161671 Change-Id: I50ee6506ff71e7613e50c33d865199b07918c1de --- A maintenance/archives/patch-revision-restructure.sql M maintenance/tables.sql 2 files changed, 234 insertions(+), 32 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core refs/changes/97/350097/1 diff --git a/maintenance/archives/patch-revision-restructure.sql b/maintenance/archives/patch-revision-restructure.sql new file mode 100644 index 0000000..b802992 --- /dev/null +++ b/maintenance/archives/patch-revision-restructure.sql @@ -0,0 +1,109 @@ +-- @TODO consider splitting this to three patch files: setup, migration, cleanup. + +-- +-- Setup: +-- Create the new tables, and add new columns to revision... +-- + +-- the table creations are in tables.sql, need to be copied here: +-- @TODO create comment +-- @TODO create actor +-- @TODO create content +-- @TODO create slots + +-- @TODO create slot_role +-- @TODO populate slot_role +-- @TODO create content_format +-- @TODO populate content_format +-- @TODO create content_model +-- @TODO populate content_model + +-- Add new fields to revision +ALTER TABLE revision + CHANGE COLUMN rev_id rev_id bigint unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT, + ADD COLUMN rev_comment_id bigint + AFTER rev_page, + ADD COLUMN rev_actor int unsigned NOT NULL default 0 + AFTER rev_comment_id, +; +CREATE INDEX /*i*/actor_timestamp ON /*_*/revision (rev_actor,rev_timestamp); +CREATE INDEX /*i*/page_actor_timestamp ON /*_*/revision (rev_page,rev_actor,rev_timestamp); + +-- +-- Migration: +-- Move data from the old fields to the new tables, brute-force style. +-- +-- Production upgrade would not run this, instead migrating data in small bits +-- through a batch process. +-- +-- If this is run on a small wiki via update.php it should work, but requires +-- the wiki to be offline and won't provide any feedback about how long it +-- takes. +-- +-- A smarter updater plugin could do the migration in the same way we'd do it +-- in production, which might be safer. +-- + +-- Migrate comments... +INSERT INTO comment (comment_id, comment_text) + SELECT rev_id, rev_comment + FROM revision + ORDER BY rev_id; +UPDATE revision + SET rev_comment_id=rev_id + WHERE rev_comment_id IS NULL; + +-- Migrate actors... +INSERT INTO actor (actor_user) + SELECT rev_user FROM user + WHERE rev_user > 0 + GROUP BY rev_user + ORDER BY rev_user; +INSERT INTO actor (actor_text) + SELECT rev_user_text FROM revision + WHERE rev_user = 0 + GROUP BY rev_user_text + ORDER BY rev_user_text; +UPDATE revision + SET rev_actor = (SELECT actor_id FROM actor WHERE actor_user=rev_user) + WHERE rev_user > 0 AND rev_actor IS NULL; +UPDATE revision + SET rev_actor = (SELECT actor_id FROM actor WHERE actor_text=rev_user_text) + WHERE rev_user == 0 AND rev_actor IS NULL; + +-- Migrate content... +INSERT INTO content (cont_id,cont_text_id,cont_len,cont_sha1,cont_model,cont_format) + SELECT rev_id,rev_text_id,rev_len,rev_sha1,cm_id,cf_id + FROM revision + LEFT JOIN content_model ON rev_content_model=cm_model + LEFT JOIN content_format ON rev_content_format=cm_format; + +-- Migrate slot associations... +INSERT INTO slots (slot_revision,slot_content,slot_role) + SELECT rev_id,rev_id,sr_id + FROM revision + LEFT JOIN slot_role ON sr_role='default'; + +-- +-- Cleanup: +-- Once everything is migrated, we can remove the obsolete fields from +-- the revision table and their indexes. +-- +-- Production upgrade would run this on replicas when they're out of rotation. +-- + +-- Drop old fields +ALTER TABLE revision + DROP KEY user_timestamp, + DROP KEY usertext_timestamp, + DROP KEY page_user_timestamp, + + DROP COLUMN rev_comment, + DROP COLUMN rev_text_id, + DROP COLUMN rev_user, + DROP COLUMN rev_user_text, + DROP COLUMN rev_len, + DROP COLUMN rev_sha1, + DROP COLUMN rev_content_model, + DROP COLUMN rev_content_format +; diff --git a/maintenance/tables.sql b/maintenance/tables.sql index 38fef45..fec2bff 100644 --- a/maintenance/tables.sql +++ b/maintenance/tables.sql @@ -323,28 +323,19 @@ -- CREATE TABLE /*_*/revision ( -- Unique ID to identify each revision - rev_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT, + rev_id bigint unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT, -- Key to page_id. This should _never_ be invalid. rev_page int unsigned NOT NULL, - -- Key to text.old_id, where the actual bulk text is stored. - -- It's possible for multiple revisions to use the same text, - -- for instance revisions where only metadata is altered - -- or a rollback to a previous version. - rev_text_id int unsigned NOT NULL, + -- Key to comment.comment_id, where the actual comment text is stored. + -- It's possible for multiple revisions to use the same comment reference, + -- or for this to be NULL if no comment is associated. + rev_comment_id bigint, - -- Text comment summarizing the change. - -- This text is shown in the history and other changes lists, - -- rendered in a subset of wiki markup by Linker::formatComment() - rev_comment varbinary(767) NOT NULL, - - -- Key to user.user_id of the user who made this edit. - -- Stores 0 for anonymous edits and for some mass imports. - rev_user int unsigned NOT NULL default 0, - - -- Text username or IP address of the editor. - rev_user_text varchar(255) binary NOT NULL default '', + -- Key to actor.actor_id of the user who made this edit. + -- actor_user or actor_text will have the actual id or IP address. + rev_user_entry int unsigned NOT NULL default 0, -- Timestamp of when revision was created rev_timestamp binary(14) NOT NULL default '', @@ -356,21 +347,9 @@ -- Restrictions on who can access this revision rev_deleted tinyint unsigned NOT NULL default 0, - -- Length of this revision in bytes - rev_len int unsigned, - -- Key to revision.rev_id -- This field is used to add support for a tree structure (The Adjacency List Model) rev_parent_id int unsigned default NULL, - - -- SHA-1 text content hash in base-36 - rev_sha1 varbinary(32) NOT NULL default '', - - -- content model, see CONTENT_MODEL_XXX constants - rev_content_model varbinary(32) DEFAULT NULL, - - -- content format, see CONTENT_FORMAT_XXX constants - rev_content_format varbinary(64) DEFAULT NULL ) /*$wgDBTableOptions*/ MAX_ROWS=10000000 AVG_ROW_LENGTH=1024; -- In case tables are created as MyISAM, use row hints for MySQL <5.0 to avoid 4GB limit @@ -378,9 +357,123 @@ CREATE INDEX /*i*/rev_page_id ON /*_*/revision (rev_page, rev_id); CREATE INDEX /*i*/rev_timestamp ON /*_*/revision (rev_timestamp); CREATE INDEX /*i*/page_timestamp ON /*_*/revision (rev_page,rev_timestamp); -CREATE INDEX /*i*/user_timestamp ON /*_*/revision (rev_user,rev_timestamp); -CREATE INDEX /*i*/usertext_timestamp ON /*_*/revision (rev_user_text,rev_timestamp); -CREATE INDEX /*i*/page_user_timestamp ON /*_*/revision (rev_page,rev_user,rev_timestamp); +CREATE INDEX /*i*/actor_timestamp ON /*_*/revision (rev_actor,rev_timestamp); +CREATE INDEX /*i*/page_actor_timestamp ON /*_*/revision (rev_page,rev_actor,rev_timestamp); + +-- +-- Revisions are usually marked with a textual comment describing the change. +-- They are stored in the comment table to keep revision more compact, +-- and potentially to allow combining entries for identical comments. +-- +CREATE TABLE /*_*/comment( + -- Unique ID to identify each comment + comment_id bigint unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT, + + -- Text comment summarizing the change. + -- This text is shown in the history and other changes lists, + -- rendered in a subset of wiki markup by Linker::formatComment() + -- Size limits are enforced at the application level, and should + -- take care to crop UTF-8 strings appropriately. + comment_text MEDIUMBLOB NOT NULL +) /*$wgDBTableOptions*/; + +-- +-- Each revision references one or more content objects via the +-- slots table, ending up here. +-- +CREATE TABLE /*_*/content ( + cont_id bigint unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT, + + -- Key to text.old_id, where the actual bulk text is stored. + -- It's possible for multiple revisions to use the same text, + -- for instance revisions where only metadata is altered + -- or a rollback to a previous version. + cont_text_id bigint unsigned NOT NULL, + + -- Length of this content in bytes + cont_len int unsigned, + + -- SHA-1 text content hash in base-36 + cont_sha1 varbinary(32) NOT NULL DEFAULT '', + + -- content model, keys to content_model.cm_id for cm_model + -- See CONTENT_MODEL_XXX constants + cont_model smallint NOT NULL DEFAULT 0, + + -- content format, keys to content_format.cf_id for cf_format + -- See CONTENT_FORMT_XXX constants + cont_format smallint NOT NULL DEFAULT 0 +) /*$wgDBTableOptions*/; + + +-- +-- Slots are the association between a revision and one of its content objects. +-- Classic wiki pages may use only a single, default slot but more complex +-- data types may associate multiple content objects. +-- +CREATE TABLE /*_*/slots ( + -- Key to revision.rev_id + slot_revision BIGINT UNSIGNED NOT NULL, + + -- Key to content.cont_id + slot_content BIGINT UNSIGNED NOT NULL, + + -- Key to content_roles.cr_id + slot_role SMALLINT UNSIGNED NOT NULL, + + PRIMARY KEY (slot_revision, slot_role, slot_content) +) /*$wgDBTableOptions*/; + +CREATE UNIQUE INDEX /*i*/slot_revision_role ON /*_*/slots (slot_revision, slot_role); + + +-- +-- Mapping table for content slot roles. +-- +CREATE TABLE /*_*/content_roles ( + -- Unique ID for each role, used in slots.slot_role + cr_id SMALLINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + + -- Textual role name + cr_role VARBINARY(32) NOT NULL DEFAULT '' +) /*$wgDBTableOptions*/; + +CREATE UNIQUE INDEX /*i*/cr_role ON /*_*/content_roles (cr_role); + + +-- +-- Mapping table for content models. +-- A content model represents the conceptual data model of the underlying +-- page content, such as wikitext or javascript or a graph definition. +-- +CREATE TABLE /*_*/content_model ( + -- Unique ID for each content model, used in content.cont_model + cm_id smallint NOT NULL PRIMARY KEY AUTO_INCREMENT, + + -- Textual content model name + -- See CONTENT_MODEL_XXX constants + cm_model varbinary(32) NOT NULL DEFAULT '' +) /*$wgDBTableOptions*/; + +CREATE UNIQUE INDEX /*i*/cm_model ON /*_*/content_model (cm_model); + + +-- +-- Mapping table for content formats. +-- A content format represents the in-DB representation of the underlying +-- page content, such as wikitext or JSON. +-- +CREATE TABLE /*_*/content_format ( + -- Unique ID for each content format, used in content.cont_format + cf_id smallint NOT NULL PRIMARY KEY AUTO_INCREMENT, + + -- Textual content format name + -- See CONTENT_FORMT_XXX constants + cf_format varbinary(64) NOT NULL DEFAULT '' +) /*$wgDBTableOptions*/; + +CREATE UNIQUE INDEX /*i*/cf_format ON /*_*/content_format (cf_format); + -- -- Holds text of individual page revisions. -- To view, visit https://gerrit.wikimedia.org/r/350097 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I50ee6506ff71e7613e50c33d865199b07918c1de Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/core Gerrit-Branch: master Gerrit-Owner: Brion VIBBER <br...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits