Add table filtering to avrorouter
The filtering is implemented with PCRE2 regular expressions and as such is not the most user-friendly interface.
This commit is contained in:
@ -66,6 +66,21 @@ _deflate_. These are the mandatory compression algorithms required by the
|
|||||||
Avro specification. For more information about the compression types,
|
Avro specification. For more information about the compression types,
|
||||||
refer to the [Avro specification](https://avro.apache.org/docs/current/spec.html#Required+Codecs).
|
refer to the [Avro specification](https://avro.apache.org/docs/current/spec.html#Required+Codecs).
|
||||||
|
|
||||||
|
### `match`
|
||||||
|
|
||||||
|
Only process events for tables that match this PCRE2 regular expression. See
|
||||||
|
[Regular Expressions](../Getting-Started/Configuration-Guide.md#regular-expressions)
|
||||||
|
for more information about regular expressions.
|
||||||
|
|
||||||
|
This parameter was added in MaxScale 2.2.14.
|
||||||
|
|
||||||
|
### `exclude`
|
||||||
|
|
||||||
|
Ignore events for tables that match this PCRE2 regular expression. This can be
|
||||||
|
combined with the `match` parameter to implement table event filtering.
|
||||||
|
|
||||||
|
This parameter was added in MaxScale 2.2.14.
|
||||||
|
|
||||||
**Note:** Since the 2.1 version of MaxScale, all of the router options can also
|
**Note:** Since the 2.1 version of MaxScale, all of the router options can also
|
||||||
be defined as parameters.
|
be defined as parameters.
|
||||||
|
|
||||||
|
|||||||
@ -44,6 +44,7 @@
|
|||||||
#include <maxscale/service.h>
|
#include <maxscale/service.h>
|
||||||
#include <maxscale/spinlock.h>
|
#include <maxscale/spinlock.h>
|
||||||
#include <maxscale/utils.h>
|
#include <maxscale/utils.h>
|
||||||
|
#include <maxscale/pcre2.h>
|
||||||
#include <binlog_common.h>
|
#include <binlog_common.h>
|
||||||
|
|
||||||
#ifndef BINLOG_NAMEFMT
|
#ifndef BINLOG_NAMEFMT
|
||||||
@ -272,6 +273,8 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
|||||||
{"start_index", MXS_MODULE_PARAM_COUNT, "1"},
|
{"start_index", MXS_MODULE_PARAM_COUNT, "1"},
|
||||||
{"block_size", MXS_MODULE_PARAM_SIZE, "0"},
|
{"block_size", MXS_MODULE_PARAM_SIZE, "0"},
|
||||||
{"codec", MXS_MODULE_PARAM_ENUM, "null", MXS_MODULE_OPT_ENUM_UNIQUE, codec_values},
|
{"codec", MXS_MODULE_PARAM_ENUM, "null", MXS_MODULE_OPT_ENUM_UNIQUE, codec_values},
|
||||||
|
{"match", MXS_MODULE_PARAM_REGEX},
|
||||||
|
{"exclude", MXS_MODULE_PARAM_REGEX},
|
||||||
{MXS_END_MODULE_PARAMS}
|
{MXS_END_MODULE_PARAMS}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -481,6 +484,18 @@ createInstance(SERVICE *service, char **options)
|
|||||||
AVRO_INSTANCE *inst;
|
AVRO_INSTANCE *inst;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
MXS_CONFIG_PARAMETER *params = service->svc_config_param;
|
||||||
|
pcre2_code* match = config_get_compiled_regex(params, "match", 0, NULL);
|
||||||
|
pcre2_code* exclude = config_get_compiled_regex(params, "exclude", 0, NULL);
|
||||||
|
pcre2_match_data* md_match = NULL;
|
||||||
|
pcre2_match_data* md_exclude = NULL;
|
||||||
|
|
||||||
|
if ((match && (md_match = pcre2_match_data_create_from_pattern(match, NULL)) == NULL) ||
|
||||||
|
(exclude && (md_exclude = pcre2_match_data_create_from_pattern(exclude, NULL)) == NULL))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if ((inst = MXS_CALLOC(1, sizeof(AVRO_INSTANCE))) == NULL)
|
if ((inst = MXS_CALLOC(1, sizeof(AVRO_INSTANCE))) == NULL)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -502,8 +517,6 @@ createInstance(SERVICE *service, char **options)
|
|||||||
inst->trx_count = 0;
|
inst->trx_count = 0;
|
||||||
inst->binlogdir = NULL;
|
inst->binlogdir = NULL;
|
||||||
|
|
||||||
MXS_CONFIG_PARAMETER *params = service->svc_config_param;
|
|
||||||
|
|
||||||
inst->avrodir = MXS_STRDUP_A(config_get_string(params, "avrodir"));
|
inst->avrodir = MXS_STRDUP_A(config_get_string(params, "avrodir"));
|
||||||
inst->fileroot = MXS_STRDUP_A(config_get_string(params, "filestem"));
|
inst->fileroot = MXS_STRDUP_A(config_get_string(params, "filestem"));
|
||||||
inst->row_target = config_get_integer(params, "group_rows");
|
inst->row_target = config_get_integer(params, "group_rows");
|
||||||
@ -511,6 +524,10 @@ createInstance(SERVICE *service, char **options)
|
|||||||
inst->codec = config_get_enum(params, "codec", codec_values);
|
inst->codec = config_get_enum(params, "codec", codec_values);
|
||||||
int first_file = config_get_integer(params, "start_index");
|
int first_file = config_get_integer(params, "start_index");
|
||||||
inst->block_size = config_get_size(params, "block_size");
|
inst->block_size = config_get_size(params, "block_size");
|
||||||
|
inst->match = match;
|
||||||
|
inst->exclude = exclude;
|
||||||
|
inst->md_match = md_match;
|
||||||
|
inst->md_exclude = md_exclude;
|
||||||
|
|
||||||
MXS_CONFIG_PARAMETER *param = config_get_param(params, "source");
|
MXS_CONFIG_PARAMETER *param = config_get_param(params, "source");
|
||||||
inst->gtid.domain = 0;
|
inst->gtid.domain = 0;
|
||||||
@ -1284,3 +1301,20 @@ static bool ensure_dir_ok(const char* path, int mode)
|
|||||||
|
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool table_matches(AVRO_INSTANCE* inst, const char* ident)
|
||||||
|
{
|
||||||
|
bool rval = false;
|
||||||
|
|
||||||
|
if (!inst->match || pcre2_match(inst->match, (PCRE2_SPTR)ident, PCRE2_ZERO_TERMINATED,
|
||||||
|
0, 0, inst->md_match, NULL) > 0)
|
||||||
|
{
|
||||||
|
if (!inst->exclude || pcre2_match(inst->exclude, (PCRE2_SPTR)ident, PCRE2_ZERO_TERMINATED,
|
||||||
|
0, 0, inst->md_exclude, NULL) == PCRE2_ERROR_NOMATCH)
|
||||||
|
{
|
||||||
|
rval = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|||||||
@ -100,6 +100,12 @@ bool handle_table_map_event(AVRO_INSTANCE *router, REP_HEADER *hdr, uint8_t *ptr
|
|||||||
int ev_len = router->event_type_hdr_lens[hdr->event_type];
|
int ev_len = router->event_type_hdr_lens[hdr->event_type];
|
||||||
|
|
||||||
read_table_info(ptr, ev_len, &id, table_ident, sizeof(table_ident));
|
read_table_info(ptr, ev_len, &id, table_ident, sizeof(table_ident));
|
||||||
|
|
||||||
|
if (!table_matches(router, table_ident))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
TABLE_CREATE* create = hashtable_fetch(router->created_tables, table_ident);
|
TABLE_CREATE* create = hashtable_fetch(router->created_tables, table_ident);
|
||||||
|
|
||||||
if (create)
|
if (create)
|
||||||
@ -292,6 +298,12 @@ bool handle_row_event(AVRO_INSTANCE *router, REP_HEADER *hdr, uint8_t *ptr)
|
|||||||
{
|
{
|
||||||
char table_ident[MYSQL_TABLE_MAXLEN + MYSQL_DATABASE_MAXLEN + 2];
|
char table_ident[MYSQL_TABLE_MAXLEN + MYSQL_DATABASE_MAXLEN + 2];
|
||||||
snprintf(table_ident, sizeof(table_ident), "%s.%s", map->database, map->table);
|
snprintf(table_ident, sizeof(table_ident), "%s.%s", map->database, map->table);
|
||||||
|
|
||||||
|
if (!table_matches(router, table_ident))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
AVRO_TABLE* table = hashtable_fetch(router->open_tables, table_ident);
|
AVRO_TABLE* table = hashtable_fetch(router->open_tables, table_ident);
|
||||||
TABLE_CREATE* create = map->table_create;
|
TABLE_CREATE* create = map->table_create;
|
||||||
ss_dassert(hashtable_fetch(router->created_tables, table_ident) == create);
|
ss_dassert(hashtable_fetch(router->created_tables, table_ident) == create);
|
||||||
@ -369,7 +381,7 @@ bool handle_row_event(AVRO_INSTANCE *router, REP_HEADER *hdr, uint8_t *ptr)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MXS_ERROR("Row event for unknown table mapped to ID %lu. Data will not "
|
MXS_INFO("Row event for unknown table mapped to ID %lu. Data will not "
|
||||||
"be processed.", table_id);
|
"be processed.", table_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -303,6 +303,12 @@ typedef struct avro_instance
|
|||||||
* a flush of all tables */
|
* a flush of all tables */
|
||||||
uint64_t block_size; /**< Avro datablock size */
|
uint64_t block_size; /**< Avro datablock size */
|
||||||
enum mxs_avro_codec_type codec; /**< Avro codec type, defaults to `null` */
|
enum mxs_avro_codec_type codec; /**< Avro codec type, defaults to `null` */
|
||||||
|
|
||||||
|
/** Match and exclude patterns for tables */
|
||||||
|
pcre2_code* match;
|
||||||
|
pcre2_code* exclude;
|
||||||
|
pcre2_match_data* md_match;
|
||||||
|
pcre2_match_data* md_exclude;
|
||||||
struct avro_instance *next;
|
struct avro_instance *next;
|
||||||
} AVRO_INSTANCE;
|
} AVRO_INSTANCE;
|
||||||
|
|
||||||
@ -355,6 +361,8 @@ extern void avro_flush_all_tables(AVRO_INSTANCE *router, enum avrorouter_file_op
|
|||||||
#define AVRO_CS_BUSY 0x0001
|
#define AVRO_CS_BUSY 0x0001
|
||||||
#define AVRO_WAIT_DATA 0x0002
|
#define AVRO_WAIT_DATA 0x0002
|
||||||
|
|
||||||
|
bool table_matches(AVRO_INSTANCE* inst, const char* ident);
|
||||||
|
|
||||||
MXS_END_DECLS
|
MXS_END_DECLS
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user