@@ -24,6 +24,8 @@ To enable the routing rules engine, find the following lines in
24
24
* Set ` rulesEngineEnabled ` to ` true ` , then ` rulesType ` as ` FILE ` or ` EXTERNAL ` .
25
25
* If you set ` rulesType: FILE ` , then set ` rulesConfigPath ` to the path to your
26
26
rules config file.
27
+ * The rules file will be re-read every minute by default. You may change this by setting
28
+ ` rulesRefreshPeriod: Duration ` , where duration is an airlift style Duration such as ` 30s ` .
27
29
* If you set ` rulesType: EXTERNAL ` , set ` rulesExternalConfiguration ` to the URL
28
30
of an external service for routing rules processing.
29
31
* ` rulesType ` is by default ` FILE ` unless specified.
@@ -92,11 +94,10 @@ return a result with the following criteria:
92
94
93
95
# ## Configure routing rules with a file
94
96
95
- To express and fire routing rules, we use the
96
- [easy-rules](https://github.com/j-easy/easy-rules) engine. These rules must be
97
- stored in a YAML file. Rules consist of a name, description, condition, and list
97
+ Rules consist of a name, description, condition, and list
98
98
of actions. If the condition of a particular rule evaluates to `true`, its
99
- actions are fired.
99
+ actions are fired. Rules are stored as a
100
+ [multi-document](https://www.yaml.info/learn/document.html) YAML file.
100
101
101
102
` ` ` yaml
102
103
---
@@ -113,20 +114,37 @@ actions:
113
114
- 'result.put("routingGroup", "etl-special")'
114
115
` ` `
115
116
116
- In the condition, you can access the methods of a
117
- [HttpServletRequest](https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html)
118
- object called `request`. Rules may also utilize
117
+ Three objects are available by default. They are
118
+ * `request`, the incoming request as an [HttpServletRequest](https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html)
119
+ * `state`, a `HashMap<String, Object>` that allows passing arbitrary state from one rule evaluation to the next
120
+ * `result`, a `HashMap<String, String>` that is used to return the result of rule evaluation to the engine
121
+
122
+ In addition to the default objects, rules may optionally utilize
119
123
[trinoRequestUser](#trinorequestuser) and
120
124
[trinoQueryProperties](#trinoqueryproperties)
121
- objects , which provide information about the user and query respectively.
125
+ , which provide information about the user and query respectively.
122
126
You must include an action of the form `result.put(\"routingGroup\", \"foo\")`
123
127
to trigger routing of a request that satisfies the condition to the specific
124
128
routing group. Without this action, the default adhoc group is used and the
125
129
whole routing rule is redundant.
126
130
127
131
The condition and actions are written in [MVEL](http://mvel.documentnode.com/),
128
- an expression language with Java-like syntax. In most cases, you can write
129
- conditions and actions in Java syntax and expect it to work. There are some
132
+ an expression language with Java-like syntax. Classes from `java.util`, data-type
133
+ classes from `java.lang` such as `Integer` and `String`, as well as `java.lang.Math`
134
+ and `java.lang.StrictMath` are available in rules. Rules may not use `java.lang.System`
135
+ and other classes that allow access the Java runtime and operating system.
136
+ In most cases, you can write
137
+ conditions and actions in Java syntax and expect it to work. One exception is
138
+ parametrized types. Exclude type parameters, for example to add a `HashSet` to the
139
+ `state` variable, use an action such as :
140
+ ` ` ` java
141
+ actions:
142
+ - |
143
+ state.put("triggeredRules",new HashSet())
144
+ ` ` `
145
+ This is equivalent to `new HashSet<Object>()`.
146
+
147
+ There are some
130
148
MVEL-specific operators. For example, instead of doing a null-check before
131
149
accessing the `String.contains` method like this :
132
150
@@ -296,8 +314,8 @@ actions:
296
314
` ` `
297
315
298
316
This can difficult to maintain with more rules. To have better control over the
299
- execution of rules, we can use rule priorities and composite rules . Overall,
300
- priorities, composite rules, and other constructs that MVEL support allows
317
+ execution of rules, we can use rule priorities. Overall,
318
+ priorities and other constructs that MVEL support allows
301
319
you to express your routing logic.
302
320
303
321
# ### Rule priority
@@ -328,99 +346,52 @@ that the first rule (priority 0) is fired before the second rule (priority 1).
328
346
Thus `routingGroup` is set to `etl` and then to `etl-special`, so the
329
347
` routingGroup` is always `etl-special` in the end.
330
348
331
- More specific rules must be set to a lesser priority so they are evaluated last
332
- to set a `routingGroup`. To further control the execution of rules, for example
333
- to have only one rule fire, you can use composite rules.
349
+ More specific rules must be set to a higher priority so they are evaluated last
350
+ to set a `routingGroup`.
334
351
335
- # #### Composite rules
352
+ # #### Passing State
336
353
337
- First, please refer to the [easy-rule composite rules documentation](https://github.com/j-easy/easy-rules/wiki/defining-rules#composite-rules).
338
-
339
- The preceding section covers how to control the order of rule execution using
340
- priorities. In addition, you can configure evaluation so that only the first
341
- rule matched fires (the highest priority one) and the rest is ignored. You can
342
- use `ActivationRuleGroup` to achieve this :
354
+ The `state` object may be used to pass information from one rule evaluation to
355
+ the next. This allows an author to avoid duplicating logic in multiple rules.
356
+ Priority should be used to ensure that `state` is updated before being used
357
+ in downstream rules. For example, the atomic rules may be re-implemented as
343
358
344
359
` ` ` yaml
345
360
---
346
- name: "airflow rule group"
347
- description: "routing rules for query from airflow"
348
- compositeRuleType: "ActivationRuleGroup"
349
- composingRules:
350
- - name: "airflow special"
351
- description: "if query from airflow with special label, route to etl-special group"
352
- priority: 0
353
- condition: 'request.getHeader("X-Trino-Source") == "airflow" && request.getHeader("X-Trino-Client-Tags") contains "label=special"'
354
- actions:
355
- - 'result.put("routingGroup", "etl-special")'
356
- - name: "airflow"
357
- description: "if query from airflow, route to etl group"
358
- priority: 1
359
- condition: 'request.getHeader("X-Trino-Source") == "airflow"'
360
- actions:
361
- - 'result.put("routingGroup", "etl")'
362
- ` ` `
363
-
364
- Note that the priorities have switched. The more specific rule has a higher
365
- priority, since it should fire first. A query coming from airflow with special
366
- label is matched to the "airflow special" rule first, since it's higher
367
- priority, and the second rule is ignored. A query coming from airflow with no
368
- labels does not match the first rule, and is then tested and matched to the
369
- second rule.
370
-
371
- You can also use `ConditionalRuleGroup` and `ActivationRuleGroup` to implement
372
- an if/else workflow. The following logic in pseudocode :
373
-
374
- ` ` ` text
375
- if source == "airflow":
376
- if clientTags["label"] == "foo":
377
- return "etl-foo"
378
- else if clientTags["label"] = "bar":
379
- return "etl-bar"
380
- else
381
- return "etl"
382
- ` ` `
383
-
384
- This logic can be implemented with the following rules :
361
+ name: "initialize state"
362
+ description: "Add a set to the state map to track rules that have evaluated to true"
363
+ priority: 0
364
+ condition: "true"
365
+ actions:
366
+ - |
367
+ state.put("triggeredRules",new HashSet())
368
+ # MVEL does not support type parameters! HashSet<String>() would result in an error.
369
+ ---
370
+ name: "airflow"
371
+ description: "if query from airflow, route to etl group"
372
+ priority: 1
373
+ condition: |
374
+ request.getHeader("X-Trino-Source") == "airflow"
375
+ actions:
376
+ - |
377
+ result.put("routingGroup", "etl")
378
+ - |
379
+ state.get("triggeredRules").add("airflow")
380
+ ---
381
+ name: "airflow special"
382
+ description: "if query from airflow with special label, route to etl-special group"
383
+ priority: 2
384
+ condition: |
385
+ state.get("triggeredRules").contains("airflow") && request.getHeader("X-Trino-Client-Tags") contains "label=special"
386
+ actions:
387
+ - |
388
+ result.put("routingGroup", "etl-special")
385
389
386
- ` ` ` yaml
387
- name: "airflow rule group"
388
- description: "routing rules for query from airflow"
389
- compositeRuleType: "ConditionalRuleGroup"
390
- composingRules:
391
- - name: "main condition"
392
- description: "source is airflow"
393
- priority: 0 # rule with the highest priority acts as main condition
394
- condition: 'request.getHeader("X-Trino-Source") == "airflow"'
395
- actions:
396
- - ""
397
- - name: "airflow subrules"
398
- compositeRuleType: "ActivationRuleGroup" # use ActivationRuleGroup to simulate if/else
399
- composingRules:
400
- - name: "label foo"
401
- description: "label client tag is foo"
402
- priority: 0
403
- condition: 'request.getHeader("X-Trino-Client-Tags") contains "label=foo"'
404
- actions:
405
- - 'result.put("routingGroup", "etl-foo")'
406
- - name: "label bar"
407
- description: "label client tag is bar"
408
- priority: 0
409
- condition: 'request.getHeader("X-Trino-Client-Tags") contains "label=bar"'
410
- actions:
411
- - 'result.put("routingGroup", "etl-bar")'
412
- - name: "airflow default"
413
- description: "airflow queries default to etl"
414
- condition: "true"
415
- actions:
416
- - 'result.put("routingGroup", "etl")'
417
390
` ` `
418
391
419
392
# #### If statements (MVEL Flow Control)
420
393
421
- In the preceding section you see how `ConditionalRuleGroup` and
422
- ` ActivationRuleGroup` are used to implement an `if/else` workflow. You can
423
- use MVEL support for `if` statements and other flow control. The following logic
394
+ You can use MVEL support for `if` statements and other flow control. The following logic
424
395
in pseudocode :
425
396
426
397
` ` ` text
0 commit comments