Requests for comment/Configuration database

Request for comment (RFC)
Configuration database
Component General
Creation date
Author(s) Brion Vibber, Chad Horohoe
Document status declined
Close, abandoned by authors -- Tim Starling (talk) 21:11, 12 March 2014 (UTC)[reply]

Problems to solve

edit
  • .php config files are fragile
    • easy to break by mistake
    • no edit history unless you explicit do external version control
  • Editing config files is difficult/inconvenient for many sites
    • requires shell access
    • can't easily expose limited settings to admins on the wiki, making serious bottlenecks for site config issues on a big wiki farm
  • Handling config for multi-wiki farms right now is very difficult
    • SiteConfig/InitaliseSettings stuff is weirdly Wikimedia-specific and just plain ugly
    • querying settings from a sister site (even simply translating dbname <-> URL and getting descriptive names) is very difficult, unreliable (various settings may be in CommonSettings and not show up in the config arrays)
    • extensions are difficult to configure since the array is read in before the extensions and their defaults are loaded
  • Many, or most, config options can be readable for the public or wiki users, since they're no secrets. Yet wiki owners may wish not to publicise some specific settings even if that was not a security risk. At least in large wikis, holders of different rights may be wanted who can alter different config settings but not all (e.g. a bureaucrat may enable or disable extensions but neither see not alter database passwords)
    • Have a special page listing all config settings that are viewable be the current user as per their group rights.
    • Allow wiki admins to assign viewing and altering rights to each config setting, groupwise or individually.

Counter-arguments

edit
  • Users can help with site config issues by making recommendations; they can use Extension:ViewFiles to view the config files and say exactly what they want changed. Then all that is needed is for a site admin to make the change.
  • There will always have to be a configuration file at least for database credentials (e.g. $wgDBname) that MediaWiki needs to access the table with the configuration settings.
  • The config files are suitable for developers who want to tinker without having to go through the steps of changing the database. It's quicker to comment out configuration settings than to go to a configuration screen to change them. This is especially true if the names, structure (e.g. array vs. string), etc. of the configuration settings are rapidly changing as the code evolves.
  • If the configuration gets messed up to the point that one can't even get to the configuration screen (e.g. suppose access to the configuration screen is inadvertently restricted to a group of users with no members), it's easier to edit a config file than to find an alternative way to change the database and thereby restore the system administrator's access to the configuration screen.

Requirements

