WL#6940: Server version token and check
Affects: Server-5.7 — Status: Complete
Version Tokens can help a system of clients and servers to synchronize with each other. The current requirement for the feature comes from Fabric. But it shall be seen as a general synchronization feature, which can also be used independent from Fabric. In other words, the server implementation shall be done in a way, that it can be used directly by applications. The feature requires changes in the MySQL server, in Fabric, and in the connectors. This worklog specifies the changes in the MySQL server.
NF1: The server side support will be implemented as an audit plugin. FR2: The plugin will provide a read/write session level variable to hold the session level version token list that will allow setting/reading the list in full only FR3: The plugin will provide an imitation of global level variable to store the version tokens. FR4: The plugin will provide a UDF to set the above stated variable in full which will get replicated. UDF to partially update the variable, i.e. add/delete particular key-value pairs shall also be provided. A UDF is provided to show the list of version tokens present. FR5: Token names and values in global and the session token lists values will consist of valid characters of the system character set and will exclude equal '=' and semicolon ';', quotes and spaces. The tokens are case sensitive. Any extra spaces which are part of either name or value will be deleted and not considered as they are inserted in the hash. For example, in
key1 = value1 ;, key name will be key1 and value is value1 both without any spaces. FR6: When set or retrieved, the token lists will be represented as a semi-colon separated list of name=value pairs. FR7: At session creation time (post-authentication) the session's token list will be empty. FR8: At plugin load time the server's token list will be empty FR9: Before executing each query the plugin will compare the session token list with the global token list and will return an error if: - a session token name is not present in the global list - a session token value does not correspond to the value for the same token in the global list In case the session level variable itself is to be changed from some invalid value (i.e. some name/value which is not in accordance with the global hash) it cannot be done directly as the query itself fails because of invalid values. This scenario can be handled by using COM_RESET_CONNECTION which resets the session value to blank and hence no checks are performed when a new value is set. FR10: If FR9 is not met, the query execution will stop with an error NF11: Before doing the comparisons in FR9 the plugin will acquire locking service locks in share mode for tokens in session version vector. NF12: The plugin will release the locking service locks it has at the end of query execution. FR13: A user needs super privileges to make any modifications to global version vector and for even viewing them.
-- The plugin Structure -- A shared library shall be introduced which implements: 1. An audit plugin tracking query execution and connect/disconnect events. This also holds a list of version tokens to be considered which are accessible through UDFs. 2. UDFs to set, edit, delete and show version_tokens_list. -- The global token list -- The plugin will internally hold a global hash(version_tokens_hash) with token name/values as its members. At plugin load time the global hash is always empty. The hash contains the version token pairs in structs. -- The version_tokens_list variable -- This is not a pure plugin variable, but a kind of imitation. It can be changed completely using version_tokens_set() UDF. Partial modifications are done through version_tokens_edit() UDF. Individual tokens can be deleted using version_tokens_delete() UDF. The variable can be displayed using version_tokens_show() UDF. All the above UDFs need super privileges. If any token is repeated in the arguments for setting/editing the variable in these UDFs, the one which comes last is considered. For example, when version_tokens_set("token1=val1; token2=val2; token1=val3") is executed, val3 is considered for token1. This variable when set or edited will: 1. Take an exclusive lock on the global hash. 2. Parse the semicolon separated list of global tokens and store these into version_tokens_hash. 3. Release the exclusive lock on the hash. When displayed through show(), all the tokens present in the hash are displayed, not necessarily in a sorted order. -- Session level tokens -- This session attributes are stored as system session variable called session_version_tokens with SESSION_ONLY flag set. This variable can be easily read using the thd object in plugin. It is stored in the same format as the string used for the global version tokens list i.e. semicolon separated list of tokens. -- tokens_version_number -- To avoid any unnecessary comparisons, a version number shall be maintained in the plugin to track the changes to global plugin variable. On updating the variable, this version number shall be incremented by 1. When the plugin is loaded, this variable starts with the value 1. This integer is only for the internal implementation and is invisible to the user. A session level variable called tokens_version_number (default 0), with flags SESSION_ONLY and INVISIBLE is also introduced to hold this value. Before comparing the session level tokens with global tokens, the value stored in tokens_version_number variable is compared against version number stored in the plugin. If the numbers match, the comparison step can be safely skipped. Else, the comparison is done again, and the variable is updated with the latest value if the comparison is successful. When the session_version_tokens variable is updated, tokens_version_number is set back to 0. Since the global version value is never 0, comparision will take place for next query execution in that session. -- The version_tokens_set() UDF -- There will be a new UDF exposed by the plugin that will have the following syntax: version_tokens_set(version_tokens_list varchar()); This when called, will parse the semicolon separated key value pairs and store them in version_tokens_hash. -- The version_tokens_edit() UDF -- Syntax: version_tokens_edit(version_tokens_list varchar()); This when called, will parse the semicolon separated key value pairs and search for them in version_tokens_hash. If the entries are found, their values are edited. Else the new pair is stored in the hash. -- The version_tokens_delete() UDF -- Syntax: version_tokens_delete(version_tokens_names_list varchar()); This when called, will parse the semicolon separated version token names and search for them in version_tokens_hash. If found, they are deleted from the hash. If '*' is passed as an argument, all the tokens are deleted. -- The version_tokens_show() UDF -- Syntax: version_tokens_show(); This when called, will fetch all the key value pairs from version_tokens_hash, form a semicolon separated list of them and returns it. -- At query execution start event -- The plugin will: 1. If there are no session vectors, do nothing. 2. Grab locking service shared locks for all elements of session version vectors 3. If the version number matches with token_version_number, move to query execution. Else, compare them with the existing ones in global hash. 4. If they differ, unlock the version vectors and return the relevant error (if the token name doesn't exist or the value is different). Else, update token_version_number. 5. Keep the locks for the duration of the query -- At query execution end event -- If there are no session vectors, do nothing. Else, the plugin will release all the locking service locks taken. -- At plugin unload event -- The plugin will: 1. Take exclusive locks on all the global version vector names 2. Clear the hash. 3. Release locks. -- Admin locking of tokens -- If the admin wants to lock certain tokens in share/exclusive mode, it can be done directly through the following helper UDFs talking to the locking service APIs (see WL#8161 for further details) : --- version_tokens_lock_shared --- version_tokens_lock_shared(lock_name1 VARCHAR[, ...], lock_timeout_seconds INT) RETURNS INT returns 0 on failure, 1 on success. Takes (atomically) shared locking service locks on lock_name1, lock_name2, ... The last argument is the lock wait timeout in seconds. --- version_tokens_lock_exclusive --- version_tokens_lock_exclusive(lock_name1 VARCHAR[, ...], lock_timeout_seconds INT) RETURNS INT returns 0 on failure, 1 on success. Takes (atomically) exclusive locking service locks on lock_name1, lock_name2, ... The last argument is the lock wait timeout in seconds. --- version_tokens_unlock --- version_tokens_unlock() RETURNS INT returns 0 on failure, 1 on success. Releases all locking service locks taken by the thread.
Copyright (c) 2000, 2020, Oracle Corporation and/or its affiliates. All rights reserved.