Query hint to influence Consistent Critical Read Filter
MXS-1067. The CCRFilter will now look at the hints in a write-type query. The parameter-value combinations "ccr=match" and "ccr=ignore" are recognized and will override any regex match and ignore settings. For the hints to work, the HintFilter needs to be present in the filter chain before the CCRFilter. Also ran astyle and updated documentation.
This commit is contained in:
parent
2778056212
commit
bff5e96b1a
@ -12,6 +12,27 @@ a routing hint to all following statements. This routing hint guides the routing
|
||||
module to route the statement to the master server where data is guaranteed to
|
||||
be in an up-to-date state.
|
||||
|
||||
The triggering of the filter can be limited further by adding MaxScale supported
|
||||
comments to queries and/or by using regular expressions. The query comments take
|
||||
precedence: if a comment is found it is obayed even if a regular expression
|
||||
parameter might give a different result. Even a comment cannot cause a
|
||||
SELECT-query to trigger the filter. Such a comment is considered an error and
|
||||
ignored.
|
||||
|
||||
The comments must follow the [MaxScale hint syntax](../Reference/Hint-Syntax.md)
|
||||
and the *HintFilter* needs to be in the filter chain before the CCR-filter. If a
|
||||
query has a MaxScale supported comment line which defines the parameter `ccr`,
|
||||
that comment is caught by the CCR-filter. Parameter values `match` and `ignore`
|
||||
are supported, causing the filter to trigger (`match`) or not trigger (`ignore`)
|
||||
on receiving the write query. For example, the query
|
||||
```
|
||||
INSERT INTO departments VALUES ('d1234', 'NewDepartment'); -- maxscale ccr=ignore
|
||||
```
|
||||
would normally cause the filter to trigger, but does not because of the
|
||||
comment. The `match`-comment typically has no effect, since write queries by
|
||||
default trigger the filter anyway. It can be used to override an ignore-type
|
||||
regular expression that would othewise prevent triggering.
|
||||
|
||||
## Filter Options
|
||||
|
||||
The CCR filter accepts the following options.
|
||||
@ -62,7 +83,9 @@ _count_.
|
||||
|
||||
An optional parameter that can be used to control which statements trigger the
|
||||
statement re-routing. The parameter value is a regular expression that is used
|
||||
to match against the SQL text. Only non-SELECT statements are inspected.
|
||||
to match against the SQL text. Only non-SELECT statements are inspected. If this
|
||||
parameter is defined, *only* matching SQL-queries will trigger the filter
|
||||
(assuming no ccr hint comments in the query).
|
||||
|
||||
```
|
||||
match=.*INSERT.*
|
||||
|
@ -103,6 +103,15 @@ static const MXS_ENUM_VALUE option_values[] =
|
||||
{NULL}
|
||||
};
|
||||
|
||||
typedef enum ccr_hint_value_t
|
||||
{
|
||||
CCR_HINT_NONE,
|
||||
CCR_HINT_MATCH,
|
||||
CCR_HINT_IGNORE
|
||||
} CCR_HINT_VALUE;
|
||||
|
||||
static CCR_HINT_VALUE search_ccr_hint(GWBUF* buffer);
|
||||
|
||||
/**
|
||||
* The module entry point routine. It is this routine that
|
||||
* must populate the structure that is referred to as the
|
||||
@ -148,11 +157,11 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{"match", MXS_MODULE_PARAM_STRING},
|
||||
{"ignore", MXS_MODULE_PARAM_STRING},
|
||||
{
|
||||
"options",
|
||||
MXS_MODULE_PARAM_ENUM,
|
||||
"ignorecase",
|
||||
MXS_MODULE_OPT_NONE,
|
||||
option_values
|
||||
"options",
|
||||
MXS_MODULE_PARAM_ENUM,
|
||||
"ignorecase",
|
||||
MXS_MODULE_OPT_NONE,
|
||||
option_values
|
||||
},
|
||||
{MXS_END_MODULE_PARAMS}
|
||||
}
|
||||
@ -299,28 +308,49 @@ routeQuery(MXS_FILTER *instance, MXS_FILTER_SESSION *session, GWBUF *queue)
|
||||
{
|
||||
if ((sql = modutil_get_SQL(queue)) != NULL)
|
||||
{
|
||||
if (my_instance->nomatch == NULL ||
|
||||
(my_instance->nomatch && regexec(&my_instance->nore, sql, 0, NULL, 0) != 0))
|
||||
bool trigger_ccr = true;
|
||||
bool decided = false; // Set by hints to take precedence.
|
||||
CCR_HINT_VALUE ccr_hint_val = search_ccr_hint(queue);
|
||||
if (ccr_hint_val == CCR_HINT_IGNORE)
|
||||
{
|
||||
if (my_instance->match == NULL ||
|
||||
(my_instance->match && regexec(&my_instance->re, sql, 0, NULL, 0) == 0))
|
||||
trigger_ccr = false;
|
||||
decided = true;
|
||||
}
|
||||
else if (ccr_hint_val == CCR_HINT_MATCH)
|
||||
{
|
||||
decided = true;
|
||||
}
|
||||
if (!decided)
|
||||
{
|
||||
if (my_instance->nomatch &&
|
||||
regexec(&my_instance->nore, sql, 0, NULL, 0) == 0)
|
||||
{
|
||||
if (my_instance->count)
|
||||
{
|
||||
my_session->hints_left = my_instance->count;
|
||||
MXS_INFO("Write operation detected, next %d queries routed to master", my_instance->count);
|
||||
}
|
||||
|
||||
if (my_instance->time)
|
||||
{
|
||||
my_session->last_modification = now;
|
||||
MXS_INFO("Write operation detected, queries routed to master for %d seconds", my_instance->time);
|
||||
}
|
||||
|
||||
my_instance->stats.n_modified++;
|
||||
// Nomatch was present and sql matched it.
|
||||
trigger_ccr = false;
|
||||
}
|
||||
else if (my_instance->match &&
|
||||
(regexec(&my_instance->re, sql, 0, NULL, 0) != 0))
|
||||
{
|
||||
// Match was present but sql did *not* match it.
|
||||
trigger_ccr = false;
|
||||
}
|
||||
}
|
||||
if (trigger_ccr)
|
||||
{
|
||||
if (my_instance->count)
|
||||
{
|
||||
my_session->hints_left = my_instance->count;
|
||||
MXS_INFO("Write operation detected, next %d queries routed to master", my_instance->count);
|
||||
}
|
||||
|
||||
if (my_instance->time)
|
||||
{
|
||||
my_session->last_modification = now;
|
||||
MXS_INFO("Write operation detected, queries routed to master for %d seconds", my_instance->time);
|
||||
}
|
||||
|
||||
my_instance->stats.n_modified++;
|
||||
}
|
||||
MXS_FREE(sql);
|
||||
}
|
||||
}
|
||||
@ -429,3 +459,52 @@ static uint64_t getCapabilities(MXS_FILTER* instance)
|
||||
{
|
||||
return RCAP_TYPE_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the first CCR filter hint. The hint is removed from the buffer and the
|
||||
* contents returned.
|
||||
*
|
||||
* @param buffer Input buffer
|
||||
* @return The found ccr hint value
|
||||
*/
|
||||
static CCR_HINT_VALUE search_ccr_hint(GWBUF* buffer)
|
||||
{
|
||||
const char CCR[] = "ccr";
|
||||
CCR_HINT_VALUE rval = CCR_HINT_NONE;
|
||||
bool found_ccr = false;
|
||||
HINT** prev_ptr = &buffer->hint;
|
||||
HINT* hint = buffer->hint;
|
||||
|
||||
while (hint && !found_ccr)
|
||||
{
|
||||
if (hint->type == HINT_PARAMETER && strcasecmp(hint->data, CCR) == 0)
|
||||
{
|
||||
found_ccr = true;
|
||||
if (strcasecmp(hint->value, "match") == 0)
|
||||
{
|
||||
rval = CCR_HINT_MATCH;
|
||||
}
|
||||
else if (strcasecmp(hint->value, "ignore") == 0)
|
||||
{
|
||||
rval = CCR_HINT_IGNORE;
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Unknown value for hint parameter %s: '%s'.",
|
||||
CCR, (char*)hint->value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
prev_ptr = &hint->next;
|
||||
hint = hint->next;
|
||||
}
|
||||
}
|
||||
// Remove the ccr-hint from the hint chain. Otherwise rwsplit will complain.
|
||||
if (found_ccr)
|
||||
{
|
||||
*prev_ptr = hint->next;
|
||||
hint_free(hint);
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user