edit
  • Should work cleanly for one-off installs
  • Should handle Wikimedia-style farms:
    • global configuration
    • configurations shared among project groups
    • configurations specific to a wiki
    • need to be able to fetch config info from other wikis cleanly
    • Varying user permissions; not all settings should be editable by everyone
  • Metadata...
    • For each config (core or ext) provide:
      • type
      • validation rules
      • friendly name
      • more detailed description, examples
      • Default
  • Needs high-level read/write support, so we can abstract backends more easily (CDB,SQL,etc)
  • Possibly use HtmlForm or similar framework for defining UI paging? Similar to prefs rewrite?
    • We can adapt some of the form field types in Configure (that's where I got the idea for HTMLForm) for the new configuration (there are some specialist types like group permissions arrays).
  • Allow XML, JSON, or YAML config files?
    • Model off of Zend_Config, maybe.

Data types

edit
  • boolean
  • number
  • text
    • email
    • URL
    • local path
    • message key
    • ?
  • wiki reference
    • wiki-id needs decoupling from $wgDBname
    • Be able to get a db connection (just like wfGetDB()) for a remote wiki. Or an API url, if we don't have DB access)
  • array of any date type in this list other than 'array'
  • ?

Internal interface

edit

Common case: local lookups:

  • Conf::get( 'serverUrl' ) (shortcut)
  • Conf::singleton()->reallyGet( 'serverUrl' )

Other-local-site info lookups:

  • Conf::get( 'serverUrl', 'dewiki' );

abstract class Conf {
	// All of the configuration variables we've loaded thus far
	private $data;
	
	// The Wiki ID (usually $wgDBname)
	private $wikiId;
	
	// Singleton
	private static $__instance;
	
	protected function __construct( $conf ) {
		$this->config = $conf['id'];
	}
	
	/**
	 * Initialize a new child class based on a configuration array
	 * @param $conf Array of configuration settings, see $wgConfiguration
	 *   for details
	 * @return Conf child
	 */
	private static function newFromSettings( $conf ) {
		$class = ucfirst( $conf['type'] ) . 'Conf';
		if( !class_exists( $class ) ) {
			throw new MWException( '$wgConf misconfigured with invalid "type"' );
		}
		return new $class( $conf );
	}
	
	/**
	 * Get the singleton if we don't want a specific wiki
	 * @param $wiki String An id for a remote wiki
	 * @return Conf child
	 */
	public static function load( $wiki = false ) {
		if( !self::$__instance ) {
			global $wgConfiguration;
			self::$__instance = self::newFromSettings( $wgConfiguration );
		} if( $wiki && $wiki != self::$__instance->getWikiId() ) {
			// Load configuration for a different wiki, not sure how
			// we're gonna do this yet
			return null;
		}
		return self::$__instance;
	}
	
	/**
	 * Get a property from the configuration database, falling back
	 * to DefaultSettings if undefined
	 * @param $key String Any possible configuration string
	 * @param $wiki String A wiki ID of a remote configuration [no-op]
	 * @return Mixed
	 */
	public static function get( $key, $wiki = false ) {
		return self::load( $wiki )->reallyGet( $key );
	}
	
	/**
	 * Set a property to the configuration database
	 * @param $key String Any possible configuration string
	 * @param $value Mixed Anything
	 * @param $write boolean If true, write to database
	 * @param $wiki String A wiki ID of a remote configuration [no-op]
	 * @return Boolean true on successful write
	 */
	public static function set( $key, $value, $write = false, $wiki = false ) {
		return self::load( $wiki )->reallySet( $key, $value, $write );
	}
	
	/**
	 * What is the wiki ID for this site?
	 * @return String
	 */
	public function getWikiId() {
		return $this->wikiId;
	}
	
	/**
	 * Actually retrieve the data. Check local var cache before
	 * trying the database and then default settings
	 * @param $key String a configuration key
	 * @return Mixed
	 */
	protected function reallyGet( $key ) {
		if( !isset( $this->data[$key] ) ) {
			$val = $this->retrieveSetting( $key );
			if( $val !== null ) {
				$val = DefaultSettings::$ds[$key];
			}
			$this->data[$key] = $val;
		}
		return $this->data[$key];
	}
	
	/**
	 * Set the var to the local cache, then potentially to the database itself
	 * @param $key a configuration key
	 * @param $value Mixed
	 * @param $write Boolean true to write to the database, false for request-only
	 * @return unknown_type
	 */
	protected function reallySet( $key, $value, $write ) {
		if( count( $this->data ) > 100 ) {
			$this->data = array();
		}
		$this->data[$key] = $value;
		if( $write ) {
			return $this->applySetting( $key, $value );
		}
		return true;
	}
	
	/**
	 * This is the implementation-specific part of the get() process.
	 * Child classes will need to implement this (eg: SQL queries, DBA
	 * calls, etc)
	 * @param $key String a configuration key
	 * @return Mixed
	 */
	abstract protected function retrieveSetting( $key );
	
	/**
	 * This is the implementation-specific part of the set() process.
	 * Child classes will need to implement this (eg: SQL queries, DBA
	 * calls, etc)
	 * @param $key String a configuration key
	 * @param $value Mixed
	 * @return Boolean success on write
	 */
	abstract protected function applySetting( $key, $value );
}

User interface

edit

There's a lot that can be adapted or copied from Configure.

Bugs that could be solved / New features

edit
  • bug 12518 - Cross-wiki userrights reflects groups on local wiki, not target wiki
  • Maybe add a iw_wiki col in the interwiki table that references the wiki ID:
    • bug 11 - Red interwiki links -- check for page existence across wikis
    • bug 9890 - Reasonably efficient interwiki transclusion
      • (these sorts of things benefit from being able to look up things like remote namespaces cleanly, so we can pull data from a db directly instead of doing some nasty api hack)
    • Drop $wgLocalInterwiki and check if iw_wiki == wfWikiId().
  • Maybe run internal HTTP requests (transwiki imports, fetching remote file description page, interwiki transclusion) internally?
  • Drop $wgLocalDatabases and replace this with a query in the database.
  • bug 7750 - Extension for storing non-default $wgGroupPermission's in MySQL table

See also

edit