From 151eb23bfee6a87087c4217dd3bec649b2f634ed Mon Sep 17 00:00:00 2001 From: andreyaksenov Date: Thu, 23 May 2024 11:39:53 +0300 Subject: [PATCH 01/12] Applications: update vshard/crud version --- .../sharded_cluster/sharded_cluster-scm-1.rockspec | 2 +- .../sharded_cluster_crud/sharded_cluster_crud-scm-1.rockspec | 4 ++-- .../templates/basic/{{.name}}-scm-1.rockspec.tt.template | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster/sharded_cluster-scm-1.rockspec b/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster/sharded_cluster-scm-1.rockspec index d88dc912ee..5be1567c6b 100644 --- a/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster/sharded_cluster-scm-1.rockspec +++ b/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster/sharded_cluster-scm-1.rockspec @@ -5,7 +5,7 @@ source = { } dependencies = { - 'vshard == 0.1.26' + 'vshard == 0.1.27' } build = { type = 'none'; diff --git a/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/sharded_cluster_crud-scm-1.rockspec b/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/sharded_cluster_crud-scm-1.rockspec index ca26ede2b6..fb61077796 100644 --- a/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/sharded_cluster_crud-scm-1.rockspec +++ b/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/sharded_cluster_crud-scm-1.rockspec @@ -5,8 +5,8 @@ source = { } dependencies = { - 'vshard == 0.1.26', - 'crud == 1.4.3' + 'vshard == 0.1.27', + 'crud == 1.5.2' } build = { type = 'none'; diff --git a/doc/code_snippets/snippets/sharding/templates/basic/{{.name}}-scm-1.rockspec.tt.template b/doc/code_snippets/snippets/sharding/templates/basic/{{.name}}-scm-1.rockspec.tt.template index 2806c482ce..b34bf8e015 100644 --- a/doc/code_snippets/snippets/sharding/templates/basic/{{.name}}-scm-1.rockspec.tt.template +++ b/doc/code_snippets/snippets/sharding/templates/basic/{{.name}}-scm-1.rockspec.tt.template @@ -5,7 +5,7 @@ source = { } dependencies = { - 'vshard == 0.1.25' + 'vshard == 0.1.27' } build = { type = 'none'; From 469ec7b77fb3114b83619adc1962b92f93f9df5d Mon Sep 17 00:00:00 2001 From: andreyaksenov Date: Thu, 23 May 2024 12:50:22 +0300 Subject: [PATCH 02/12] Applications: use new roles in the 'crud' sample --- .../sharded_cluster_crud/config.yaml | 16 ++++++- .../sharded_cluster_crud/router.lua | 16 ------- .../sharded_cluster_crud/storage.lua | 42 +++++++------------ 3 files changed, 29 insertions(+), 45 deletions(-) diff --git a/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/config.yaml b/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/config.yaml index 77a5a5d8b0..b414e60201 100644 --- a/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/config.yaml +++ b/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/config.yaml @@ -19,7 +19,9 @@ sharding: groups: storages: - roles: [storage] + roles: [roles.crud-storage] + app: + module: storage sharding: roles: [storage] replication: @@ -48,7 +50,17 @@ groups: listen: - uri: '127.0.0.1:3305' routers: - roles: [router] + roles: [roles.crud-router] + roles_cfg: + roles.crud-router: + stats: true + stats_driver: metrics + stats_quantiles: false + stats_quantile_tolerated_error: 0.001 + stats_quantile_age_buckets_count: 5 + stats_quantile_max_age_time: 180 + app: + module: router sharding: roles: [router] replicasets: diff --git a/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/router.lua b/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/router.lua index c09e13003c..61ccb2c40b 100644 --- a/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/router.lua +++ b/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/router.lua @@ -1,17 +1 @@ local vshard = require('vshard') -local crud = require('crud') - -local M = {} - -function M.validate() -end - -function M.apply() - crud.init_router() -end - -function M.stop() - crud.stop_router() -end - -return M diff --git a/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/storage.lua b/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/storage.lua index 4ddfdf2e65..f692015c03 100644 --- a/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/storage.lua +++ b/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/storage.lua @@ -1,29 +1,17 @@ -local crud = require('crud') - -local M = {} - -function M.validate() -end - -function M.apply() - crud.init_storage() - if box.info.ro ~= true then - box.schema.create_space('bands', { - format = { - { name = 'id', type = 'unsigned' }, - { name = 'bucket_id', type = 'unsigned' }, - { name = 'band_name', type = 'string' }, - { name = 'year', type = 'unsigned' } - }, - if_not_exists = true - }) - box.space.bands:create_index('id', { parts = { 'id' }, if_not_exists = true }) - box.space.bands:create_index('bucket_id', { parts = { 'bucket_id' }, unique = false, if_not_exists = true }) +box.watch('box.status', function() + if box.info.ro then + return end -end - -function M.stop() - crud.stop_storage() -end -return M + box.schema.create_space('bands', { + format = { + { name = 'id', type = 'unsigned' }, + { name = 'bucket_id', type = 'unsigned' }, + { name = 'band_name', type = 'string' }, + { name = 'year', type = 'unsigned' } + }, + if_not_exists = true + }) + box.space.bands:create_index('id', { parts = { 'id' }, if_not_exists = true }) + box.space.bands:create_index('bucket_id', { parts = { 'bucket_id' }, unique = false, if_not_exists = true }) +end) From 08047ded6059a5957c30cbb7e17f721b2316d96e Mon Sep 17 00:00:00 2001 From: andreyaksenov Date: Thu, 23 May 2024 11:27:50 +0300 Subject: [PATCH 03/12] Applications: custom role samples --- .../application_role/config.yaml | 7 ++ .../application_role/greeter.lua | 6 ++ .../application_role/instances.yml | 1 + .../application_role_cfg/byeer.lua | 9 +++ .../application_role_cfg/config.yaml | 10 +++ .../application_role_cfg/greeter.lua | 23 +++++++ .../application_role_cfg/instances.yml | 1 + .../application_role_http_api/config.yaml | 11 ++++ .../application_role_http_api/data.lua | 32 +++++++++ .../application_role_http_api/http-api.lua | 65 +++++++++++++++++++ .../application_role_http_api/instances.yml | 1 + 11 files changed, 166 insertions(+) create mode 100644 doc/code_snippets/snippets/config/instances.enabled/application_role/config.yaml create mode 100644 doc/code_snippets/snippets/config/instances.enabled/application_role/greeter.lua create mode 100644 doc/code_snippets/snippets/config/instances.enabled/application_role/instances.yml create mode 100644 doc/code_snippets/snippets/config/instances.enabled/application_role_cfg/byeer.lua create mode 100644 doc/code_snippets/snippets/config/instances.enabled/application_role_cfg/config.yaml create mode 100644 doc/code_snippets/snippets/config/instances.enabled/application_role_cfg/greeter.lua create mode 100644 doc/code_snippets/snippets/config/instances.enabled/application_role_cfg/instances.yml create mode 100644 doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/config.yaml create mode 100644 doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/data.lua create mode 100644 doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/http-api.lua create mode 100644 doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/instances.yml diff --git a/doc/code_snippets/snippets/config/instances.enabled/application_role/config.yaml b/doc/code_snippets/snippets/config/instances.enabled/application_role/config.yaml new file mode 100644 index 0000000000..2258022f07 --- /dev/null +++ b/doc/code_snippets/snippets/config/instances.enabled/application_role/config.yaml @@ -0,0 +1,7 @@ +groups: + group001: + replicasets: + replicaset001: + instances: + instance001: + roles: [ greeter ] diff --git a/doc/code_snippets/snippets/config/instances.enabled/application_role/greeter.lua b/doc/code_snippets/snippets/config/instances.enabled/application_role/greeter.lua new file mode 100644 index 0000000000..e49da0d052 --- /dev/null +++ b/doc/code_snippets/snippets/config/instances.enabled/application_role/greeter.lua @@ -0,0 +1,6 @@ +-- greeter.lua -- +return { + validate = function() end, + apply = function() require('log').info("Hi from the 'greeter' role!") end, + stop = function() end, +} diff --git a/doc/code_snippets/snippets/config/instances.enabled/application_role/instances.yml b/doc/code_snippets/snippets/config/instances.enabled/application_role/instances.yml new file mode 100644 index 0000000000..aa60c2fc42 --- /dev/null +++ b/doc/code_snippets/snippets/config/instances.enabled/application_role/instances.yml @@ -0,0 +1 @@ +instance001: diff --git a/doc/code_snippets/snippets/config/instances.enabled/application_role_cfg/byeer.lua b/doc/code_snippets/snippets/config/instances.enabled/application_role_cfg/byeer.lua new file mode 100644 index 0000000000..f7cf4d2bad --- /dev/null +++ b/doc/code_snippets/snippets/config/instances.enabled/application_role_cfg/byeer.lua @@ -0,0 +1,9 @@ +-- byeer.lua -- +local log = require('log').new("byeer") + +return { + dependencies = { 'greeter' }, + validate = function() end, + apply = function() log.info("Bye from the 'byeer' role!") end, + stop = function() end, +} diff --git a/doc/code_snippets/snippets/config/instances.enabled/application_role_cfg/config.yaml b/doc/code_snippets/snippets/config/instances.enabled/application_role_cfg/config.yaml new file mode 100644 index 0000000000..190cf800a5 --- /dev/null +++ b/doc/code_snippets/snippets/config/instances.enabled/application_role_cfg/config.yaml @@ -0,0 +1,10 @@ +groups: + group001: + replicasets: + replicaset001: + instances: + instance001: + roles: [ greeter ] + roles_cfg: + greeter: + greeting: 'Hi' diff --git a/doc/code_snippets/snippets/config/instances.enabled/application_role_cfg/greeter.lua b/doc/code_snippets/snippets/config/instances.enabled/application_role_cfg/greeter.lua new file mode 100644 index 0000000000..cf56cb2027 --- /dev/null +++ b/doc/code_snippets/snippets/config/instances.enabled/application_role_cfg/greeter.lua @@ -0,0 +1,23 @@ +-- greeter.lua -- +local log = require('log').new("greeter") + +local function validate_config(cfg) + if cfg.greeting then + assert(type(cfg.greeting) == "string", "'greeting' should be a string") + assert(cfg.greeting == "Hi" or cfg.greeting == "Hello", "'greeting' should be 'Hi' or 'Hello'") + end +end + +local function apply_config(cfg) + log.info("%s from the 'greeter' role!", cfg.greeting) +end + +local function stop_role() + log.info("The 'greeter' role is stopped") +end + +return { + validate = validate_config, + apply = apply_config, + stop = stop_role, +} diff --git a/doc/code_snippets/snippets/config/instances.enabled/application_role_cfg/instances.yml b/doc/code_snippets/snippets/config/instances.enabled/application_role_cfg/instances.yml new file mode 100644 index 0000000000..aa60c2fc42 --- /dev/null +++ b/doc/code_snippets/snippets/config/instances.enabled/application_role_cfg/instances.yml @@ -0,0 +1 @@ +instance001: diff --git a/doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/config.yaml b/doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/config.yaml new file mode 100644 index 0000000000..76e7b9fe74 --- /dev/null +++ b/doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/config.yaml @@ -0,0 +1,11 @@ +groups: + group001: + replicasets: + replicaset001: + instances: + instance001: + roles: [ http-api ] + roles_cfg: + http-api: + host: '127.0.0.1' + port: 8080 diff --git a/doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/data.lua b/doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/data.lua new file mode 100644 index 0000000000..775d00d668 --- /dev/null +++ b/doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/data.lua @@ -0,0 +1,32 @@ +local data = {} + +data.add_sample_data = function() + box.watch('box.status', function() + if box.info.ro then + return + end + + box.schema.space.create('bands', { if_not_exists = true }) + box.space.bands:format({ + { name = 'id', type = 'unsigned' }, + { name = 'band_name', type = 'string' }, + { name = 'year', type = 'unsigned' } + }) + box.space.bands:create_index('primary', { parts = { 'id' } }) + box.space.bands:create_index('band', { parts = { 'band_name' } }) + box.space.bands:create_index('year_band', { parts = { { 'year' }, { 'band_name' } } }) + + box.space.bands:insert { 1, 'Roxette', 1986 } + box.space.bands:insert { 2, 'Scorpions', 1965 } + box.space.bands:insert { 3, 'Ace of Base', 1987 } + box.space.bands:insert { 4, 'The Beatles', 1960 } + box.space.bands:insert { 5, 'Pink Floyd', 1965 } + box.space.bands:insert { 6, 'The Rolling Stones', 1962 } + box.space.bands:insert { 7, 'The Doors', 1965 } + box.space.bands:insert { 8, 'Nirvana', 1987 } + box.space.bands:insert { 9, 'Led Zeppelin', 1968 } + box.space.bands:insert { 10, 'Queen', 1970 } + end) +end + +return data diff --git a/doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/http-api.lua b/doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/http-api.lua new file mode 100644 index 0000000000..dd1961a4ee --- /dev/null +++ b/doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/http-api.lua @@ -0,0 +1,65 @@ +-- http-api.lua -- +local httpd +local json = require('json') + +local function validate_config(cfg) + if cfg.host then + assert(type(cfg.host) == "string", "'host' should be a string containing a valid IP address") + end + if cfg.port then + assert(type(cfg.port) == "number", "'port' should be a number") + assert(cfg.port >= 1 and cfg.port <= 65536, "'port' should be between 1 and 65536") + end +end + +local function apply_config(cfg) + if httpd then + httpd:stop() + end + httpd = require('http.server').new(cfg.host, cfg.port) + local response_headers = { ['content-type'] = 'application/json' } + httpd:route({ path = '/band/:id', method = 'GET' }, function(req) + local id = req:stash('id') + local band_tuple = box.space.bands:get(tonumber(id)) + if not band_tuple then + return { status = 404, body = 'Band not found' } + else + local band = { id = band_tuple['id'], + band_name = band_tuple['band_name'], + year = band_tuple['year'] } + return { status = 200, headers = response_headers, body = json.encode(band) } + end + end) + httpd:route({ path = '/band', method = 'GET' }, function(req) + local limit = req:query_param('limit') + if not limit then + limit = 5 + end + local band_tuples = box.space.bands:select({}, { limit = tonumber(limit) }) + local bands = {} + for _, tuple in pairs(band_tuples) do + local band = { id = tuple['id'], + band_name = tuple['band_name'], + year = tuple['year'] } + table.insert(bands, band) + end + return { status = 200, headers = response_headers, body = json.encode(bands) } + end) + httpd:start() +end + +local function stop_role() + httpd:stop() +end + +local function init() + require('data'):add_sample_data() +end + +init() + +return { + validate = validate_config, + apply = apply_config, + stop = stop_role, +} diff --git a/doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/instances.yml b/doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/instances.yml new file mode 100644 index 0000000000..aa60c2fc42 --- /dev/null +++ b/doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/instances.yml @@ -0,0 +1 @@ +instance001: From d77da3be39e6b8a71c17e0256f8d45cb75eaf04c Mon Sep 17 00:00:00 2001 From: andreyaksenov Date: Thu, 30 May 2024 11:58:16 +0300 Subject: [PATCH 04/12] Config: precedence note --- doc/concepts/configuration.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/concepts/configuration.rst b/doc/concepts/configuration.rst index 900a480562..5e1fcd171d 100644 --- a/doc/concepts/configuration.rst +++ b/doc/concepts/configuration.rst @@ -129,6 +129,9 @@ Most of the configuration options can be applied to a specific instance, replica :emphasize-lines: 1-3 :dedent: +Configuration scopes above are listed in the order of their precedence -- from highest to lowest. +For example, if the same option is defined at the instance and global level, the instance's value takes precedence over the global one. + .. NOTE:: From 6cef2a0c00229b5671b6e9ed1065c6cbe25212b3 Mon Sep 17 00:00:00 2001 From: andreyaksenov Date: Wed, 29 May 2024 10:47:40 +0300 Subject: [PATCH 05/12] Roles: configuration reference --- .../configuration/configuration_reference.rst | 111 +++++++++++++++++- 1 file changed, 108 insertions(+), 3 deletions(-) diff --git a/doc/reference/configuration/configuration_reference.rst b/doc/reference/configuration/configuration_reference.rst index bb76dd970a..23769f69c7 100644 --- a/doc/reference/configuration/configuration_reference.rst +++ b/doc/reference/configuration/configuration_reference.rst @@ -11,6 +11,100 @@ This topic describes all :ref:`configuration parameters ` provide Most of the configuration options described in this reference can be applied to a specific instance, replica set, group, or to all instances globally. To do so, you need to define the required option at the :ref:`specified level `. + + +.. _configuration_reference_app: + +app +--- + +Using Tarantool as an application server, you can run your own Lua applications. +In the ``app`` section, you can load the application and provide an application configuration in the ``app.cfg`` section. + +.. NOTE:: + + ``app`` can be defined in any :ref:`scope `. + +- :ref:`app.cfg ` +- :ref:`app.file ` +- :ref:`app.module ` + + +.. _configuration_reference_app_cfg: + +.. confval:: app.cfg + + **Since:** :doc:`3.0.0 `. + + A configuration of the application loaded using ``app.file`` or ``app.module``. + + **Example** + + In the example below, the application is loaded from the ``myapp.lua`` file placed next to the YAML configuration file: + + .. literalinclude:: /code_snippets/snippets/config/instances.enabled/application/config.yaml + :language: yaml + :end-at: greeting + :dedent: + + Example on GitHub: `application `_ + + | + | Type: map + | Default: nil + | Environment variable: TT_APP_CFG + + +.. _configuration_reference_app_file: + +.. confval:: app.file + + **Since:** :doc:`3.0.0 `. + + A path to a Lua file to load an application from. + + | + | Type: string + | Default: nil + | Environment variable: TT_APP_FILE + + +.. _configuration_reference_app_module: + +.. confval:: app.module + + **Since:** :doc:`3.0.0 `. + + A Lua module to load an application from. + + **Example** + + The ``app`` section can be placed in any :ref:`configuration scope `. + As an example use case, you can provide different applications for storages and routers in a sharded cluster: + + .. code-block:: yaml + + groups: + storages: + app: + module: storage + # ... + routers: + app: + module: router + # ... + + | + | Type: string + | Default: nil + | Environment variable: TT_APP_MODULE + + + + + + + .. _configuration_reference_audit: audit_log @@ -2586,8 +2680,8 @@ The ``process`` section defines configuration parameters of the Tarantool proces If not specified, defaults to the current working directory. Other directory and file parameters, if set as relative paths, - are interpreted as relative to ``process.work_dir``. For example, - :ref:`directories for storing snapshots and write-ahead logs ` + are interpreted as relative to ``process.work_dir``, for example, directories for storing + :ref:`snapshots and write-ahead logs `. | | Type: string @@ -2974,7 +3068,7 @@ The ``replication`` section defines configuration parameters related to :ref:`re roles ----- -This section describes configuration parameters related to roles. +This section describes configuration parameters related to :ref:`application roles `. .. NOTE:: @@ -2990,6 +3084,11 @@ This section describes configuration parameters related to roles. **Since:** :doc:`3.0.0 `. + Specify the roles of an instance. + To specify a role's configuration, use the :ref:`roles_cfg ` option. + + See also: :ref:`configuration_application_roles` + | | Type: array | Default: nil @@ -3002,6 +3101,12 @@ This section describes configuration parameters related to roles. **Since:** :doc:`3.0.0 `. + Specify a role's configuration. + This option accepts a role name as the key and a role's configuration as the value. + To specify the roles of an instance, use the :ref:`roles ` option. + + See also: :ref:`configuration_application_roles` + | | Type: map | Default: nil From 1c098eeba2aa4d97ccfb05dab0f69127885b5a60 Mon Sep 17 00:00:00 2001 From: andreyaksenov Date: Wed, 29 May 2024 10:44:28 +0300 Subject: [PATCH 06/12] Roles: docs --- doc/book/admin/index.rst | 1 - doc/book/cluster_app.rst | 14 + doc/book/cluster_app/app_roles.rst | 464 ++++++++++++++++++ .../instance_config.rst | 6 +- doc/book/index.rst | 1 + doc/index.rst | 1 + doc/toctree.rst | 1 + 7 files changed, 484 insertions(+), 4 deletions(-) create mode 100644 doc/book/cluster_app.rst create mode 100644 doc/book/cluster_app/app_roles.rst rename doc/book/{admin => cluster_app}/instance_config.rst (98%) diff --git a/doc/book/admin/index.rst b/doc/book/admin/index.rst index 0396a88f28..1fd4f1615d 100644 --- a/doc/book/admin/index.rst +++ b/doc/book/admin/index.rst @@ -25,7 +25,6 @@ This chapter includes the following sections: :maxdepth: 2 :numbered: 0 - instance_config start_stop_instance modules logs diff --git a/doc/book/cluster_app.rst b/doc/book/cluster_app.rst new file mode 100644 index 0000000000..51fb25fd7e --- /dev/null +++ b/doc/book/cluster_app.rst @@ -0,0 +1,14 @@ +.. _cluster_app: + +Cluster applications +==================== + +.. NOTE:: + + This section is under development. + +.. toctree:: + :maxdepth: 1 + + cluster_app/instance_config + cluster_app/app_roles diff --git a/doc/book/cluster_app/app_roles.rst b/doc/book/cluster_app/app_roles.rst new file mode 100644 index 0000000000..46505617b9 --- /dev/null +++ b/doc/book/cluster_app/app_roles.rst @@ -0,0 +1,464 @@ +.. _application_roles: + +Application roles +================= + +An application role is a Lua module that implements specific functions or logic. +You can enable or turn off a specific role for certain instances in a :ref:`configuration ` without restarting these instances. +A role is run when a configuration is loaded or reloaded. + +Roles can be divided into the following groups: + +- Roles provided by Tarantool. + For example, the ``config.storage`` role can be used to make a Tarantool replica set act as a :ref:`configuration storage `. +- Roles provided by third-party Lua modules. + For example, the `CRUD `__ module provides the ``roles.crud-storage`` and ``roles.crud-router`` roles that enable CRUD operations in a sharded cluster. +- Custom roles that are developed as a part of a cluster application. + For example, you create a custom role to define a stored procedure or implement a supplementary service, such as an email notifier or a replicator. + +This section describes how to develop custom roles. +To learn how to enable and configure roles, see :ref:`configuration_application_roles`. + +.. NOTE:: + + Don't confuse application roles with other role types: + + - A role is a container for privileges that can be granted to users. Learn more in :ref:`access_control_concepts_roles`. + - A role of a replica set in regard to sharding. Learn more in :ref:`vshard_config_sharding_roles`. + + + + +.. _roles_create_custom_role_config: + +Providing a role configuration +------------------------------ + +A custom role can be configured in the same way as roles provided by Tarantool or third-party Lua modules. +You can learn more from :ref:`configuration_application_roles`. + +This example shows how to enable and configure the ``greeter`` role, which is implemented in the next section: + +.. literalinclude:: /code_snippets/snippets/config/instances.enabled/application_role_cfg/config.yaml + :language: yaml + :start-at: instance001 + :dedent: + +The role's configuration provided in ``roles_cfg`` can be accessed when :ref:`validating ` and :ref:`applying ` this configuration. + +Given that a role is a Lua module, a role's name is passed to ``require()`` to obtain the module. +This means that to implement the ``greeter`` role, the ``greeter.lua`` file should be created next to a cluster's configuration file. + +.. NOTE:: + + You can also provide a role's configuration in any place inside a cluster's configuration file. + In this case, you can get configuration values using :ref:`config:get() `. + + + +.. _roles_create_custom_role: + +Creating a custom role +---------------------- + +.. _roles_create_custom_role_overview: + +Overview +~~~~~~~~ + +The main steps of creating a custom role might include the following steps: + +1. Define a function that validates a role's configuration. +2. Define a function that applies a validated configuration. +3. Define a function that stops a role. +4. (Optional) Define roles from which a custom role depends on. + +As a result, a role's module should return an object that has corresponding functions and fields specified: + +.. code-block:: lua + + return { + validate = function() -- ... -- end, + apply = function() -- ... -- end, + stop = function() -- ... -- end, + dependencies = { -- ... -- }, + } + +The examples below show how to do this. + +.. NOTE:: + + Code snippets shown in this section are included from the following application: `application_role_cfg `_. + + + + +.. _roles_create_custom_role_validate: + +Validating a role configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To validate a role's configuration, you need to define the :ref:`validate([cfg]) ` function. +The ``cfg`` argument allows you to get access to the provided :ref:`role's configuration ` and check its validity. + +In the example below, the ``validate_config()`` function is used to validate the ``greeting`` configuration value: + +.. literalinclude:: /code_snippets/snippets/config/instances.enabled/application_role_cfg/greeter.lua + :language: lua + :start-at: function validate_config + :end-before: function apply_config + :dedent: + +``validate_config`` should be assigned to ``validate``: + +.. literalinclude:: /code_snippets/snippets/config/instances.enabled/application_role_cfg/greeter.lua + :language: lua + :start-at: return + :dedent: + +If the configuration is not valid, ``validate`` reports an unrecoverable error by throwing an error object. + + + + +.. _roles_create_custom_role_apply: + +Applying a role configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To apply a validated configuration, define the :ref:`apply([cfg]) ` function. +As the ``validate()`` function, ``apply()`` also provides access to a role's configuration using the ``cfg`` argument. + +In the example below, the ``apply_config()`` function uses the :ref:`log ` module to write a role's configuration value to the log: + +.. literalinclude:: /code_snippets/snippets/config/instances.enabled/application_role_cfg/greeter.lua + :language: lua + :start-at: function apply_config + :end-before: function stop_role + :dedent: + +``apply_config`` should be assigned to ``apply``: + +.. literalinclude:: /code_snippets/snippets/config/instances.enabled/application_role_cfg/greeter.lua + :language: lua + :start-at: return + :dedent: + + + +.. _roles_create_custom_role_stop: + +Stopping a role +~~~~~~~~~~~~~~~ + +To stop a role, use the :ref:`stop() ` function. + +In the example below, the ``stop()`` function uses the :ref:`log ` module to indicate that a role is stopped: + +.. literalinclude:: /code_snippets/snippets/config/instances.enabled/application_role_cfg/greeter.lua + :language: lua + :start-at: function stop_role + :end-before: return + :dedent: + + + +.. _roles_create_custom_role_dependencies: + +Role dependencies +~~~~~~~~~~~~~~~~~ + +To define a role's dependencies, use the :ref:`dependencies ` field. +In this example, the ``byeer`` role has the ``greeter`` role as the dependency: + +.. literalinclude:: /code_snippets/snippets/config/instances.enabled/application_role_cfg/byeer.lua + :language: lua + :dedent: + +A role cannot be started without its dependencies. +This means that all the dependencies of a role should be defined in the ``roles`` configuration parameter: + +.. code-block:: yaml + + instance001: + roles: [ greeter, byeer ] + +You can find the full example here: `application_role_cfg `_. + + + +.. _roles_create_custom_role_init: + +Adding initialization code +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can add initialization code to a role by defining and calling a function with an arbitrary name at the top level of a module, for example: + +.. code-block:: lua + + local function init() + -- ... -- + end + + init() + +For example, you can :ref:`create spaces `, define :ref:`indexes `, or :ref:`grant privileges ` to specific users or roles. + +See also: :ref:`roles_create_space`. + + + +.. _roles_create_space: + +Specifics of creating spaces +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To create a space in a role, you need to make sure that the target instance is in read-write mode (its :ref:`box.info.ro ` is ``false``). +Depending on the :ref:`replication.failover ` mode, you can check an instance state as follows: + +- ``manual``: Use one of the following approaches: + + - Subscribe to the ``box.status`` event using :ref:`box.watch() `. + + .. code-block:: lua + + box.watch('box.status', function() + -- creating a space + -- ... + end) + + - Create a :ref:`fiber ` and use :ref:`box.ctl.wait_rw() ` to wait until ``box.info.ro`` is ``false``. + +- ``election``: Use the :ref:`box.ctl.on_election() ` trigger. + +.. NOTE:: + + Given that a role may be enabled when an instance is already in read-write mode, + you also need to execute schema initialization code from :ref:`apply() `. + To make sure a space is created only once, use the :ref:`if_not_exists ` option. + + + +.. _roles_create_custom_role_life_cycle: + +Roles life cycle +---------------- + +Roles' life cycle includes the following stages: + +1) Loading roles + + On each run, all roles are loaded in the order they are specified in a :ref:`configuration `. + A role cannot be started if it has :ref:`dependencies ` that are not specified in a configuration. + + .. NOTE:: + + Dependencies do not affect the order of how roles are loaded. + At the same time, the ``validate()`` and ``apply()`` functions are executed taking into account the dependencies. + +2) Executing initialization code + + This stage is in effect when a role is enabled or an instance with this role is restarted. + At this stage, a role executes the :ref:`initialization code `. + +3) Stopping roles + + This stage is in effect on configuration reload when a role is removed from configuration for the given instance. + Note that all ``stop()`` calls are performed before any ``validate()`` or ``apply()`` calls + This means that old roles are stopped first and only then new roles are started. + + .. NOTE:: + + Note that roles removed from a configuration are stopped in the order reversed to the order they were specified in a configuration. + + +4) Validating roles' configurations + + At this stage, a configuration for each role is validated using the corresponding :ref:`validate() ` function in the same order as they specified in a configuration. + + For roles that :ref:`depend ` on each other, their ``validate()`` and ``apply()`` functions are executed taking into account the dependencies. + Suppose, three roles depend on each other as follows: + + .. code-block:: none + + role1 + └─── role2 + └─── role3 + + In this case, ``validate()`` and ``apply()`` for these roles are executed in the following order: + + .. code-block:: none + + role3 -> role2 -> role1 + + +5) Applying roles' configurations + + At this stage, a configuration for each role is applied using the corresponding :ref:`apply() ` function in the same order as they specified in a configuration. + + +All role's functions report an unrecoverable error by throwing an error object. +If an error is thrown in any phase, applying a configuration is stopped. +If starting or stopping a role throws an error, no roles are stopped or started afterward. +An error is catched and shown in :ref:`config:info() ` in the ``alerts`` section. + + + + +.. _roles_example_custom_role: + +Example: Role without a configuration +------------------------------------- + +The example below shows how to enable the custom ``greeter`` role for ``instance001``: + +.. literalinclude:: /code_snippets/snippets/config/instances.enabled/application_role/config.yaml + :language: yaml + :start-at: instance001 + :end-at: greeter + :dedent: + +You can define the ``validate()``, ``apply()``, and ``stop()`` functions in the role's code as follows: + +.. literalinclude:: /code_snippets/snippets/config/instances.enabled/application_role/greeter.lua + :language: lua + :dedent: + +Example on GitHub: `application_role `_ + + + +.. _roles_example_custom_role_with_config: + +Example: Role with a configuration +---------------------------------- + +The example below shows how to enable the custom ``greeter`` role for ``instance001`` and specify the configuration for this role: + +.. literalinclude:: /code_snippets/snippets/config/instances.enabled/application_role_cfg/config.yaml + :language: yaml + :start-at: instance001 + :end-at: greeting + :dedent: + +You can define the ``validate()``, ``apply()``, and ``stop()`` functions in the role's code as follows: + +.. literalinclude:: /code_snippets/snippets/config/instances.enabled/application_role_cfg/greeter.lua + :language: lua + :dedent: + +Example on GitHub: `application_role_cfg `_ + + + + +.. _roles_example_custom_role_http_api: + +Example: HTTP API +----------------- + +The example below shows how to enable and configure the ``http-api`` custom role: + +.. literalinclude:: /code_snippets/snippets/config/instances.enabled/application_role_http_api/config.yaml + :language: yaml + :start-at: instance001 + :end-at: 8080 + :dedent: + +The implementation of this role looks as follows: + +.. literalinclude:: /code_snippets/snippets/config/instances.enabled/application_role_http_api/http-api.lua + :language: lua + :dedent: + +Example on GitHub: `application_role_http_api `_ + + + + + +.. _roles_api_reference: + +API Reference +------------- + +.. container:: table + + .. rst-class:: left-align-column-1 + .. rst-class:: left-align-column-2 + + .. list-table:: + :widths: 35 65 + + * - **Members** + - + + * - :ref:`validate([cfg]) ` + - Validate a role's configuration. + + * - :ref:`apply([cfg]) ` + - Apply a role's configuration. + + * - :ref:`stop() ` + - Stop a role. + + * - :ref:`dependencies ` + - Define a role's dependencies. + + + + +.. _roles_api_reference_validate: + +.. function:: validate([cfg]) + + Validate a role's configuration. + This function is called on instance startup or when the :ref:`configuration is reloaded ` for the instance with this role. + Note that the ``validate()`` function is called regardless of whether the role's configuration or any field in a cluster's configuration is changed. + + ``validate()`` should throw an error if the validation fails. + + :param cfg: a role's role configuration to be validated; this parameter provides access to configuration options defined in :ref:`roles_cfg.\ ` + + See also: :ref:`roles_create_custom_role_validate` + + +.. _roles_api_reference_apply: + +.. function:: apply([cfg]) + + Apply a role's configuration. + ``apply()`` is called after ``validate()`` is executed for all the enabled roles. + As the ``validate()`` function, ``apply()`` is called on instance startup or when the configuration is reloaded for the instance with this role. + + ``apply()`` should throw an error if the specified configuration can't be applied. + + .. NOTE:: + + Note that ``apply()`` is not invoked if an instance switches to read-write mode when :ref:`replication.failover ` is set to ``election`` or ``supervised``. + For example, you can use the :ref:`box.ctl.on_election() ` trigger for the ``election`` failover mode to handle this event. + + :param cfg: a role's role configuration to be applied; this parameter provides access to configuration options defined in :ref:`roles_cfg.\ ` + + See also: :ref:`roles_create_custom_role_apply` + + +.. _roles_api_reference_stop: + +.. function:: stop() + + Stop a role. + This function is called on configuration reload if the role is removed from ``roles`` for the given instance. + + See also: :ref:`roles_create_custom_role_stop` + +.. _roles_api_reference_dependencies: + +.. data:: dependencies + + (Optional) Define a role's dependencies. + + :rtype: table + + See also: :ref:`roles_create_custom_role_dependencies` + diff --git a/doc/book/admin/instance_config.rst b/doc/book/cluster_app/instance_config.rst similarity index 98% rename from doc/book/admin/instance_config.rst rename to doc/book/cluster_app/instance_config.rst index 054f6e7d64..64cb813802 100644 --- a/doc/book/admin/instance_config.rst +++ b/doc/book/cluster_app/instance_config.rst @@ -20,7 +20,7 @@ The main steps of creating and preparing the application for deployment are: In this section, a `sharded_cluster `_ application is used as an example. This cluster includes 5 instances: one router and 4 storages, which constitute two replica sets. -.. image:: admin_instances_dev.png +.. image:: /book/admin/admin_instances_dev.png :align: left :width: 700 :alt: Cluster topology @@ -171,7 +171,7 @@ define instances to run on each machine by changing the content of the ``instanc - On the developer's machine, this file might include all the instances defined in the cluster configuration. - .. image:: admin_instances_dev.png + .. image:: /book/admin/admin_instances_dev.png :align: left :width: 700 :alt: Cluster topology @@ -184,7 +184,7 @@ define instances to run on each machine by changing the content of the ``instanc - In the production environment, this file includes instances to run on the specific machine. - .. image:: admin_instances_prod.png + .. image:: /book/admin/admin_instances_prod.png :align: left :width: 700 :alt: Cluster topology diff --git a/doc/book/index.rst b/doc/book/index.rst index aed53f9158..37138cb9ee 100644 --- a/doc/book/index.rst +++ b/doc/book/index.rst @@ -17,6 +17,7 @@ User's Guide box/index admin/index monitoring/index + cluster_app connectors ../reference/reference_sql/index faq diff --git a/doc/index.rst b/doc/index.rst index 3255bf13da..9d17dd8a06 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -56,6 +56,7 @@ concepts/index CRUD operations book/admin/index + book/cluster_app book/connectors enterprise/index reference/index diff --git a/doc/toctree.rst b/doc/toctree.rst index e85d81075d..68c79cbbda 100644 --- a/doc/toctree.rst +++ b/doc/toctree.rst @@ -11,6 +11,7 @@ concepts/index CRUD operations book/admin/index + book/cluster_app book/connectors enterprise/index reference/index From d398494520a189054bacc93e25930fd2469cdbd4 Mon Sep 17 00:00:00 2001 From: andreyaksenov Date: Tue, 4 Jun 2024 10:39:30 +0300 Subject: [PATCH 07/12] Roles: configuration docs --- doc/concepts/configuration.rst | 316 ++++++++++++++++++--------------- 1 file changed, 171 insertions(+), 145 deletions(-) diff --git a/doc/concepts/configuration.rst b/doc/concepts/configuration.rst index 5e1fcd171d..75c9d8765a 100644 --- a/doc/concepts/configuration.rst +++ b/doc/concepts/configuration.rst @@ -58,18 +58,22 @@ A cluster's topology includes the following elements, starting from the lower le You can flexibly configure a cluster's settings on different levels: from global settings applied to all groups to parameters specific for concrete instances. +.. NOTE:: + + All the available options are documented in the :ref:`Configuration reference `. + .. _configuration_file: Configuration in a file -~~~~~~~~~~~~~~~~~~~~~~~ +----------------------- This section provides an overview on how to configure Tarantool in a YAML file. .. _configuration_instance_basic: Basic instance configuration -**************************** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The example below shows a sample configuration of a single Tarantool instance: @@ -86,7 +90,7 @@ The example below shows a sample configuration of a single Tarantool instance: .. _configuration_scopes: Configuration scopes -******************** +~~~~~~~~~~~~~~~~~~~~ This section shows how to control a scope the specified configuration option is applied to. Most of the configuration options can be applied to a specific instance, replica set, group, or to all instances globally. @@ -141,7 +145,7 @@ For example, if the same option is defined at the instance and global level, the .. _configuration_replica_set_scopes: Configuration scopes: Replica set example -***************************************** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The example below shows how specific configuration options work in different configuration scopes for a replica set with a manual failover. You can learn more about configuring replication from :ref:`Replication tutorials `. @@ -174,55 +178,129 @@ You can learn more about configuring replication from :ref:`Replication tutorial .. _configuration_application: +.. _configuration_application_roles: + +Enabling and configuring roles +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An application role is a Lua module that implements specific functions or logic. +You can enable or turn off a specific role for certain instances in a configuration without restarting these instances. + +This section describes how to enable and configure roles. +To learn how to develop custom roles, see :ref:`application_roles`. -Loading an application -********************** +.. _configuration_application_roles_enable: -Using Tarantool as an application server, you can run your own Lua applications. -In the ``app`` section, you can load the application and provide a custom application configuration in the ``cfg`` section. +Enabling a role +*************** -In the example below, the application is loaded from the ``myapp.lua`` file placed next to the YAML configuration file: +To enable or turn off a role for a specific instance or a set of instances, use the :ref:`roles ` configuration option. +The example below shows how to enable the ``roles.crud-router`` role provided by the `CRUD `__ module using the :ref:`roles ` option: -.. literalinclude:: /code_snippets/snippets/config/instances.enabled/application/config.yaml +.. literalinclude:: /code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/config.yaml :language: yaml + :start-at: roles.crud-router + :end-at: roles.crud-router :dedent: -To get a value of the custom ``greeting`` property in the application code, -use the ``config:get()`` function provided by the :ref:`config ` module. +Similarly, you can enable the ``roles.crud-storage`` role to make instances act as CRUD storages: -.. literalinclude:: /code_snippets/snippets/config/instances.enabled/application/myapp.lua - :language: lua +.. literalinclude:: /code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/config.yaml + :language: yaml + :start-at: roles.crud-storage + :end-at: roles.crud-storage :dedent: -As a result of :ref:`starting ` the *instance001*, a log should contain the following line: +Example on GitHub: `sharded_cluster_crud `_ -.. code-block:: console - main/103/interactive/myapp I> Hello from app, instance001! +.. _configuration_application_roles_configure: -The ``app`` section can be placed in any :ref:`configuration scope `. -As an example use case, you can provide different applications for storages and routers in a sharded cluster: +Configuring a role +****************** -.. code-block:: yaml +The :ref:`roles_cfg ` option allows you to specify the configuration for each role. +In this option, the role name is the key and the role configuration is the value. + +The example below shows how to enable statistics on called operations by providing the ``roles.crud-router`` role's configuration: + +.. literalinclude:: /code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/config.yaml + :language: yaml + :start-at: roles.crud-router + :end-at: stats_quantile_max_age_time + :dedent: + +Example on GitHub: `sharded_cluster_crud `_ + + + +.. _configuration_application_roles_scopes: + +Roles and configuration scopes +****************************** + +As the most of configuration options, roles and their configurations can be defined at :ref:`different levels `. +Given that the ``roles`` option has the ``array`` type and ``roles_cfg`` has the ``map`` type, different rules are used when applying the configuration: + +- For ``roles``, an instance's role takes precedence over roles defined at another levels. + In the example below, ``instance001`` has only ``role3``: + + .. code-block:: yaml + + # ... + replicaset001: + roles: [ role1, role2 ] + instances: + instance001: + roles: [ role3 ] + + Learn more about the order of precedence for different configuration scopes in :ref:`configuration_scopes`. + +- For ``roles_cfg``, the following rules are applied: + + - If the same role's option exists at different levels, an instance's option takes precedence over options defined at another levels. + In the example below, ``role1.greeting`` is ``'Hi'``: + + .. code-block:: yaml + + # ... + replicaset001: + roles_cfg: + role1: + greeting: 'Hello' + instances: + instance001: + roles: [ role1 ] + roles_cfg: + role1: + greeting: 'Hi' + + - If some role's option exists at one level but doesn't exist at another level, these options are merged. + In the example below, ``role1.greeting`` is ``'Hi'`` and ``role1.farewell`` is ``'Goodbye'``: + + .. code-block:: yaml + + # ... + replicaset001: + roles_cfg: + role1: + greeting: 'Hello' + farewell: 'Goodbye' + instances: + instance001: + roles: [ role1 ] + roles_cfg: + role1: + greeting: 'Hi' - groups: - storages: - app: - module: storage - # ... - routers: - app: - module: router - # ... -Learn more about using Tarantool as the application server from :ref:`Developing applications with Tarantool `. .. _configuration_predefined_variables: Predefined variables -******************** +~~~~~~~~~~~~~~~~~~~~ In a configuration file, you can use the following predefined variables that are replaced with actual values at runtime: @@ -237,14 +315,14 @@ In the example below, ``{{ instance_name }}`` is replaced with *instance001*. :language: yaml :dedent: -As a result, the :ref:`paths to snapshots and write-ahead logs ` differ for different instances. +As a result, the paths to :ref:`snapshots and write-ahead logs ` differ for different instances. .. _configuration_environment_variable: Environment variables -~~~~~~~~~~~~~~~~~~~~~ +--------------------- For each configuration parameter, Tarantool provides two sets of predefined environment variables: @@ -262,66 +340,93 @@ To see all the supported environment variables, execute the ``tarantool`` comman $ tarantool --help-env-list -Below are a few examples that show how to set environment variables of different types, like *string*, *number*, *array*, or *map*: +.. NOTE:: + + There are also special ``TT_INSTANCE_NAME`` and ``TT_CONFIG`` environment variables that can be used to :ref:`start ` the specified Tarantool instance with configuration from the given file. -* String. In this example, ``TT_LOG_LEVEL`` is used to set a logging level to ``CRITICAL``: +Below are a few examples that show how to set environment variables of different types, like *string*, *number*, *array*, or *map*. - .. code-block:: console +.. _configuration_environment_variable_string: - $ export TT_LOG_LEVEL='crit' +String +~~~~~~ -* Number. In this example, a logging level is set to ``CRITICAL`` using a corresponding numeric value: +In this example, ``TT_LOG_LEVEL`` is used to set a logging level to ``CRITICAL``: - .. code-block:: console +.. code-block:: console - $ export TT_LOG_LEVEL=3 + $ export TT_LOG_LEVEL='crit' -* Array. The examples below show how to set the ``TT_SHARDING_ROLES`` variable that accepts an array value. - Arrays can be passed in two ways: using a *simple* ... - .. code-block:: console +.. _configuration_environment_variable_number: - $ export TT_SHARDING_ROLES=router,storage +Number +~~~~~~ - ... or *JSON* format: +In this example, a logging level is set to ``CRITICAL`` using a corresponding numeric value: - .. code-block:: console +.. code-block:: console - $ export TT_SHARDING_ROLES='["router", "storage"]' + $ export TT_LOG_LEVEL=3 - The *simple* format is applicable only to arrays containing scalar values. +.. _configuration_environment_variable_array: -* Map. To assign map values to environment variables, you can also use *simple* or *JSON* formats. - In the example below, ``TT_LOG_MODULES`` sets different logging levels for different modules using a *simple* format: +Array +~~~~~ - .. code-block:: console +The examples below show how to set the ``TT_SHARDING_ROLES`` variable that accepts an array value. +Arrays can be passed in two ways: using a *simple* ... - $ export TT_LOG_MODULES=module1=info,module2=error +.. code-block:: console - In the next example, ``TT_APP_CFG`` is used to specify the value of a custom configuration property for a :ref:`loaded application ` using a *JSON* format: + $ export TT_SHARDING_ROLES=router,storage - .. code-block:: console +... or *JSON* format: - $ export TT_APP_CFG='{"greeting":"Hi"}' +.. code-block:: console - The *simple* format is applicable only to maps containing scalar values. + $ export TT_SHARDING_ROLES='["router", "storage"]' -* Array of maps. In the example below, ``TT_IPROTO_LISTEN`` is used to specify a :ref:`listening host and port ` values: +The *simple* format is applicable only to arrays containing scalar values. - .. code-block:: console - $ export TT_IPROTO_LISTEN=['{"uri":"127.0.0.1:3311"}'] +.. _configuration_environment_variable_map: - You can also pass several listening addresses: +Map +~~~ - .. code-block:: console +To assign map values to environment variables, you can also use *simple* or *JSON* formats. +In the example below, ``TT_LOG_MODULES`` sets different logging levels for different modules using a *simple* format: - $ export TT_IPROTO_LISTEN=['{"uri":"127.0.0.1:3311"}','{"uri":"127.0.0.1:3312"}'] +.. code-block:: console + $ export TT_LOG_MODULES=module1=info,module2=error -.. NOTE:: +In the next example, ``TT_ROLES_CFG`` is used to specify the value of a custom configuration for a :ref:`role ` using a *JSON* format: - There are also special ``TT_INSTANCE_NAME`` and ``TT_CONFIG`` environment variables that can be used to :ref:`start ` the specified Tarantool instance with configuration from the given file. +.. code-block:: console + + $ export TT_ROLES_CFG='{"greeter":{"greeting":"Hello"}}' + +The *simple* format is applicable only to maps containing scalar values. + + +.. _configuration_environment_variable_array_of_maps: + +Array of maps +~~~~~~~~~~~~~ + +In the example below, ``TT_IPROTO_LISTEN`` is used to specify a :ref:`listening host and port ` values: + +.. code-block:: console + + $ export TT_IPROTO_LISTEN=['{"uri":"127.0.0.1:3311"}'] + +You can also pass several listening addresses: + +.. code-block:: console + + $ export TT_IPROTO_LISTEN=['{"uri":"127.0.0.1:3311"}','{"uri":"127.0.0.1:3312"}'] @@ -331,7 +436,7 @@ Below are a few examples that show how to set environment variables of different .. _configuration_etcd_overview: Centralized configuration -~~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------- .. include:: /concepts/configuration/configuration_etcd.rst :start-after: ee_note_centralized_config_start @@ -358,7 +463,7 @@ Learn more from the following guide: :ref:`configuration_etcd`. .. _configuration_precedence: Configuration precedence -~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------ Tarantool configuration options are applied from multiple sources with the following precedence, from highest to lowest: @@ -371,85 +476,6 @@ If the same option is defined in two or more locations, the option with the high -.. _configuration_options_overview: - -Configuration options overview ------------------------------- - -This section gives an overview of some useful configuration options. -All the available options are documented in the :ref:`Configuration reference `. - -.. _configuration_options_connection: - -Connection settings -~~~~~~~~~~~~~~~~~~~ - -To configure an address used to listen for incoming requests, use the ``iproto.listen`` option. -The example below shows how to set a listening IP address for ``instance001`` to ``127.0.0.1:3301``: - -.. literalinclude:: /code_snippets/snippets/config/instances.enabled/iproto_listen_address/config.yaml - :start-at: instance001 - :end-at: '127.0.0.1:3301' - :language: yaml - :dedent: - -You can learn more from the :ref:`configuration_connections` topic. - - -.. _configuration_options_access_control: - -Access control -~~~~~~~~~~~~~~ - -The ``credentials`` section allows you to create users and grant them the specified privileges. -In the example below, a ``dbadmin`` user with the specified password is created: - -.. literalinclude:: /code_snippets/snippets/config/instances.enabled/credentials/config.yaml - :language: yaml - :start-at: credentials: - :end-at: T0p_Secret - :dedent: - -To learn more, see the :ref:`configuration_credentials` section. - - -.. _configuration_options_memory: - -Memory -~~~~~~ - -The :ref:`memtx.memory ` option specifies how much :ref:`memory ` -Tarantool allocates to actually store data. - -.. literalinclude:: /code_snippets/snippets/config/instances.enabled/memtx/config.yaml - :language: yaml - :start-at: memtx: - :end-at: 1073741824 - :dedent: - -When the limit is reached, ``INSERT`` or ``UPDATE`` requests fail with :ref:`ER_MEMORY_ISSUE `. - -Learn more: :ref:`In-memory storage configuration `. - -.. _configuration_options_directories: - -Snapshots and write-ahead logs -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The :ref:`snapshot.dir ` and :ref:`wal.dir ` -options can be used to configure directories for storing snapshots and write-ahead logs. -For example, you can place snapshots and write-ahead logs on different hard drives for better reliability. - -.. code-block:: yaml - - instance001: - snapshot: - dir: '/media/drive1/snapshots' - wal: - dir: '/media/drive2/wals' - -To learn more about the persistence mechanism in Tarantool, see the :ref:`Persistence ` section. -Read more about snapshot and WAL configuration: :ref:`Persistence `. .. toctree:: From 1dd9de054905bb779b71d88a4d6b293ffc612017 Mon Sep 17 00:00:00 2001 From: andreyaksenov Date: Wed, 5 Jun 2024 17:34:00 +0300 Subject: [PATCH 08/12] Roles: update per DEV review --- doc/book/admin/index.rst | 1 + .../instance_config.rst | 0 doc/book/cluster_app.rst | 14 -- .../application_role_cfg/greeter.lua | 12 +- .../application_role_http_api/http-api.lua | 14 +- .../sharded_cluster_crud/config.yaml | 12 +- doc/concepts/configuration.rst | 17 +- .../cluster_app => how-to/app}/app_roles.rst | 156 ++++++++++-------- doc/how-to/app/index.rst | 1 + doc/index.rst | 1 - doc/toctree.rst | 1 - 11 files changed, 113 insertions(+), 116 deletions(-) rename doc/book/{cluster_app => admin}/instance_config.rst (100%) delete mode 100644 doc/book/cluster_app.rst rename doc/{book/cluster_app => how-to/app}/app_roles.rst (78%) diff --git a/doc/book/admin/index.rst b/doc/book/admin/index.rst index 1fd4f1615d..0396a88f28 100644 --- a/doc/book/admin/index.rst +++ b/doc/book/admin/index.rst @@ -25,6 +25,7 @@ This chapter includes the following sections: :maxdepth: 2 :numbered: 0 + instance_config start_stop_instance modules logs diff --git a/doc/book/cluster_app/instance_config.rst b/doc/book/admin/instance_config.rst similarity index 100% rename from doc/book/cluster_app/instance_config.rst rename to doc/book/admin/instance_config.rst diff --git a/doc/book/cluster_app.rst b/doc/book/cluster_app.rst deleted file mode 100644 index 51fb25fd7e..0000000000 --- a/doc/book/cluster_app.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. _cluster_app: - -Cluster applications -==================== - -.. NOTE:: - - This section is under development. - -.. toctree:: - :maxdepth: 1 - - cluster_app/instance_config - cluster_app/app_roles diff --git a/doc/code_snippets/snippets/config/instances.enabled/application_role_cfg/greeter.lua b/doc/code_snippets/snippets/config/instances.enabled/application_role_cfg/greeter.lua index cf56cb2027..2be88cde8a 100644 --- a/doc/code_snippets/snippets/config/instances.enabled/application_role_cfg/greeter.lua +++ b/doc/code_snippets/snippets/config/instances.enabled/application_role_cfg/greeter.lua @@ -1,23 +1,23 @@ -- greeter.lua -- local log = require('log').new("greeter") -local function validate_config(cfg) +local function validate(cfg) if cfg.greeting then assert(type(cfg.greeting) == "string", "'greeting' should be a string") assert(cfg.greeting == "Hi" or cfg.greeting == "Hello", "'greeting' should be 'Hi' or 'Hello'") end end -local function apply_config(cfg) +local function apply(cfg) log.info("%s from the 'greeter' role!", cfg.greeting) end -local function stop_role() +local function stop() log.info("The 'greeter' role is stopped") end return { - validate = validate_config, - apply = apply_config, - stop = stop_role, + validate = validate, + apply = apply, + stop = stop, } diff --git a/doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/http-api.lua b/doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/http-api.lua index dd1961a4ee..e54d20b45d 100644 --- a/doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/http-api.lua +++ b/doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/http-api.lua @@ -2,17 +2,17 @@ local httpd local json = require('json') -local function validate_config(cfg) +local function validate(cfg) if cfg.host then assert(type(cfg.host) == "string", "'host' should be a string containing a valid IP address") end if cfg.port then assert(type(cfg.port) == "number", "'port' should be a number") - assert(cfg.port >= 1 and cfg.port <= 65536, "'port' should be between 1 and 65536") + assert(cfg.port >= 1 and cfg.port <= 65535, "'port' should be between 1 and 65535") end end -local function apply_config(cfg) +local function apply(cfg) if httpd then httpd:stop() end @@ -48,7 +48,7 @@ local function apply_config(cfg) httpd:start() end -local function stop_role() +local function stop() httpd:stop() end @@ -59,7 +59,7 @@ end init() return { - validate = validate_config, - apply = apply_config, - stop = stop_role, + validate = validate, + apply = apply, + stop = stop, } diff --git a/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/config.yaml b/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/config.yaml index b414e60201..9135ca916e 100644 --- a/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/config.yaml +++ b/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/config.yaml @@ -2,10 +2,10 @@ credentials: users: replicator: password: 'topsecret' - roles: [replication] + roles: [ replication ] storage: password: 'secret' - roles: [sharding] + roles: [ sharding ] iproto: advertise: @@ -19,11 +19,11 @@ sharding: groups: storages: - roles: [roles.crud-storage] + roles: [ roles.crud-storage ] app: module: storage sharding: - roles: [storage] + roles: [ storage ] replication: failover: manual replicasets: @@ -50,7 +50,7 @@ groups: listen: - uri: '127.0.0.1:3305' routers: - roles: [roles.crud-router] + roles: [ roles.crud-router ] roles_cfg: roles.crud-router: stats: true @@ -62,7 +62,7 @@ groups: app: module: router sharding: - roles: [router] + roles: [ router ] replicasets: router-a: instances: diff --git a/doc/concepts/configuration.rst b/doc/concepts/configuration.rst index 75c9d8765a..651669c363 100644 --- a/doc/concepts/configuration.rst +++ b/doc/concepts/configuration.rst @@ -240,7 +240,7 @@ Roles and configuration scopes ****************************** As the most of configuration options, roles and their configurations can be defined at :ref:`different levels `. -Given that the ``roles`` option has the ``array`` type and ``roles_cfg`` has the ``map`` type, different rules are used when applying the configuration: +Given that the ``roles`` option has the ``array`` type and ``roles_cfg`` has the ``map`` type, there are some specifics when applying the configuration: - For ``roles``, an instance's role takes precedence over roles defined at another levels. In the example below, ``instance001`` has only ``role3``: @@ -258,7 +258,7 @@ Given that the ``roles`` option has the ``array`` type and ``roles_cfg`` has the - For ``roles_cfg``, the following rules are applied: - - If the same role's option exists at different levels, an instance's option takes precedence over options defined at another levels. + - If a configuration for the same role is provided at different levels, an instance configuration takes precedence over configuration defined at another levels. In the example below, ``role1.greeting`` is ``'Hi'``: .. code-block:: yaml @@ -275,8 +275,8 @@ Given that the ``roles`` option has the ``array`` type and ``roles_cfg`` has the role1: greeting: 'Hi' - - If some role's option exists at one level but doesn't exist at another level, these options are merged. - In the example below, ``role1.greeting`` is ``'Hi'`` and ``role1.farewell`` is ``'Goodbye'``: + - If configurations for different roles are provided at different levels, both configurations are applied at the instance level. + In the example below, ``instance001`` has ``role1.greeting`` set to ``'Hi'`` and ``role2.farewell`` set to ``'Bye'``: .. code-block:: yaml @@ -284,14 +284,13 @@ Given that the ``roles`` option has the ``array`` type and ``roles_cfg`` has the replicaset001: roles_cfg: role1: - greeting: 'Hello' - farewell: 'Goodbye' + greeting: 'Hi' instances: instance001: - roles: [ role1 ] + roles: [ role1, role2 ] roles_cfg: - role1: - greeting: 'Hi' + role2: + farewell: 'Bye' diff --git a/doc/book/cluster_app/app_roles.rst b/doc/how-to/app/app_roles.rst similarity index 78% rename from doc/book/cluster_app/app_roles.rst rename to doc/how-to/app/app_roles.rst index 46505617b9..dce8377826 100644 --- a/doc/book/cluster_app/app_roles.rst +++ b/doc/how-to/app/app_roles.rst @@ -46,13 +46,8 @@ This example shows how to enable and configure the ``greeter`` role, which is im The role's configuration provided in ``roles_cfg`` can be accessed when :ref:`validating ` and :ref:`applying ` this configuration. -Given that a role is a Lua module, a role's name is passed to ``require()`` to obtain the module. -This means that to implement the ``greeter`` role, the ``greeter.lua`` file should be created next to a cluster's configuration file. - -.. NOTE:: - - You can also provide a role's configuration in any place inside a cluster's configuration file. - In this case, you can get configuration values using :ref:`config:get() `. +Given that a role is a :ref:`Lua module `, a role's name is passed to ``require()`` to obtain the module. +When :ref:`developing an application `, you can place a file with a role's code next to a cluster's configuration file. @@ -101,19 +96,12 @@ Validating a role configuration To validate a role's configuration, you need to define the :ref:`validate([cfg]) ` function. The ``cfg`` argument allows you to get access to the provided :ref:`role's configuration ` and check its validity. -In the example below, the ``validate_config()`` function is used to validate the ``greeting`` configuration value: - -.. literalinclude:: /code_snippets/snippets/config/instances.enabled/application_role_cfg/greeter.lua - :language: lua - :start-at: function validate_config - :end-before: function apply_config - :dedent: - -``validate_config`` should be assigned to ``validate``: +In the example below, the ``validate()`` function is used to validate the ``greeting`` configuration value: .. literalinclude:: /code_snippets/snippets/config/instances.enabled/application_role_cfg/greeter.lua :language: lua - :start-at: return + :start-at: local function validate + :end-before: local function apply :dedent: If the configuration is not valid, ``validate`` reports an unrecoverable error by throwing an error object. @@ -129,19 +117,12 @@ Applying a role configuration To apply a validated configuration, define the :ref:`apply([cfg]) ` function. As the ``validate()`` function, ``apply()`` also provides access to a role's configuration using the ``cfg`` argument. -In the example below, the ``apply_config()`` function uses the :ref:`log ` module to write a role's configuration value to the log: - -.. literalinclude:: /code_snippets/snippets/config/instances.enabled/application_role_cfg/greeter.lua - :language: lua - :start-at: function apply_config - :end-before: function stop_role - :dedent: - -``apply_config`` should be assigned to ``apply``: +In the example below, the ``apply()`` function uses the :ref:`log ` module to write a role's configuration value to the log: .. literalinclude:: /code_snippets/snippets/config/instances.enabled/application_role_cfg/greeter.lua :language: lua - :start-at: return + :start-at: local function apply + :end-before: local function stop :dedent: @@ -157,10 +138,17 @@ In the example below, the ``stop()`` function uses the :ref:`log ` m .. literalinclude:: /code_snippets/snippets/config/instances.enabled/application_role_cfg/greeter.lua :language: lua - :start-at: function stop_role + :start-at: local function stop :end-before: return :dedent: +When you've defined all the role's functions, you need to return an object that has corresponding functions specified: + +.. literalinclude:: /code_snippets/snippets/config/instances.enabled/application_role_cfg/greeter.lua + :language: lua + :start-at: return + :dedent: + .. _roles_create_custom_role_dependencies: @@ -214,22 +202,15 @@ Specifics of creating spaces ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To create a space in a role, you need to make sure that the target instance is in read-write mode (its :ref:`box.info.ro ` is ``false``). -Depending on the :ref:`replication.failover ` mode, you can check an instance state as follows: +You can check an instance state by subscribing to the ``box.status`` event using :ref:`box.watch() `: -- ``manual``: Use one of the following approaches: - - - Subscribe to the ``box.status`` event using :ref:`box.watch() `. - - .. code-block:: lua - - box.watch('box.status', function() - -- creating a space - -- ... - end) +.. code-block:: lua - - Create a :ref:`fiber ` and use :ref:`box.ctl.wait_rw() ` to wait until ``box.info.ro`` is ``false``. + box.watch('box.status', function() + -- creating a space + -- ... + end) -- ``election``: Use the :ref:`box.ctl.on_election() ` trigger. .. NOTE:: @@ -239,68 +220,95 @@ Depending on the :ref:`replication.failover `. + This stage is in effect when a role is enabled or an instance with this role is restarted. + At this stage, a role executes the :ref:`initialization code `. + A role cannot be started if it has :ref:`dependencies ` that are not specified in a configuration. .. NOTE:: Dependencies do not affect the order of how roles are loaded. - At the same time, the ``validate()`` and ``apply()`` functions are executed taking into account the dependencies. + At the same time, the ``validate()``, ``apply()``, and ``stop()`` functions are executed taking into account the dependencies. + Learn more in :ref:`roles_life_cycle_dependencies_specifics`. -2) Executing initialization code - This stage is in effect when a role is enabled or an instance with this role is restarted. - At this stage, a role executes the :ref:`initialization code `. +.. _roles_life_cycle_stopping_roles: -3) Stopping roles +2) *Stopping roles* This stage is in effect on configuration reload when a role is removed from configuration for the given instance. - Note that all ``stop()`` calls are performed before any ``validate()`` or ``apply()`` calls + Note that all ``stop()`` calls are performed before any ``validate()`` or ``apply()`` calls. This means that old roles are stopped first and only then new roles are started. - .. NOTE:: +.. _roles_life_cycle_validating_role_config: - Note that roles removed from a configuration are stopped in the order reversed to the order they were specified in a configuration. +3) *Validating roles' configurations* + At this stage, a configuration for each role is validated using the corresponding :ref:`validate() ` function in the same order as they specified in a configuration. -4) Validating roles' configurations +.. _roles_life_cycle_applying_role_config: - At this stage, a configuration for each role is validated using the corresponding :ref:`validate() ` function in the same order as they specified in a configuration. +4) *Applying roles' configurations* - For roles that :ref:`depend ` on each other, their ``validate()`` and ``apply()`` functions are executed taking into account the dependencies. - Suppose, three roles depend on each other as follows: + At this stage, a configuration for each role is applied using the corresponding :ref:`apply() ` function in the same order as they specified in a configuration. - .. code-block:: none - role1 - └─── role2 - └─── role3 +All role's functions report an unrecoverable error by throwing an error object. +If an error is thrown in any phase, applying a configuration is stopped. +If starting or stopping a role throws an error, no roles are stopped or started afterward. +An error is catched and shown in :ref:`config:info() ` in the ``alerts`` section. - In this case, ``validate()`` and ``apply()`` for these roles are executed in the following order: - .. code-block:: none +.. _roles_life_cycle_dependencies_specifics: - role3 -> role2 -> role1 +Executing functions for dependent roles +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +For roles that :ref:`depend ` on each other, their ``validate()``, ``apply()``, and ``stop()`` functions are executed taking into account the dependencies. +Suppose, there are two independent roles (``role1``, ``role2``) and three roles that depend on each other as follows: -5) Applying roles' configurations +.. code-block:: none - At this stage, a configuration for each role is applied using the corresponding :ref:`apply() ` function in the same order as they specified in a configuration. + role3 + └─── role4 + └─── role5 +The roles are enable in a configuration as follows: -All role's functions report an unrecoverable error by throwing an error object. -If an error is thrown in any phase, applying a configuration is stopped. -If starting or stopping a role throws an error, no roles are stopped or started afterward. -An error is catched and shown in :ref:`config:info() ` in the ``alerts`` section. +.. code-block:: yaml + + roles: [ role1, role2, role3, role4, role5 ] + +In this case, ``validate()`` and ``apply()`` for these roles are executed in the following order: + +.. code-block:: none + + role1 -> role2 -> role5 -> role4 -> role3 + +Roles removed from a configuration are stopped in the order reversed to the order they are specified in a configuration, taking into account the dependencies. +If all roles except ``role1`` are removed from the configuration above, ... + +.. code-block:: yaml + + roles: [ role1 ] + +... ``stop()`` functions for removed roles are executed in the following order: + +.. code-block:: none + + role3 -> role4 -> role5 -> role2 @@ -418,7 +426,9 @@ API Reference ``validate()`` should throw an error if the validation fails. - :param cfg: a role's role configuration to be validated; this parameter provides access to configuration options defined in :ref:`roles_cfg.\ ` + :param cfg: a role's role configuration to be validated. + This parameter provides access to configuration options defined in :ref:`roles_cfg.\ `. + To get values of configuration options placed outside ``roles_cfg.``, use :ref:`config:get() `. See also: :ref:`roles_create_custom_role_validate` @@ -436,9 +446,11 @@ API Reference .. NOTE:: Note that ``apply()`` is not invoked if an instance switches to read-write mode when :ref:`replication.failover ` is set to ``election`` or ``supervised``. - For example, you can use the :ref:`box.ctl.on_election() ` trigger for the ``election`` failover mode to handle this event. + You can check an instance state by subscribing to the ``box.status`` event using :ref:`box.watch() `. - :param cfg: a role's role configuration to be applied; this parameter provides access to configuration options defined in :ref:`roles_cfg.\ ` + :param cfg: a role's role configuration to be applied. + This parameter provides access to configuration options defined in :ref:`roles_cfg.\ `. + To get values of configuration options placed outside ``roles_cfg.``, use :ref:`config:get() `. See also: :ref:`roles_create_custom_role_apply` diff --git a/doc/how-to/app/index.rst b/doc/how-to/app/index.rst index 9600843978..452b4caf7e 100644 --- a/doc/how-to/app/index.rst +++ b/doc/how-to/app/index.rst @@ -12,6 +12,7 @@ in C or C++. .. toctree:: :maxdepth: 1 + app_roles launching_app creating_app using_ide diff --git a/doc/index.rst b/doc/index.rst index 9d17dd8a06..3255bf13da 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -56,7 +56,6 @@ concepts/index CRUD operations book/admin/index - book/cluster_app book/connectors enterprise/index reference/index diff --git a/doc/toctree.rst b/doc/toctree.rst index 68c79cbbda..e85d81075d 100644 --- a/doc/toctree.rst +++ b/doc/toctree.rst @@ -11,7 +11,6 @@ concepts/index CRUD operations book/admin/index - book/cluster_app book/connectors enterprise/index reference/index From 922d99eaaf1919e14d0bb43af593713dc9671859 Mon Sep 17 00:00:00 2001 From: andreyaksenov Date: Thu, 6 Jun 2024 16:06:16 +0300 Subject: [PATCH 09/12] Roles: cosmetics --- doc/concepts/configuration.rst | 10 +++---- doc/how-to/app/app_roles.rst | 48 +++++++++++++++++----------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/doc/concepts/configuration.rst b/doc/concepts/configuration.rst index 651669c363..72b60129bf 100644 --- a/doc/concepts/configuration.rst +++ b/doc/concepts/configuration.rst @@ -184,7 +184,7 @@ Enabling and configuring roles ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ An application role is a Lua module that implements specific functions or logic. -You can enable or turn off a specific role for certain instances in a configuration without restarting these instances. +You can enable or turn off a particular role for certain instances in a configuration without restarting these instances. This section describes how to enable and configure roles. To learn how to develop custom roles, see :ref:`application_roles`. @@ -240,9 +240,9 @@ Roles and configuration scopes ****************************** As the most of configuration options, roles and their configurations can be defined at :ref:`different levels `. -Given that the ``roles`` option has the ``array`` type and ``roles_cfg`` has the ``map`` type, there are some specifics when applying the configuration: +Given that the ``roles`` option has the ``array`` type and ``roles_cfg`` has the ``map`` type, there are some specifics of applying the configuration: -- For ``roles``, an instance's role takes precedence over roles defined at another levels. +- For ``roles``, an instance's role takes precedence over roles defined at another level. In the example below, ``instance001`` has only ``role3``: .. code-block:: yaml @@ -258,7 +258,7 @@ Given that the ``roles`` option has the ``array`` type and ``roles_cfg`` has the - For ``roles_cfg``, the following rules are applied: - - If a configuration for the same role is provided at different levels, an instance configuration takes precedence over configuration defined at another levels. + - If a configuration for the same role is provided at different levels, an instance configuration takes precedence over the configuration defined at another level. In the example below, ``role1.greeting`` is ``'Hi'``: .. code-block:: yaml @@ -275,7 +275,7 @@ Given that the ``roles`` option has the ``array`` type and ``roles_cfg`` has the role1: greeting: 'Hi' - - If configurations for different roles are provided at different levels, both configurations are applied at the instance level. + - If the configurations for different roles are provided at different levels, both configurations are applied at the instance level. In the example below, ``instance001`` has ``role1.greeting`` set to ``'Hi'`` and ``role2.farewell`` set to ``'Bye'``: .. code-block:: yaml diff --git a/doc/how-to/app/app_roles.rst b/doc/how-to/app/app_roles.rst index dce8377826..5e5574af88 100644 --- a/doc/how-to/app/app_roles.rst +++ b/doc/how-to/app/app_roles.rst @@ -4,7 +4,7 @@ Application roles ================= An application role is a Lua module that implements specific functions or logic. -You can enable or turn off a specific role for certain instances in a :ref:`configuration ` without restarting these instances. +You can enable or turn off a particular role for certain instances in a :ref:`configuration ` without restarting these instances. A role is run when a configuration is loaded or reloaded. Roles can be divided into the following groups: @@ -14,7 +14,7 @@ Roles can be divided into the following groups: - Roles provided by third-party Lua modules. For example, the `CRUD `__ module provides the ``roles.crud-storage`` and ``roles.crud-router`` roles that enable CRUD operations in a sharded cluster. - Custom roles that are developed as a part of a cluster application. - For example, you create a custom role to define a stored procedure or implement a supplementary service, such as an email notifier or a replicator. + For example, you can create a custom role to define a stored procedure or implement a supplementary service, such as an email notifier or a replicator. This section describes how to develop custom roles. To learn how to enable and configure roles, see :ref:`configuration_application_roles`. @@ -61,12 +61,12 @@ Creating a custom role Overview ~~~~~~~~ -The main steps of creating a custom role might include the following steps: +Creating a custom role includes the following steps: 1. Define a function that validates a role's configuration. 2. Define a function that applies a validated configuration. 3. Define a function that stops a role. -4. (Optional) Define roles from which a custom role depends on. +4. (Optional) Define roles from which this custom role depends on. As a result, a role's module should return an object that has corresponding functions and fields specified: @@ -104,7 +104,7 @@ In the example below, the ``validate()`` function is used to validate the ``gree :end-before: local function apply :dedent: -If the configuration is not valid, ``validate`` reports an unrecoverable error by throwing an error object. +If the configuration is not valid, ``validate()`` reports an unrecoverable error by throwing an error object. @@ -114,8 +114,8 @@ If the configuration is not valid, ``validate`` reports an unrecoverable error b Applying a role configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To apply a validated configuration, define the :ref:`apply([cfg]) ` function. -As the ``validate()`` function, ``apply()`` also provides access to a role's configuration using the ``cfg`` argument. +To apply the validated configuration, define the :ref:`apply([cfg]) ` function. +As the ``validate()`` function, ``apply()`` provides access to a role's configuration using the ``cfg`` argument. In the example below, the ``apply()`` function uses the :ref:`log ` module to write a role's configuration value to the log: @@ -225,22 +225,22 @@ You can check an instance state by subscribing to the ``box.status`` event using Roles life cycle ---------------- -Roles' life cycle includes the stages described below. +A role’s life cycle includes the stages described below. .. _roles_life_cycle_loading_roles: 1) *Loading roles* - On each run, all roles are loaded in the order they are specified in a :ref:`configuration `. - This stage is in effect when a role is enabled or an instance with this role is restarted. + On each run, all roles are loaded in the order they are specified in the :ref:`configuration `. + This stage takes effect when a role is enabled or an instance with this role is restarted. At this stage, a role executes the :ref:`initialization code `. A role cannot be started if it has :ref:`dependencies ` that are not specified in a configuration. .. NOTE:: - Dependencies do not affect the order of how roles are loaded. - At the same time, the ``validate()``, ``apply()``, and ``stop()`` functions are executed taking into account the dependencies. + Dependencies do not affect the order in which roles are loaded. + However, the ``validate()``, ``apply()``, and ``stop()`` functions are executed taking dependencies into account. Learn more in :ref:`roles_life_cycle_dependencies_specifics`. @@ -248,27 +248,27 @@ Roles' life cycle includes the stages described below. 2) *Stopping roles* - This stage is in effect on configuration reload when a role is removed from configuration for the given instance. + This stage takes effect during a configuration reload when a role is removed from the configuration for a given instance. Note that all ``stop()`` calls are performed before any ``validate()`` or ``apply()`` calls. - This means that old roles are stopped first and only then new roles are started. + This means that old roles are stopped first, and only then new roles are started. .. _roles_life_cycle_validating_role_config: -3) *Validating roles' configurations* +3) *Validating a role's configurations* - At this stage, a configuration for each role is validated using the corresponding :ref:`validate() ` function in the same order as they specified in a configuration. + At this stage, a configuration for each role is validated using the corresponding :ref:`validate() ` function in the same order in which they are specified in the configuration. .. _roles_life_cycle_applying_role_config: -4) *Applying roles' configurations* +4) *Applying a role's configurations* - At this stage, a configuration for each role is applied using the corresponding :ref:`apply() ` function in the same order as they specified in a configuration. + At this stage, a configuration for each role is applied using the corresponding :ref:`apply() ` function in the same order in which they are specified in the configuration. All role's functions report an unrecoverable error by throwing an error object. If an error is thrown in any phase, applying a configuration is stopped. If starting or stopping a role throws an error, no roles are stopped or started afterward. -An error is catched and shown in :ref:`config:info() ` in the ``alerts`` section. +An error is caught and shown in :ref:`config:info() ` in the ``alerts`` section. .. _roles_life_cycle_dependencies_specifics: @@ -285,7 +285,7 @@ Suppose, there are two independent roles (``role1``, ``role2``) and three roles └─── role4 └─── role5 -The roles are enable in a configuration as follows: +The roles are enabled in a configuration as follows: .. code-block:: yaml @@ -298,13 +298,13 @@ In this case, ``validate()`` and ``apply()`` for these roles are executed in the role1 -> role2 -> role5 -> role4 -> role3 Roles removed from a configuration are stopped in the order reversed to the order they are specified in a configuration, taking into account the dependencies. -If all roles except ``role1`` are removed from the configuration above, ... +Suppose, all roles except ``role1`` are removed from the configuration above: .. code-block:: yaml roles: [ role1 ] -... ``stop()`` functions for removed roles are executed in the following order: +After reloading a configuration, ``stop()`` functions for the removed roles are executed in the following order: .. code-block:: none @@ -326,7 +326,7 @@ The example below shows how to enable the custom ``greeter`` role for ``instance :end-at: greeter :dedent: -You can define the ``validate()``, ``apply()``, and ``stop()`` functions in the role's code as follows: +The implementation of this role looks as follows: .. literalinclude:: /code_snippets/snippets/config/instances.enabled/application_role/greeter.lua :language: lua @@ -349,7 +349,7 @@ The example below shows how to enable the custom ``greeter`` role for ``instance :end-at: greeting :dedent: -You can define the ``validate()``, ``apply()``, and ``stop()`` functions in the role's code as follows: +The implementation of this role looks as follows: .. literalinclude:: /code_snippets/snippets/config/instances.enabled/application_role_cfg/greeter.lua :language: lua From 5f8c589e5cd2f8d6e82c1e99d2b4ee57074a2165 Mon Sep 17 00:00:00 2001 From: andreyaksenov Date: Thu, 6 Jun 2024 20:52:45 +0300 Subject: [PATCH 10/12] Roles: add sample readme --- .../application_role_http_api/README.md | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/README.md diff --git a/doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/README.md b/doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/README.md new file mode 100644 index 0000000000..e5014fae80 --- /dev/null +++ b/doc/code_snippets/snippets/config/instances.enabled/application_role_http_api/README.md @@ -0,0 +1,22 @@ +# HTTP API + +A sample application showing how to implement a custom `http-api` role. + +## Running + +To start an application, execute the following command in the [config](../../../config) directory: + +```console +$ tt start application_role_http_api +``` + +## Making test requests + +To test the API, make the following requests: + +```console +$ curl -X GET --location "http://0.0.0.0:8080/band?limit=7" \ + -H "Accept: application/json" +$ curl -X GET --location "http://0.0.0.0:8080/band/5" \ + -H "Accept: application/json" +``` From 86bee5ec933eb7595c9b6052bd3236ddb2020ee5 Mon Sep 17 00:00:00 2001 From: andreyaksenov Date: Fri, 7 Jun 2024 10:27:39 +0300 Subject: [PATCH 11/12] Roles: cosmetics 2 --- doc/concepts/configuration.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/concepts/configuration.rst b/doc/concepts/configuration.rst index 72b60129bf..0e2c369d02 100644 --- a/doc/concepts/configuration.rst +++ b/doc/concepts/configuration.rst @@ -258,7 +258,7 @@ Given that the ``roles`` option has the ``array`` type and ``roles_cfg`` has the - For ``roles_cfg``, the following rules are applied: - - If a configuration for the same role is provided at different levels, an instance configuration takes precedence over the configuration defined at another level. + - If a configuration *for the same role* is provided at different levels, an instance configuration takes precedence over the configuration defined at another level. In the example below, ``role1.greeting`` is ``'Hi'``: .. code-block:: yaml @@ -275,7 +275,7 @@ Given that the ``roles`` option has the ``array`` type and ``roles_cfg`` has the role1: greeting: 'Hi' - - If the configurations for different roles are provided at different levels, both configurations are applied at the instance level. + - If the configurations *for different roles* are provided at different levels, both configurations are applied at the instance level. In the example below, ``instance001`` has ``role1.greeting`` set to ``'Hi'`` and ``role2.farewell`` set to ``'Bye'``: .. code-block:: yaml From d5cf730479a055327e26c64fe786ca35432ad3e1 Mon Sep 17 00:00:00 2001 From: andreyaksenov Date: Mon, 10 Jun 2024 10:23:46 +0300 Subject: [PATCH 12/12] Roles: update per TW review --- doc/concepts/configuration.rst | 7 +++++-- doc/how-to/app/app_roles.rst | 13 +++++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/doc/concepts/configuration.rst b/doc/concepts/configuration.rst index 0e2c369d02..a895e31e32 100644 --- a/doc/concepts/configuration.rst +++ b/doc/concepts/configuration.rst @@ -184,17 +184,20 @@ Enabling and configuring roles ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ An application role is a Lua module that implements specific functions or logic. -You can enable or turn off a particular role for certain instances in a configuration without restarting these instances. +You can turn on or off a particular role for certain instances in a configuration without restarting these instances. +There can be built-in Tarantool roles, roles provided by third-party Lua modules, or custom roles that are developed as a part of a cluster application. This section describes how to enable and configure roles. To learn how to develop custom roles, see :ref:`application_roles`. + + .. _configuration_application_roles_enable: Enabling a role *************** -To enable or turn off a role for a specific instance or a set of instances, use the :ref:`roles ` configuration option. +To turn on or off a role for a specific instance or a set of instances, use the :ref:`roles ` configuration option. The example below shows how to enable the ``roles.crud-router`` role provided by the `CRUD `__ module using the :ref:`roles ` option: .. literalinclude:: /code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud/config.yaml diff --git a/doc/how-to/app/app_roles.rst b/doc/how-to/app/app_roles.rst index 5e5574af88..96b6bdcbb4 100644 --- a/doc/how-to/app/app_roles.rst +++ b/doc/how-to/app/app_roles.rst @@ -4,12 +4,12 @@ Application roles ================= An application role is a Lua module that implements specific functions or logic. -You can enable or turn off a particular role for certain instances in a :ref:`configuration ` without restarting these instances. +You can turn on or off a particular role for certain instances in a :ref:`configuration ` without restarting these instances. A role is run when a configuration is loaded or reloaded. Roles can be divided into the following groups: -- Roles provided by Tarantool. +- Tarantool's built-in roles. For example, the ``config.storage`` role can be used to make a Tarantool replica set act as a :ref:`configuration storage `. - Roles provided by third-party Lua modules. For example, the `CRUD `__ module provides the ``roles.crud-storage`` and ``roles.crud-router`` roles that enable CRUD operations in a sharded cluster. @@ -94,7 +94,7 @@ Validating a role configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To validate a role's configuration, you need to define the :ref:`validate([cfg]) ` function. -The ``cfg`` argument allows you to get access to the provided :ref:`role's configuration ` and check its validity. +The ``cfg`` argument provides access to the :ref:`role's configuration ` and check its validity. In the example below, the ``validate()`` function is used to validate the ``greeting`` configuration value: @@ -277,14 +277,19 @@ Executing functions for dependent roles ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ For roles that :ref:`depend ` on each other, their ``validate()``, ``apply()``, and ``stop()`` functions are executed taking into account the dependencies. -Suppose, there are two independent roles (``role1``, ``role2``) and three roles that depend on each other as follows: +Suppose, there are three independent and two dependent roles: .. code-block:: none + role1 + role2 role3 └─── role4 └─── role5 +- ``role1``, ``role2``, and ``role5`` are independent roles. +- ``role3`` depends on ``role4``, ``role4`` depends on ``role5``. + The roles are enabled in a configuration as follows: .. code-block:: yaml