The bounds that dmn keeps on a model that comes from outside.
A model is untrusted input, and so are the models it imports. Passing a bound is an error value of the kind :limit
(Dmn.Error), never a crash. So is a limits: option that is none of nil, a t/0 or a keyword list of its fields (validate/1, DD-39). The defaults are generous for a model that a person wrote (the largest in the TCK is a few
hundred kB) and small for one made to hurt; a caller that reads models it trusts can raise them, and one that reads models of
anyone should lower them to what its models need.
Reading the XML (Dmn.XML)
| Field | Default | Bounds |
|---|---|---|
max_bytes | 5,000,000 | the size of one document |
max_depth | 100 | how deep the elements nest |
max_elements | 200,000 | the elements in one document |
max_attributes | 100 | the attributes of one element, and its namespace declarations |
max_name_length | 256 | the length of the name of an element or an attribute, and of a prefix |
max_uri_length | 1,024 | the length of a namespace URI; every element that is in it holds a copy of it |
xml_timeout | 5,000 | milliseconds that reading one document may take (Feel.Worker) |
max_memory | 1,000,000,000 | bytes that the process reading it may grow to |
The SAX parser takes time quadratic in the attributes of one element, and has no bound of its own. The count is checked when
the parser reports the element, which is after the work; xml_timeout and max_memory are what stop a document made to
waste it.
These document limits do not widen an enclosing guard: policy. Ordinary Dmn.load/2 and
Dmn.compile/2 default to a 128 MiB heap and a five-second deadline for the complete operation,
including its input/result transfers. The XML worker inherits tighter enclosing allowances.
Imports (Dmn.Resolver)
| Field | Default | Bounds |
|---|---|---|
max_units | 10,000 | input data, decisions, knowledge models and services in the whole graph |
max_models | 64 | the models of one compile, the root and all it imports |
max_import_depth | 16 | the imports in a chain |
max_import_reads | 256 | the documents that the resolver is asked for, whatever comes of them (an import that fails is a read too) |
max_import_bytes | 20,000,000 | the bytes of all the documents that the imports bring, the ones that cannot be read included |
import_timeout | 30,000 | milliseconds that reading all the imports may take; a document is read with what is left of it as its xml_timeout |
The checks of the decision tables (Dmn.Compiler.Checks)
| Field | Default | Bounds |
|---|---|---|
table_check_timeout | 1,000 | milliseconds that the questions about one table (overlap, never reached, complete) may take |
checks_timeout | 10,000 | milliseconds that the questions about all the tables of one compile may take |
The number of rules and of cells of a table that is asked about is limited (200 and 4,000), and the time is not bounded by that:
100 rules of ranges and not(…) with 4 inputs took 38 s to answer and 6 ms to compile without. The questions are asked in a
process that is ended at the deadline, and a table that passed it is said to be unchecked (:table_not_checked, an info),
which is not an answer to the questions.
The limits of one document, times the models, are more than a compile should spend (max_models times max_bytes is 320 MB of
text), and a root can hold tens of thousands of imports that all fail: so the whole graph of imports has a budget of its own. A
document is asked for once for each model and location (Dmn.Imports), so the same import that fails twice is not read twice.
The resolver itself is the caller's, and its time is only counted, not cut: guard: on Dmn.compile/2 is what ends it.
Summary
Functions
Returns the limits that Dmn functions take as an option: a t/0, or the fields to replace. Raises as new/1 does; validate/1 does not.
The default limits, with any field replaced.
The limits that a caller passed as an option (limits: of Dmn.load/2 and Dmn.compile/2), or why they are none: nil, a t/0
or a keyword list of its fields, with every value a positive integer. Nothing raises for what the caller passed (DD-39).
Types
@type t() :: %Dmn.Limits{ checks_timeout: pos_integer(), import_timeout: pos_integer(), max_attributes: pos_integer(), max_bytes: pos_integer(), max_depth: pos_integer(), max_elements: pos_integer(), max_import_bytes: pos_integer(), max_import_depth: pos_integer(), max_import_reads: pos_integer(), max_memory: pos_integer(), max_models: pos_integer(), max_name_length: pos_integer(), max_units: pos_integer(), max_uri_length: pos_integer(), table_check_timeout: pos_integer(), xml_timeout: pos_integer() }
Functions
Returns the limits that Dmn functions take as an option: a t/0, or the fields to replace. Raises as new/1 does; validate/1 does not.
The default limits, with any field replaced.
iex> Dmn.Limits.new(max_depth: 10).max_depth
10Raises ArgumentError for a field that is not one of the limits, or a value that is not a positive integer.
The limits that a caller passed as an option (limits: of Dmn.load/2 and Dmn.compile/2), or why they are none: nil, a t/0
or a keyword list of its fields, with every value a positive integer. Nothing raises for what the caller passed (DD-39).
iex> {:ok, %Dmn.Limits{max_depth: 10}} = Dmn.Limits.validate(max_depth: 10)
iex> {:error, _message} = Dmn.Limits.validate(max_depth: -1)
iex> {:error, _message} = Dmn.Limits.validate(:x)