diff --git a/_head.html b/_head.html new file mode 100644 index 00000000..3be00201 --- /dev/null +++ b/_head.html @@ -0,0 +1,2 @@ + + diff --git a/chunks/khepri.chunk b/chunks/khepri.chunk new file mode 100644 index 00000000..14248981 Binary files /dev/null and b/chunks/khepri.chunk differ diff --git a/chunks/khepri_adv.chunk b/chunks/khepri_adv.chunk new file mode 100644 index 00000000..fc3a300f Binary files /dev/null and b/chunks/khepri_adv.chunk differ diff --git a/chunks/khepri_app.chunk b/chunks/khepri_app.chunk new file mode 100644 index 00000000..b02a0f97 Binary files /dev/null and b/chunks/khepri_app.chunk differ diff --git a/chunks/khepri_cluster.chunk b/chunks/khepri_cluster.chunk new file mode 100644 index 00000000..1a059017 Binary files /dev/null and b/chunks/khepri_cluster.chunk differ diff --git a/chunks/khepri_condition.chunk b/chunks/khepri_condition.chunk new file mode 100644 index 00000000..01b2a1d5 Binary files /dev/null and b/chunks/khepri_condition.chunk differ diff --git a/chunks/khepri_event_handler.chunk b/chunks/khepri_event_handler.chunk new file mode 100644 index 00000000..f3257b2d Binary files /dev/null and b/chunks/khepri_event_handler.chunk differ diff --git a/chunks/khepri_evf.chunk b/chunks/khepri_evf.chunk new file mode 100644 index 00000000..86267918 Binary files /dev/null and b/chunks/khepri_evf.chunk differ diff --git a/chunks/khepri_export_erlang.chunk b/chunks/khepri_export_erlang.chunk new file mode 100644 index 00000000..f4a63a2e Binary files /dev/null and b/chunks/khepri_export_erlang.chunk differ diff --git a/chunks/khepri_import_export.chunk b/chunks/khepri_import_export.chunk new file mode 100644 index 00000000..980208ff Binary files /dev/null and b/chunks/khepri_import_export.chunk differ diff --git a/chunks/khepri_machine.chunk b/chunks/khepri_machine.chunk new file mode 100644 index 00000000..25213c03 Binary files /dev/null and b/chunks/khepri_machine.chunk differ diff --git a/chunks/khepri_machine_v0.chunk b/chunks/khepri_machine_v0.chunk new file mode 100644 index 00000000..9a486e63 Binary files /dev/null and b/chunks/khepri_machine_v0.chunk differ diff --git a/chunks/khepri_path.chunk b/chunks/khepri_path.chunk new file mode 100644 index 00000000..fd534e42 Binary files /dev/null and b/chunks/khepri_path.chunk differ diff --git a/chunks/khepri_pattern_tree.chunk b/chunks/khepri_pattern_tree.chunk new file mode 100644 index 00000000..d64ea1a4 Binary files /dev/null and b/chunks/khepri_pattern_tree.chunk differ diff --git a/chunks/khepri_payload.chunk b/chunks/khepri_payload.chunk new file mode 100644 index 00000000..3b4c44d5 Binary files /dev/null and b/chunks/khepri_payload.chunk differ diff --git a/chunks/khepri_prefix_tree.chunk b/chunks/khepri_prefix_tree.chunk new file mode 100644 index 00000000..2a148b80 Binary files /dev/null and b/chunks/khepri_prefix_tree.chunk differ diff --git a/chunks/khepri_projection.chunk b/chunks/khepri_projection.chunk new file mode 100644 index 00000000..7a8f8494 Binary files /dev/null and b/chunks/khepri_projection.chunk differ diff --git a/chunks/khepri_sproc.chunk b/chunks/khepri_sproc.chunk new file mode 100644 index 00000000..6169a06b Binary files /dev/null and b/chunks/khepri_sproc.chunk differ diff --git a/chunks/khepri_sup.chunk b/chunks/khepri_sup.chunk new file mode 100644 index 00000000..6edbef92 Binary files /dev/null and b/chunks/khepri_sup.chunk differ diff --git a/chunks/khepri_tree.chunk b/chunks/khepri_tree.chunk new file mode 100644 index 00000000..6458c6b5 Binary files /dev/null and b/chunks/khepri_tree.chunk differ diff --git a/chunks/khepri_tx.chunk b/chunks/khepri_tx.chunk new file mode 100644 index 00000000..797118fc Binary files /dev/null and b/chunks/khepri_tx.chunk differ diff --git a/chunks/khepri_tx_adv.chunk b/chunks/khepri_tx_adv.chunk new file mode 100644 index 00000000..88f0d73e Binary files /dev/null and b/chunks/khepri_tx_adv.chunk differ diff --git a/chunks/khepri_utils.chunk b/chunks/khepri_utils.chunk new file mode 100644 index 00000000..c1ca6f4b Binary files /dev/null and b/chunks/khepri_utils.chunk differ diff --git a/edoc-extensions.css b/edoc-extensions.css new file mode 100644 index 00000000..c120569e --- /dev/null +++ b/edoc-extensions.css @@ -0,0 +1,66 @@ +@import url("github-markdown.css"); +@import url("prism.css"); +@import url("stylesheet.css"); + +body { + box-sizing: border-box; + min-width: 200px; + max-width: 980px; + margin: 0 auto; + padding: 45px; +} +@media (max-width: 767px) { + body { + padding: 15px; + } +} +/* Don't apply the table style to the top-level navigation bar. */ +.navbar table { + display: table; + width: 100%; +} +.navbar table tr, +.navbar table th, +.navbar table td { + border: 0; +} +/* Keep the same font side inside code blocks than everywhere else. */ +.markdown-body pre code, +code[class*="language-"] { + font-size: 85%; +} +/* Force the color for link on code blocks used for @see tags. */ +.markdown-body a code, +.markdown-body a code span.token { + color: var(--color-accent-fg); +} +/* Copy the style of code blocks. */ +.markdown-body .spec { + background: #f5f2f0; + padding: 1em; + margin: .5em 0; + overflow: auto; + border-radius: 6px; +} +/* Improve margins inside function spec blocks so that: + - empty paragraphs don't add useless margins + - the final top and bottom margins are equal */ +.markdown-body .spec p, +.markdown-body .spec ul { + margin-top: 16px; + margin-bottom: 0; +} +.markdown-body .spec p:first-child, +.markdown-body .spec p:empty { + margin-top: 0; +} +/* Put the function prototype in bold characters. */ +.markdown-body .spec > p > code:first-child { + font-weight: bold; +} +/* Add some margin below the module short description between the table + of contents in the Description section. This text isn't in a +

. */ +.index + p { + margin-top: 16px; +} diff --git a/edoc-info b/edoc-info new file mode 100644 index 00000000..8a04e133 --- /dev/null +++ b/edoc-info @@ -0,0 +1,6 @@ +%% encoding: UTF-8 +{application,khepri}. +{modules,[khepri,khepri_adv,khepri_cluster,khepri_condition,khepri_evf, + khepri_export_erlang,khepri_import_export,khepri_machine, + khepri_path,khepri_payload,khepri_prefix_tree,khepri_projection, + khepri_tx,khepri_tx_adv]}. diff --git a/erlang.png b/erlang.png new file mode 100644 index 00000000..987a618e Binary files /dev/null and b/erlang.png differ diff --git a/github-markdown.css b/github-markdown.css new file mode 100644 index 00000000..b7afaa41 --- /dev/null +++ b/github-markdown.css @@ -0,0 +1,1221 @@ +.markdown-body { + --base-size-4: 0.25rem; + --base-size-8: 0.5rem; + --base-size-16: 1rem; + --base-size-24: 1.5rem; + --base-size-40: 2.5rem; + --base-text-weight-normal: 400; + --base-text-weight-medium: 500; + --base-text-weight-semibold: 600; + --fontStack-monospace: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace; + --fgColor-accent: Highlight; +} +@media (prefers-color-scheme: dark) { + .markdown-body, [data-theme="dark"] { + /* dark */ + color-scheme: dark; + --focus-outlineColor: #1f6feb; + --fgColor-default: #f0f6fc; + --fgColor-muted: #9198a1; + --fgColor-accent: #4493f8; + --fgColor-success: #3fb950; + --fgColor-attention: #d29922; + --fgColor-danger: #f85149; + --fgColor-done: #ab7df8; + --bgColor-default: #0d1117; + --bgColor-muted: #151b23; + --bgColor-neutral-muted: #656c7633; + --bgColor-attention-muted: #bb800926; + --borderColor-default: #3d444d; + --borderColor-muted: #3d444db3; + --borderColor-neutral-muted: #3d444db3; + --borderColor-accent-emphasis: #1f6feb; + --borderColor-success-emphasis: #238636; + --borderColor-attention-emphasis: #9e6a03; + --borderColor-danger-emphasis: #da3633; + --borderColor-done-emphasis: #8957e5; + --color-prettylights-syntax-comment: #9198a1; + --color-prettylights-syntax-constant: #79c0ff; + --color-prettylights-syntax-constant-other-reference-link: #a5d6ff; + --color-prettylights-syntax-entity: #d2a8ff; + --color-prettylights-syntax-storage-modifier-import: #f0f6fc; + --color-prettylights-syntax-entity-tag: #7ee787; + --color-prettylights-syntax-keyword: #ff7b72; + --color-prettylights-syntax-string: #a5d6ff; + --color-prettylights-syntax-variable: #ffa657; + --color-prettylights-syntax-brackethighlighter-unmatched: #f85149; + --color-prettylights-syntax-brackethighlighter-angle: #9198a1; + --color-prettylights-syntax-invalid-illegal-text: #f0f6fc; + --color-prettylights-syntax-invalid-illegal-bg: #8e1519; + --color-prettylights-syntax-carriage-return-text: #f0f6fc; + --color-prettylights-syntax-carriage-return-bg: #b62324; + --color-prettylights-syntax-string-regexp: #7ee787; + --color-prettylights-syntax-markup-list: #f2cc60; + --color-prettylights-syntax-markup-heading: #1f6feb; + --color-prettylights-syntax-markup-italic: #f0f6fc; + --color-prettylights-syntax-markup-bold: #f0f6fc; + --color-prettylights-syntax-markup-deleted-text: #ffdcd7; + --color-prettylights-syntax-markup-deleted-bg: #67060c; + --color-prettylights-syntax-markup-inserted-text: #aff5b4; + --color-prettylights-syntax-markup-inserted-bg: #033a16; + --color-prettylights-syntax-markup-changed-text: #ffdfb6; + --color-prettylights-syntax-markup-changed-bg: #5a1e02; + --color-prettylights-syntax-markup-ignored-text: #f0f6fc; + --color-prettylights-syntax-markup-ignored-bg: #1158c7; + --color-prettylights-syntax-meta-diff-range: #d2a8ff; + --color-prettylights-syntax-sublimelinter-gutter-mark: #3d444d; + } +} +@media (prefers-color-scheme: light) { + .markdown-body, [data-theme="light"] { + /* light */ + color-scheme: light; + --focus-outlineColor: #0969da; + --fgColor-default: #1f2328; + --fgColor-muted: #59636e; + --fgColor-accent: #0969da; + --fgColor-success: #1a7f37; + --fgColor-attention: #9a6700; + --fgColor-danger: #d1242f; + --fgColor-done: #8250df; + --bgColor-default: #ffffff; + --bgColor-muted: #f6f8fa; + --bgColor-neutral-muted: #818b981f; + --bgColor-attention-muted: #fff8c5; + --borderColor-default: #d1d9e0; + --borderColor-muted: #d1d9e0b3; + --borderColor-neutral-muted: #d1d9e0b3; + --borderColor-accent-emphasis: #0969da; + --borderColor-success-emphasis: #1a7f37; + --borderColor-attention-emphasis: #9a6700; + --borderColor-danger-emphasis: #cf222e; + --borderColor-done-emphasis: #8250df; + --color-prettylights-syntax-comment: #59636e; + --color-prettylights-syntax-constant: #0550ae; + --color-prettylights-syntax-constant-other-reference-link: #0a3069; + --color-prettylights-syntax-entity: #6639ba; + --color-prettylights-syntax-storage-modifier-import: #1f2328; + --color-prettylights-syntax-entity-tag: #0550ae; + --color-prettylights-syntax-keyword: #cf222e; + --color-prettylights-syntax-string: #0a3069; + --color-prettylights-syntax-variable: #953800; + --color-prettylights-syntax-brackethighlighter-unmatched: #82071e; + --color-prettylights-syntax-brackethighlighter-angle: #59636e; + --color-prettylights-syntax-invalid-illegal-text: #f6f8fa; + --color-prettylights-syntax-invalid-illegal-bg: #82071e; + --color-prettylights-syntax-carriage-return-text: #f6f8fa; + --color-prettylights-syntax-carriage-return-bg: #cf222e; + --color-prettylights-syntax-string-regexp: #116329; + --color-prettylights-syntax-markup-list: #3b2300; + --color-prettylights-syntax-markup-heading: #0550ae; + --color-prettylights-syntax-markup-italic: #1f2328; + --color-prettylights-syntax-markup-bold: #1f2328; + --color-prettylights-syntax-markup-deleted-text: #82071e; + --color-prettylights-syntax-markup-deleted-bg: #ffebe9; + --color-prettylights-syntax-markup-inserted-text: #116329; + --color-prettylights-syntax-markup-inserted-bg: #dafbe1; + --color-prettylights-syntax-markup-changed-text: #953800; + --color-prettylights-syntax-markup-changed-bg: #ffd8b5; + --color-prettylights-syntax-markup-ignored-text: #d1d9e0; + --color-prettylights-syntax-markup-ignored-bg: #0550ae; + --color-prettylights-syntax-meta-diff-range: #8250df; + --color-prettylights-syntax-sublimelinter-gutter-mark: #818b98; + } +} + +.markdown-body { + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + margin: 0; + color: var(--fgColor-default); + background-color: var(--bgColor-default); + font-family: -apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"; + font-size: 16px; + line-height: 1.5; + word-wrap: break-word; + scroll-behavior: auto !important; +} + +.markdown-body .octicon { + display: inline-block; + fill: currentColor; + vertical-align: text-bottom; +} + +.markdown-body h1:hover .anchor .octicon-link:before, +.markdown-body h2:hover .anchor .octicon-link:before, +.markdown-body h3:hover .anchor .octicon-link:before, +.markdown-body h4:hover .anchor .octicon-link:before, +.markdown-body h5:hover .anchor .octicon-link:before, +.markdown-body h6:hover .anchor .octicon-link:before { + width: 16px; + height: 16px; + content: ' '; + display: inline-block; + background-color: currentColor; + -webkit-mask-image: url("data:image/svg+xml,"); + mask-image: url("data:image/svg+xml,"); +} + +.markdown-body details, +.markdown-body figcaption, +.markdown-body figure { + display: block; +} + +.markdown-body summary { + display: list-item; +} + +.markdown-body [hidden] { + display: none !important; +} + +.markdown-body a { + background-color: transparent; + color: var(--fgColor-accent); + text-decoration: none; +} + +.markdown-body abbr[title] { + border-bottom: none; + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +.markdown-body b, +.markdown-body strong { + font-weight: var(--base-text-weight-semibold, 600); +} + +.markdown-body dfn { + font-style: italic; +} + +.markdown-body h1 { + margin: .67em 0; + font-weight: var(--base-text-weight-semibold, 600); + padding-bottom: .3em; + font-size: 2em; + border-bottom: 1px solid var(--borderColor-muted); +} + +.markdown-body mark { + background-color: var(--bgColor-attention-muted); + color: var(--fgColor-default); +} + +.markdown-body small { + font-size: 90%; +} + +.markdown-body sub, +.markdown-body sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +.markdown-body sub { + bottom: -0.25em; +} + +.markdown-body sup { + top: -0.5em; +} + +.markdown-body img { + border-style: none; + max-width: 100%; + box-sizing: content-box; +} + +.markdown-body code, +.markdown-body kbd, +.markdown-body pre, +.markdown-body samp { + font-family: monospace; + font-size: 1em; +} + +.markdown-body figure { + margin: 1em var(--base-size-40); +} + +.markdown-body hr { + box-sizing: content-box; + overflow: hidden; + background: transparent; + border-bottom: 1px solid var(--borderColor-muted); + height: .25em; + padding: 0; + margin: var(--base-size-24) 0; + background-color: var(--borderColor-default); + border: 0; +} + +.markdown-body input { + font: inherit; + margin: 0; + overflow: visible; + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + +.markdown-body [type=button], +.markdown-body [type=reset], +.markdown-body [type=submit] { + -webkit-appearance: button; + appearance: button; +} + +.markdown-body [type=checkbox], +.markdown-body [type=radio] { + box-sizing: border-box; + padding: 0; +} + +.markdown-body [type=number]::-webkit-inner-spin-button, +.markdown-body [type=number]::-webkit-outer-spin-button { + height: auto; +} + +.markdown-body [type=search]::-webkit-search-cancel-button, +.markdown-body [type=search]::-webkit-search-decoration { + -webkit-appearance: none; + appearance: none; +} + +.markdown-body ::-webkit-input-placeholder { + color: inherit; + opacity: .54; +} + +.markdown-body ::-webkit-file-upload-button { + -webkit-appearance: button; + appearance: button; + font: inherit; +} + +.markdown-body a:hover { + text-decoration: underline; +} + +.markdown-body ::placeholder { + color: var(--fgColor-muted); + opacity: 1; +} + +.markdown-body hr::before { + display: table; + content: ""; +} + +.markdown-body hr::after { + display: table; + clear: both; + content: ""; +} + +.markdown-body table { + border-spacing: 0; + border-collapse: collapse; + display: block; + width: max-content; + max-width: 100%; + overflow: auto; +} + +.markdown-body td, +.markdown-body th { + padding: 0; +} + +.markdown-body details summary { + cursor: pointer; +} + +.markdown-body a:focus, +.markdown-body [role=button]:focus, +.markdown-body input[type=radio]:focus, +.markdown-body input[type=checkbox]:focus { + outline: 2px solid var(--focus-outlineColor); + outline-offset: -2px; + box-shadow: none; +} + +.markdown-body a:focus:not(:focus-visible), +.markdown-body [role=button]:focus:not(:focus-visible), +.markdown-body input[type=radio]:focus:not(:focus-visible), +.markdown-body input[type=checkbox]:focus:not(:focus-visible) { + outline: solid 1px transparent; +} + +.markdown-body a:focus-visible, +.markdown-body [role=button]:focus-visible, +.markdown-body input[type=radio]:focus-visible, +.markdown-body input[type=checkbox]:focus-visible { + outline: 2px solid var(--focus-outlineColor); + outline-offset: -2px; + box-shadow: none; +} + +.markdown-body a:not([class]):focus, +.markdown-body a:not([class]):focus-visible, +.markdown-body input[type=radio]:focus, +.markdown-body input[type=radio]:focus-visible, +.markdown-body input[type=checkbox]:focus, +.markdown-body input[type=checkbox]:focus-visible { + outline-offset: 0; +} + +.markdown-body kbd { + display: inline-block; + padding: var(--base-size-4); + font: 11px var(--fontStack-monospace, ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace); + line-height: 10px; + color: var(--fgColor-default); + vertical-align: middle; + background-color: var(--bgColor-muted); + border: solid 1px var(--borderColor-neutral-muted); + border-bottom-color: var(--borderColor-neutral-muted); + border-radius: 6px; + box-shadow: inset 0 -1px 0 var(--borderColor-neutral-muted); +} + +.markdown-body h1, +.markdown-body h2, +.markdown-body h3, +.markdown-body h4, +.markdown-body h5, +.markdown-body h6 { + margin-top: var(--base-size-24); + margin-bottom: var(--base-size-16); + font-weight: var(--base-text-weight-semibold, 600); + line-height: 1.25; +} + +.markdown-body h2 { + font-weight: var(--base-text-weight-semibold, 600); + padding-bottom: .3em; + font-size: 1.5em; + border-bottom: 1px solid var(--borderColor-muted); +} + +.markdown-body h3 { + font-weight: var(--base-text-weight-semibold, 600); + font-size: 1.25em; +} + +.markdown-body h4 { + font-weight: var(--base-text-weight-semibold, 600); + font-size: 1em; +} + +.markdown-body h5 { + font-weight: var(--base-text-weight-semibold, 600); + font-size: .875em; +} + +.markdown-body h6 { + font-weight: var(--base-text-weight-semibold, 600); + font-size: .85em; + color: var(--fgColor-muted); +} + +.markdown-body p { + margin-top: 0; + margin-bottom: 10px; +} + +.markdown-body blockquote { + margin: 0; + padding: 0 1em; + color: var(--fgColor-muted); + border-left: .25em solid var(--borderColor-default); +} + +.markdown-body ul, +.markdown-body ol { + margin-top: 0; + margin-bottom: 0; + padding-left: 2em; +} + +.markdown-body ol ol, +.markdown-body ul ol { + list-style-type: lower-roman; +} + +.markdown-body ul ul ol, +.markdown-body ul ol ol, +.markdown-body ol ul ol, +.markdown-body ol ol ol { + list-style-type: lower-alpha; +} + +.markdown-body dd { + margin-left: 0; +} + +.markdown-body tt, +.markdown-body code, +.markdown-body samp { + font-family: var(--fontStack-monospace, ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace); + font-size: 12px; +} + +.markdown-body pre { + margin-top: 0; + margin-bottom: 0; + font-family: var(--fontStack-monospace, ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace); + font-size: 12px; + word-wrap: normal; +} + +.markdown-body .octicon { + display: inline-block; + overflow: visible !important; + vertical-align: text-bottom; + fill: currentColor; +} + +.markdown-body input::-webkit-outer-spin-button, +.markdown-body input::-webkit-inner-spin-button { + margin: 0; + -webkit-appearance: none; + appearance: none; +} + +.markdown-body .mr-2 { + margin-right: var(--base-size-8, 8px) !important; +} + +.markdown-body::before { + display: table; + content: ""; +} + +.markdown-body::after { + display: table; + clear: both; + content: ""; +} + +.markdown-body>*:first-child { + margin-top: 0 !important; +} + +.markdown-body>*:last-child { + margin-bottom: 0 !important; +} + +.markdown-body a:not([href]) { + color: inherit; + text-decoration: none; +} + +.markdown-body .absent { + color: var(--fgColor-danger); +} + +.markdown-body .anchor { + float: left; + padding-right: var(--base-size-4); + margin-left: -20px; + line-height: 1; +} + +.markdown-body .anchor:focus { + outline: none; +} + +.markdown-body p, +.markdown-body blockquote, +.markdown-body ul, +.markdown-body ol, +.markdown-body dl, +.markdown-body table, +.markdown-body pre, +.markdown-body details { + margin-top: 0; + margin-bottom: var(--base-size-16); +} + +.markdown-body blockquote>:first-child { + margin-top: 0; +} + +.markdown-body blockquote>:last-child { + margin-bottom: 0; +} + +.markdown-body h1 .octicon-link, +.markdown-body h2 .octicon-link, +.markdown-body h3 .octicon-link, +.markdown-body h4 .octicon-link, +.markdown-body h5 .octicon-link, +.markdown-body h6 .octicon-link { + color: var(--fgColor-default); + vertical-align: middle; + visibility: hidden; +} + +.markdown-body h1:hover .anchor, +.markdown-body h2:hover .anchor, +.markdown-body h3:hover .anchor, +.markdown-body h4:hover .anchor, +.markdown-body h5:hover .anchor, +.markdown-body h6:hover .anchor { + text-decoration: none; +} + +.markdown-body h1:hover .anchor .octicon-link, +.markdown-body h2:hover .anchor .octicon-link, +.markdown-body h3:hover .anchor .octicon-link, +.markdown-body h4:hover .anchor .octicon-link, +.markdown-body h5:hover .anchor .octicon-link, +.markdown-body h6:hover .anchor .octicon-link { + visibility: visible; +} + +.markdown-body h1 tt, +.markdown-body h1 code, +.markdown-body h2 tt, +.markdown-body h2 code, +.markdown-body h3 tt, +.markdown-body h3 code, +.markdown-body h4 tt, +.markdown-body h4 code, +.markdown-body h5 tt, +.markdown-body h5 code, +.markdown-body h6 tt, +.markdown-body h6 code { + padding: 0 .2em; + font-size: inherit; +} + +.markdown-body summary h1, +.markdown-body summary h2, +.markdown-body summary h3, +.markdown-body summary h4, +.markdown-body summary h5, +.markdown-body summary h6 { + display: inline-block; +} + +.markdown-body summary h1 .anchor, +.markdown-body summary h2 .anchor, +.markdown-body summary h3 .anchor, +.markdown-body summary h4 .anchor, +.markdown-body summary h5 .anchor, +.markdown-body summary h6 .anchor { + margin-left: -40px; +} + +.markdown-body summary h1, +.markdown-body summary h2 { + padding-bottom: 0; + border-bottom: 0; +} + +.markdown-body ul.no-list, +.markdown-body ol.no-list { + padding: 0; + list-style-type: none; +} + +.markdown-body ol[type="a s"] { + list-style-type: lower-alpha; +} + +.markdown-body ol[type="A s"] { + list-style-type: upper-alpha; +} + +.markdown-body ol[type="i s"] { + list-style-type: lower-roman; +} + +.markdown-body ol[type="I s"] { + list-style-type: upper-roman; +} + +.markdown-body ol[type="1"] { + list-style-type: decimal; +} + +.markdown-body div>ol:not([type]) { + list-style-type: decimal; +} + +.markdown-body ul ul, +.markdown-body ul ol, +.markdown-body ol ol, +.markdown-body ol ul { + margin-top: 0; + margin-bottom: 0; +} + +.markdown-body li>p { + margin-top: var(--base-size-16); +} + +.markdown-body li+li { + margin-top: .25em; +} + +.markdown-body dl { + padding: 0; +} + +.markdown-body dl dt { + padding: 0; + margin-top: var(--base-size-16); + font-size: 1em; + font-style: italic; + font-weight: var(--base-text-weight-semibold, 600); +} + +.markdown-body dl dd { + padding: 0 var(--base-size-16); + margin-bottom: var(--base-size-16); +} + +.markdown-body table th { + font-weight: var(--base-text-weight-semibold, 600); +} + +.markdown-body table th, +.markdown-body table td { + padding: 6px 13px; + border: 1px solid var(--borderColor-default); +} + +.markdown-body table td>:last-child { + margin-bottom: 0; +} + +.markdown-body table tr { + background-color: var(--bgColor-default); + border-top: 1px solid var(--borderColor-muted); +} + +.markdown-body table tr:nth-child(2n) { + background-color: var(--bgColor-muted); +} + +.markdown-body table img { + background-color: transparent; +} + +.markdown-body img[align=right] { + padding-left: 20px; +} + +.markdown-body img[align=left] { + padding-right: 20px; +} + +.markdown-body .emoji { + max-width: none; + vertical-align: text-top; + background-color: transparent; +} + +.markdown-body span.frame { + display: block; + overflow: hidden; +} + +.markdown-body span.frame>span { + display: block; + float: left; + width: auto; + padding: 7px; + margin: 13px 0 0; + overflow: hidden; + border: 1px solid var(--borderColor-default); +} + +.markdown-body span.frame span img { + display: block; + float: left; +} + +.markdown-body span.frame span span { + display: block; + padding: 5px 0 0; + clear: both; + color: var(--fgColor-default); +} + +.markdown-body span.align-center { + display: block; + overflow: hidden; + clear: both; +} + +.markdown-body span.align-center>span { + display: block; + margin: 13px auto 0; + overflow: hidden; + text-align: center; +} + +.markdown-body span.align-center span img { + margin: 0 auto; + text-align: center; +} + +.markdown-body span.align-right { + display: block; + overflow: hidden; + clear: both; +} + +.markdown-body span.align-right>span { + display: block; + margin: 13px 0 0; + overflow: hidden; + text-align: right; +} + +.markdown-body span.align-right span img { + margin: 0; + text-align: right; +} + +.markdown-body span.float-left { + display: block; + float: left; + margin-right: 13px; + overflow: hidden; +} + +.markdown-body span.float-left span { + margin: 13px 0 0; +} + +.markdown-body span.float-right { + display: block; + float: right; + margin-left: 13px; + overflow: hidden; +} + +.markdown-body span.float-right>span { + display: block; + margin: 13px auto 0; + overflow: hidden; + text-align: right; +} + +.markdown-body code, +.markdown-body tt { + padding: .2em .4em; + margin: 0; + font-size: 85%; + white-space: break-spaces; + background-color: var(--bgColor-neutral-muted); + border-radius: 6px; +} + +.markdown-body code br, +.markdown-body tt br { + display: none; +} + +.markdown-body del code { + text-decoration: inherit; +} + +.markdown-body samp { + font-size: 85%; +} + +.markdown-body pre code { + font-size: 100%; +} + +.markdown-body pre>code { + padding: 0; + margin: 0; + word-break: normal; + white-space: pre; + background: transparent; + border: 0; +} + +.markdown-body .highlight { + margin-bottom: var(--base-size-16); +} + +.markdown-body .highlight pre { + margin-bottom: 0; + word-break: normal; +} + +.markdown-body .highlight pre, +.markdown-body pre { + padding: var(--base-size-16); + overflow: auto; + font-size: 85%; + line-height: 1.45; + color: var(--fgColor-default); + background-color: var(--bgColor-muted); + border-radius: 6px; +} + +.markdown-body pre code, +.markdown-body pre tt { + display: inline; + max-width: auto; + padding: 0; + margin: 0; + overflow: visible; + line-height: inherit; + word-wrap: normal; + background-color: transparent; + border: 0; +} + +.markdown-body .csv-data td, +.markdown-body .csv-data th { + padding: 5px; + overflow: hidden; + font-size: 12px; + line-height: 1; + text-align: left; + white-space: nowrap; +} + +.markdown-body .csv-data .blob-num { + padding: 10px var(--base-size-8) 9px; + text-align: right; + background: var(--bgColor-default); + border: 0; +} + +.markdown-body .csv-data tr { + border-top: 0; +} + +.markdown-body .csv-data th { + font-weight: var(--base-text-weight-semibold, 600); + background: var(--bgColor-muted); + border-top: 0; +} + +.markdown-body [data-footnote-ref]::before { + content: "["; +} + +.markdown-body [data-footnote-ref]::after { + content: "]"; +} + +.markdown-body .footnotes { + font-size: 12px; + color: var(--fgColor-muted); + border-top: 1px solid var(--borderColor-default); +} + +.markdown-body .footnotes ol { + padding-left: var(--base-size-16); +} + +.markdown-body .footnotes ol ul { + display: inline-block; + padding-left: var(--base-size-16); + margin-top: var(--base-size-16); +} + +.markdown-body .footnotes li { + position: relative; +} + +.markdown-body .footnotes li:target::before { + position: absolute; + top: calc(var(--base-size-8)*-1); + right: calc(var(--base-size-8)*-1); + bottom: calc(var(--base-size-8)*-1); + left: calc(var(--base-size-24)*-1); + pointer-events: none; + content: ""; + border: 2px solid var(--borderColor-accent-emphasis); + border-radius: 6px; +} + +.markdown-body .footnotes li:target { + color: var(--fgColor-default); +} + +.markdown-body .footnotes .data-footnote-backref g-emoji { + font-family: monospace; +} + +.markdown-body .pl-c { + color: var(--color-prettylights-syntax-comment); +} + +.markdown-body .pl-c1, +.markdown-body .pl-s .pl-v { + color: var(--color-prettylights-syntax-constant); +} + +.markdown-body .pl-e, +.markdown-body .pl-en { + color: var(--color-prettylights-syntax-entity); +} + +.markdown-body .pl-smi, +.markdown-body .pl-s .pl-s1 { + color: var(--color-prettylights-syntax-storage-modifier-import); +} + +.markdown-body .pl-ent { + color: var(--color-prettylights-syntax-entity-tag); +} + +.markdown-body .pl-k { + color: var(--color-prettylights-syntax-keyword); +} + +.markdown-body .pl-s, +.markdown-body .pl-pds, +.markdown-body .pl-s .pl-pse .pl-s1, +.markdown-body .pl-sr, +.markdown-body .pl-sr .pl-cce, +.markdown-body .pl-sr .pl-sre, +.markdown-body .pl-sr .pl-sra { + color: var(--color-prettylights-syntax-string); +} + +.markdown-body .pl-v, +.markdown-body .pl-smw { + color: var(--color-prettylights-syntax-variable); +} + +.markdown-body .pl-bu { + color: var(--color-prettylights-syntax-brackethighlighter-unmatched); +} + +.markdown-body .pl-ii { + color: var(--color-prettylights-syntax-invalid-illegal-text); + background-color: var(--color-prettylights-syntax-invalid-illegal-bg); +} + +.markdown-body .pl-c2 { + color: var(--color-prettylights-syntax-carriage-return-text); + background-color: var(--color-prettylights-syntax-carriage-return-bg); +} + +.markdown-body .pl-sr .pl-cce { + font-weight: bold; + color: var(--color-prettylights-syntax-string-regexp); +} + +.markdown-body .pl-ml { + color: var(--color-prettylights-syntax-markup-list); +} + +.markdown-body .pl-mh, +.markdown-body .pl-mh .pl-en, +.markdown-body .pl-ms { + font-weight: bold; + color: var(--color-prettylights-syntax-markup-heading); +} + +.markdown-body .pl-mi { + font-style: italic; + color: var(--color-prettylights-syntax-markup-italic); +} + +.markdown-body .pl-mb { + font-weight: bold; + color: var(--color-prettylights-syntax-markup-bold); +} + +.markdown-body .pl-md { + color: var(--color-prettylights-syntax-markup-deleted-text); + background-color: var(--color-prettylights-syntax-markup-deleted-bg); +} + +.markdown-body .pl-mi1 { + color: var(--color-prettylights-syntax-markup-inserted-text); + background-color: var(--color-prettylights-syntax-markup-inserted-bg); +} + +.markdown-body .pl-mc { + color: var(--color-prettylights-syntax-markup-changed-text); + background-color: var(--color-prettylights-syntax-markup-changed-bg); +} + +.markdown-body .pl-mi2 { + color: var(--color-prettylights-syntax-markup-ignored-text); + background-color: var(--color-prettylights-syntax-markup-ignored-bg); +} + +.markdown-body .pl-mdr { + font-weight: bold; + color: var(--color-prettylights-syntax-meta-diff-range); +} + +.markdown-body .pl-ba { + color: var(--color-prettylights-syntax-brackethighlighter-angle); +} + +.markdown-body .pl-sg { + color: var(--color-prettylights-syntax-sublimelinter-gutter-mark); +} + +.markdown-body .pl-corl { + text-decoration: underline; + color: var(--color-prettylights-syntax-constant-other-reference-link); +} + +.markdown-body [role=button]:focus:not(:focus-visible), +.markdown-body [role=tabpanel][tabindex="0"]:focus:not(:focus-visible), +.markdown-body button:focus:not(:focus-visible), +.markdown-body summary:focus:not(:focus-visible), +.markdown-body a:focus:not(:focus-visible) { + outline: none; + box-shadow: none; +} + +.markdown-body [tabindex="0"]:focus:not(:focus-visible), +.markdown-body details-dialog:focus:not(:focus-visible) { + outline: none; +} + +.markdown-body g-emoji { + display: inline-block; + min-width: 1ch; + font-family: "Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; + font-size: 1em; + font-style: normal !important; + font-weight: var(--base-text-weight-normal, 400); + line-height: 1; + vertical-align: -0.075em; +} + +.markdown-body g-emoji img { + width: 1em; + height: 1em; +} + +.markdown-body .task-list-item { + list-style-type: none; +} + +.markdown-body .task-list-item label { + font-weight: var(--base-text-weight-normal, 400); +} + +.markdown-body .task-list-item.enabled label { + cursor: pointer; +} + +.markdown-body .task-list-item+.task-list-item { + margin-top: var(--base-size-4); +} + +.markdown-body .task-list-item .handle { + display: none; +} + +.markdown-body .task-list-item-checkbox { + margin: 0 .2em .25em -1.4em; + vertical-align: middle; +} + +.markdown-body ul:dir(rtl) .task-list-item-checkbox { + margin: 0 -1.6em .25em .2em; +} + +.markdown-body ol:dir(rtl) .task-list-item-checkbox { + margin: 0 -1.6em .25em .2em; +} + +.markdown-body .contains-task-list:hover .task-list-item-convert-container, +.markdown-body .contains-task-list:focus-within .task-list-item-convert-container { + display: block; + width: auto; + height: 24px; + overflow: visible; + clip: auto; +} + +.markdown-body ::-webkit-calendar-picker-indicator { + filter: invert(50%); +} + +.markdown-body .markdown-alert { + padding: var(--base-size-8) var(--base-size-16); + margin-bottom: var(--base-size-16); + color: inherit; + border-left: .25em solid var(--borderColor-default); +} + +.markdown-body .markdown-alert>:first-child { + margin-top: 0; +} + +.markdown-body .markdown-alert>:last-child { + margin-bottom: 0; +} + +.markdown-body .markdown-alert .markdown-alert-title { + display: flex; + font-weight: var(--base-text-weight-medium, 500); + align-items: center; + line-height: 1; +} + +.markdown-body .markdown-alert.markdown-alert-note { + border-left-color: var(--borderColor-accent-emphasis); +} + +.markdown-body .markdown-alert.markdown-alert-note .markdown-alert-title { + color: var(--fgColor-accent); +} + +.markdown-body .markdown-alert.markdown-alert-important { + border-left-color: var(--borderColor-done-emphasis); +} + +.markdown-body .markdown-alert.markdown-alert-important .markdown-alert-title { + color: var(--fgColor-done); +} + +.markdown-body .markdown-alert.markdown-alert-warning { + border-left-color: var(--borderColor-attention-emphasis); +} + +.markdown-body .markdown-alert.markdown-alert-warning .markdown-alert-title { + color: var(--fgColor-attention); +} + +.markdown-body .markdown-alert.markdown-alert-tip { + border-left-color: var(--borderColor-success-emphasis); +} + +.markdown-body .markdown-alert.markdown-alert-tip .markdown-alert-title { + color: var(--fgColor-success); +} + +.markdown-body .markdown-alert.markdown-alert-caution { + border-left-color: var(--borderColor-danger-emphasis); +} + +.markdown-body .markdown-alert.markdown-alert-caution .markdown-alert-title { + color: var(--fgColor-danger); +} + +.markdown-body>*:first-child>.heading-element:first-child { + margin-top: 0 !important; +} + diff --git a/index.html b/index.html new file mode 100644 index 00000000..7eef48eb --- /dev/null +++ b/index.html @@ -0,0 +1,19 @@ + + + +The khepri application + + + + + + + + +<h2>This page uses frames</h2> +<p>Your browser does not accept frames. +<br>You should go to the <a href="overview-summary.html">non-frame version</a> instead. +</p> + + + \ No newline at end of file diff --git a/khepri-favicon.svg b/khepri-favicon.svg new file mode 100644 index 00000000..34d7e218 --- /dev/null +++ b/khepri-favicon.svg @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/khepri-logo-inkscape.svg b/khepri-logo-inkscape.svg new file mode 100644 index 00000000..912c8b83 --- /dev/null +++ b/khepri-logo-inkscape.svg @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/khepri-logo.svg b/khepri-logo.svg new file mode 100644 index 00000000..3a9f456b --- /dev/null +++ b/khepri-logo.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/khepri-social-preview.png b/khepri-social-preview.png new file mode 100644 index 00000000..28efdb70 Binary files /dev/null and b/khepri-social-preview.png differ diff --git a/khepri-social-preview.svg b/khepri-social-preview.svg new file mode 100644 index 00000000..06f3810f --- /dev/null +++ b/khepri-social-preview.svg @@ -0,0 +1,167 @@ + + + +rabbitmq/khepri diff --git a/khepri.html b/khepri.html new file mode 100644 index 00000000..b4e98505 --- /dev/null +++ b/khepri.html @@ -0,0 +1,3141 @@ + + + + +Module khepri + + + + + + +
+ +

Module khepri

+Khepri database API. + + +

Description

Khepri database API.

+ +

This module exposes the database API to manipulate data.

+ +

The API is mainly made of the functions used to perform simple direct atomic + operations and queries on the database: get/1, put/2, delete/1 and so on. In addition to that, transaction/1 is the + starting point to run transaction functions. However the API to use inside + transaction functions is provided by khepri_tx.

+ +

Functions in this module have simplified return values to cover most + frequent use cases. If you need more details about the queried or modified + tree nodes, like the ability to distinguish a non-existent tree node from a + tree node with no payload, you can use the khepri_adv module.

+ +

This module also provides functions to start and stop a simple unclustered + Khepri store. For more advanced setup and clustering, see khepri_cluster.

+ +

A Khepri store

+ +

A Khepri store is one instance of Khepri running inside a Ra cluster (which +could be made of a single Erlang node). It is possible to run multiple +Khepri stores in parallel by creating multiple Ra clusters.

+ +

A Khepri store is started and configured with start/0, start/1 or start/3. To setup a cluster, see khepri_cluster.

+ +

When a store is started, a store ID store_id() is returned. This +store ID is then used by the rest of this module's API. The returned store +ID currently corresponds exactly to the Ra cluster name. It must be an atom +though; other types are unsupported.

+ +

Interacting with the Khepri store

+ + The API provides two ways to interact with a Khepri store: + + + Simple operations are calls like: + + + Transactions are like Mnesia ones. The caller passes an anonymous function + to transaction/1, etc.: +
khepri:transaction(
+  fun() ->
+      khepri_tx:put(Path, Value)
+  end).
+ + Simple operations are more efficient than transactions, but transactions are + more flexible. +

Data Types

+ +

async_option()

+

async_option() = boolean() | ra_server:command_correlation() | ra_server:command_priority() | {ra_server:command_correlation(), ra_server:command_priority()}

+

Option to indicate if the command should be synchronous or asynchronous.

+ + Values are: +

+ +

async_ret()

+

async_ret() = khepri_adv:single_result() | khepri_adv:many_results() | khepri_tx:tx_fun_result() | khepri:error({not_leader, ra:server_id()})

+

The value returned from of a command function which was executed +asynchronously.

+ +

When a caller includes a correlation ID (ra_server:command_correlation()) async_option() in their khepri:command_options() on a command function, the caller will receive a + ra_event message. Handling the notification with khepri:handle_async_ret/2 will return a list of pairs of correlation IDs + (ra_server:command_correlation()) and the return values of the + commands which were applied, or {error, {not_leader, LeaderId}} if the +commands could not be applied since they were sent to a non-leader member.

+ +

Note that when commands are successfully applied, the return values are in + the khepri_adv formats - khepri_adv:single_result() or + khepri_adv:many_results() - rather than khepri:minimal_ret(), even if the command was sent using a function from + the khepri API such as khepri:put/4.

+ + See khepri:handle_async_ret/2.

+ +

child_list_length()

+

child_list_length() = non_neg_integer()

+

Number of direct child nodes under a tree node.

+ +

child_list_version()

+

child_list_version() = pos_integer()

+

Number of changes made to the list of child nodes of a tree node (child +nodes added or removed).

+ + The child list version starts at 1 when a tree node is created. It is + increased by 1 each time a child is added or removed. Changes made to + existing nodes are not reflected in this version.

+ +

command_options()

+

command_options() = #{timeout => timeout(), async => async_option(), reply_from => reply_from_option(), protect_against_dups => boolean()}

+

Options used in commands.

+ +

Commands are put/4, delete/3 and read-write transaction/4.

+ +

+ +

data()

+

data() = any()

+

Data stored in a tree node's payload.

+ +

error()

+

error() = error(any())

+

The error tuple returned by a function after a failure.

+ +

error()

+

error(Type) = {error, Type}

+

Return value of a failed command or query.

+ +

favor_option()

+

favor_option() = consistency | low_latency

+

Option to indicate where to put the cursor between freshness of the +returned data and low latency of queries.

+ + Values are: + + + As described above, queries are always evaluated locally by the cluster + member that gets the call. The reason Ra's leader and consistent queries + are not exposed is that the remote execution of the query function may fail + in subtle on non-subtle ways. For instance, the remote node might run a + different version of Erlang or Khepri.

+ +

filter_fun()

+

filter_fun() = fun((khepri_path:native_path(), khepri:node_props()) -> boolean())

+

Function passed to khepri:filter/4.

+ +

fold_acc()

+

fold_acc() = any()

+

Term passed to and returned by a fold_fun/0.

+ +

fold_fun()

+

fold_fun() = fun((khepri_path:native_path(), khepri:node_props(), khepri:fold_acc()) -> khepri:fold_acc())

+

Function passed to khepri:fold/5.

+ +

foreach_fun()

+

foreach_fun() = fun((khepri_path:native_path(), khepri:node_props()) -> any())

+

Function passed to khepri:foreach/4.

+ +

many_payloads_ret()

+

many_payloads_ret() = many_payloads_ret(undefined)

+

The return value of query functions in the khepri module that work +on a many nodes.

+ + undefined is returned if a tree node has no payload attached to it.

+ +

many_payloads_ret()

+

many_payloads_ret(Default) = khepri:ok(#{khepri_path:path() => khepri:data() | horus:horus_fun() | Default}) | khepri:error()

+

The return value of query functions in the khepri module that work +on many nodes.

+ + Default is the value to return if a tree node has no payload attached to + it.

+ +

map_fun()

+

map_fun() = fun((khepri_path:native_path(), khepri:node_props()) -> khepri:map_fun_ret())

+

Function passed to khepri:map/4.

+ +

map_fun_ret()

+

map_fun_ret() = any()

+

Value returned by khepri:map_fun/0.

+ +

minimal_ret()

+

minimal_ret() = ok | khepri:error()

+

The return value of update functions in the khepri module.

+ +

node_props()

+

node_props() = #{data => khepri:data(), has_data => boolean(), sproc => horus:horus_fun(), is_sproc => boolean(), payload_version => khepri:payload_version(), child_list_version => khepri:child_list_version(), child_list_length => khepri:child_list_length(), child_names => [khepri_path:node_id()]}

+

Structure used to return properties, payload and child nodes for a specific +tree node.

+ +

The payload in data or sproc is only returned if the tree node carries +something. If that key is missing from the returned properties map, it means +the tree node has no payload.

+ + By default, the payload (if any) and its version are returned by functions + exposed by khepri_adv. The list of returned properties can be + configured using the props_to_return option (see tree_options()).

+ +

ok()

+

ok(Type) = {ok, Type}

+

The result of a function after a successful call, wrapped in an "ok" tuple.

+ +

payload_ret()

+

payload_ret() = payload_ret(undefined)

+

The return value of query functions in the khepri module that work +on a single tree node.

+ + undefined is returned if a tree node has no payload attached to it.

+ +

payload_ret()

+

payload_ret(Default) = khepri:ok(khepri:data() | horus:horus_fun() | Default) | khepri:error()

+

The return value of query functions in the khepri module that work +on a single tree node.

+ + Default is the value to return if a tree node has no payload attached to + it.

+ +

payload_version()

+

payload_version() = pos_integer()

+

Number of changes made to the payload of a tree node.

+ + The payload version starts at 1 when a tree node is created. It is increased + by 1 each time the payload is added, modified or removed.

+ +

put_options()

+

put_options() = #{keep_while => khepri_condition:keep_while()}

+

Options specific to updates.

+ +

+ +

query_options()

+

query_options() = #{condition => ra:query_condition(), timeout => timeout(), favor => favor_option()}

+

Options used in queries.

+ + + + favor computes a condition internally. Therefore if both options are + set, condition takes precedence and favor is ignored.

+ +

reply_from_option()

+

reply_from_option() = leader | local | {member, ra:server_id()}

+

Options to indicate which member of the cluster should reply to a command +request.

+ +

Note that commands are always handled by the leader. This option only +controls which member of the cluster carries out the reply.

+ + + + When reply_from is {member, Member} and the given member is not part of + the cluster or when reply_from is local and there is no member local to + the caller, the leader member will perform the reply. This mechanism uses + the cluster membership information to decide which member should reply: if + the given Member or local member is a member of the cluster but is offline + or unreachable, no reply may be sent even though the leader may have + successfully handled the command.

+ +

store_id()

+

store_id() = atom()

+

ID of a Khepri store.

+ + This is the same as the Ra cluster name hosting the Khepri store.

+ +

tree_options()

+

tree_options() = #{expect_specific_node => boolean(), props_to_return => [payload_version | child_list_version | child_list_length | child_names | payload | has_payload | raw_payload], include_root_props => boolean()}

+

Options used during tree traversal.

+ +

+ +

trigger_id()

+

trigger_id() = atom()

+

An ID to identify a registered trigger.

+ +

unwrapped_many_payloads_ret()

+

unwrapped_many_payloads_ret() = unwrapped_many_payloads_ret(undefined)

+ + +

unwrapped_many_payloads_ret()

+

unwrapped_many_payloads_ret(Default) = #{khepri_path:path() => khepri:data() | horus:horus_fun() | Default}

+ + +

unwrapped_minimal_ret()

+

unwrapped_minimal_ret() = khepri:minimal_ret()

+ + +

unwrapped_payload_ret()

+

unwrapped_payload_ret() = unwrapped_payload_ret(undefined)

+ + +

unwrapped_payload_ret()

+

unwrapped_payload_ret(Default) = khepri:data() | horus:horus_fun() | Default

+ + +

Function Index

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
start/0Starts a store.
start/1Starts a store.
start/2Starts a store.
start/3Starts a store.
reset/0Resets the store on this Erlang node.
reset/1Resets the store on this Erlang node.
reset/2Resets the store on this Erlang node.
stop/0Stops a store.
stop/1Stops a store.
get_store_ids/0Returns the list of running stores.
is_empty/0Indicates if the store is empty or not.
is_empty/1Indicates if the store is empty or not.
is_empty/2Indicates if the store is empty or not.
get/1Returns the payload of the tree node pointed to by the given path +pattern.
get/2Returns the payload of the tree node pointed to by the given path +pattern.
get/3Returns the payload of the tree node pointed to by the given path +pattern.
get_or/2Returns the payload of the tree node pointed to by the given path +pattern, or a default value.
get_or/3Returns the payload of the tree node pointed to by the given path +pattern, or a default value.
get_or/4Returns the payload of the tree node pointed to by the given path +pattern, or a default value.
get_many/1Returns payloads of all the tree nodes matching the given path +pattern.
get_many/2Returns payloads of all the tree nodes matching the given path +pattern.
get_many/3Returns payloads of all the tree nodes matching the given path +pattern.
get_many_or/2Returns payloads of all the tree nodes matching the given path +pattern, or a default payload.
get_many_or/3Returns payloads of all the tree nodes matching the given path +pattern, or a default payload.
get_many_or/4Returns payloads of all the tree nodes matching the given path +pattern, or a default payload.
exists/1Indicates if the tree node pointed to by the given path exists or not.
exists/2Indicates if the tree node pointed to by the given path exists or not.
exists/3Indicates if the tree node pointed to by the given path exists or not.
has_data/1Indicates if the tree node pointed to by the given path has data or +not.
has_data/2Indicates if the tree node pointed to by the given path has data or +not.
has_data/3Indicates if the tree node pointed to by the given path has data or +not.
is_sproc/1Indicates if the tree node pointed to by the given path holds a stored +procedure or not.
is_sproc/2Indicates if the tree node pointed to by the given path holds a stored +procedure or not.
is_sproc/3Indicates if the tree node pointed to by the given path holds a stored +procedure or not.
count/1Counts all tree nodes matching the given path pattern.
count/2Counts all tree nodes matching the given path pattern.
count/3Counts all tree nodes matching the given path pattern.
fold/3Calls Fun on successive tree nodes matching the given path pattern, + starting with Acc.
fold/4Calls Fun on successive tree nodes matching the given path pattern, + starting with Acc.
fold/5Calls Fun on successive tree nodes matching the given path pattern, + starting with Acc.
foreach/2Calls Fun for each tree node matching the given path pattern.
foreach/3Calls Fun for each tree node matching the given path pattern.
foreach/4Calls Fun for each tree node matching the given path pattern.
map/2Produces a new map by calling Fun for each tree node matching the +given path pattern.
map/3Produces a new map by calling Fun for each tree node matching the +given path pattern.
map/4Produces a new map by calling Fun for each tree node matching the +given path pattern.
filter/2Returns a map for which predicate Pred holds true in tree nodes +matching the given path pattern.
filter/3Returns a map for which predicate Pred holds true in tree nodes +matching the given path pattern.
filter/4Returns a map for which predicate Pred holds true in tree nodes +matching the given path pattern.
run_sproc/2Runs the stored procedure pointed to by the given path and returns the +result.
run_sproc/3Runs the stored procedure pointed to by the given path and returns the +result.
run_sproc/4Runs the stored procedure pointed to by the given path and returns the +result.
put/2Sets the payload of the tree node pointed to by the given path +pattern.
put/3Sets the payload of the tree node pointed to by the given path +pattern.
put/4Sets the payload of the tree node pointed to by the given path +pattern.
put_many/2Sets the payload of all the tree nodes matching the given path pattern.
put_many/3Sets the payload of all the tree nodes matching the given path pattern.
put_many/4Sets the payload of all the tree nodes matching the given path pattern.
create/2Creates a tree node with the given payload.
create/3Creates a tree node with the given payload.
create/4Creates a tree node with the given payload.
update/2Updates an existing tree node with the given payload.
update/3Updates an existing tree node with the given payload.
update/4Updates an existing tree node with the given payload.
compare_and_swap/3Updates an existing tree node with the given payload only if its data +matches the given pattern.
compare_and_swap/4Updates an existing tree node with the given payload only if its data +matches the given pattern.
compare_and_swap/5Updates an existing tree node with the given payload only if its data +matches the given pattern.
delete/1Deletes the tree node pointed to by the given path pattern.
delete/2Deletes the tree node pointed to by the given path pattern.
delete/3Deletes the tree node pointed to by the given path pattern.
delete_many/1Deletes all tree nodes matching the given path pattern.
delete_many/2Deletes all tree nodes matching the given path pattern.
delete_many/3Deletes all tree nodes matching the given path pattern.
clear_payload/1Deletes the payload of the tree node pointed to by the given path +pattern.
clear_payload/2Deletes the payload of the tree node pointed to by the given path +pattern.
clear_payload/3Deletes the payload of the tree node pointed to by the given path +pattern.
clear_many_payloads/1Deletes the payload of all tree nodes matching the given path pattern.
clear_many_payloads/2Deletes the payload of all tree nodes matching the given path pattern.
clear_many_payloads/3Deletes the payload of all tree nodes matching the given path pattern.
register_trigger/3Registers a trigger.
register_trigger/4Registers a trigger.
register_trigger/5Registers a trigger.
register_projection/2Registers a projection.
register_projection/3Registers a projection.
register_projection/4Registers a projection.
unregister_projections/1Removes the given projections from the store.
unregister_projections/2Removes the given projections from the store.
unregister_projections/3Removes the given projections from the store.
has_projection/1Determines whether the store has a projection registered with the given +name.
has_projection/2Determines whether the store has a projection registered with the given +name.
has_projection/3Determines whether the store has a projection registered with the given + name.
transaction/1Runs a transaction and returns its result.
transaction/2Runs a transaction and returns its result.
transaction/3Runs a transaction and returns its result.
transaction/4Runs a transaction and returns its result.
transaction/5Runs a transaction and returns its result.
fence/0Blocks until all updates received by the cluster leader are applied +locally.
fence/1Blocks until all updates received by the cluster leader are applied +locally.
fence/2Blocks until all updates received by the cluster leader are applied +locally.
handle_async_ret/1Handles the Ra event sent for asynchronous call results.
handle_async_ret/2Handles the Ra event sent for asynchronous call results.
'get!'/1
'get!'/2
'get!'/3
'get_or!'/2
'get_or!'/3
'get_or!'/4
'get_many!'/1
'get_many!'/2
'get_many!'/3
'get_many_or!'/2
'get_many_or!'/3
'get_many_or!'/4
'exists!'/1
'exists!'/2
'exists!'/3
'has_data!'/1
'has_data!'/2
'has_data!'/3
'is_sproc!'/1
'is_sproc!'/2
'is_sproc!'/3
'count!'/1
'count!'/2
'count!'/3
'put!'/2
'put!'/3
'put!'/4
'put_many!'/2
'put_many!'/3
'put_many!'/4
'create!'/2
'create!'/3
'create!'/4
'update!'/2
'update!'/3
'update!'/4
'compare_and_swap!'/3
'compare_and_swap!'/4
'compare_and_swap!'/5
'delete!'/1
'delete!'/2
'delete!'/3
'delete_many!'/1
'delete_many!'/2
'delete_many!'/3
'clear_payload!'/1
'clear_payload!'/2
'clear_payload!'/3
'clear_many_payloads!'/1
'clear_many_payloads!'/2
'clear_many_payloads!'/3
export/2Exports a Khepri store using the Module callback module.
export/3Exports a Khepri store using the Module callback module.
export/4Exports a Khepri store using the Module callback module.
import/2Imports a previously exported set of tree nodes using the Module +callback module.
import/3Imports a previously exported set of tree nodes using the Module +callback module.
info/0Lists the running stores on stdout.
info/1Lists the content of specified store on stdout.
info/2Lists the content of specified store on stdout.
+ +

Function Details

+ +

start/0

+
+

start() -> Ret +

+

+

Starts a store. +

+

See also: khepri_cluster:start/0.

+ +

start/1

+
+

start(RaSystemOrDataDir::RaSystem | DataDir) -> Ret +

+

+

Starts a store. +

+

See also: khepri_cluster:start/1.

+ +

start/2

+
+

start(RaSystemOrDataDir::RaSystem | DataDir, StoreIdOrRaServerConfig::StoreId | RaServerConfig) -> Ret +

+

+

Starts a store. +

+

See also: khepri_cluster:start/2.

+ +

start/3

+
+

start(RaSystemOrDataDir::RaSystem | DataDir, StoreIdOrRaServerConfig::StoreId | RaServerConfig, Timeout) -> Ret +

+

+

Starts a store. +

+

See also: khepri_cluster:start/3.

+ +

reset/0

+
+

reset() -> Ret +

+

+

Resets the store on this Erlang node. +

+

See also: khepri_cluster:reset/0.

+ +

reset/1

+
+

reset(StoreIdOrTimeout::StoreId | Timeout) -> Ret +

+

+

Resets the store on this Erlang node. +

+

See also: khepri_cluster:reset/1.

+ +

reset/2

+
+

reset(StoreId, Timeout) -> Ret +

+

+

Resets the store on this Erlang node. +

+

See also: khepri_cluster:reset/2.

+ +

stop/0

+
+

stop() -> Ret +

+

+

Stops a store. +

+

See also: khepri_cluster:stop/0.

+ +

stop/1

+
+

stop(StoreId) -> Ret +

+

+

Stops a store. +

+

See also: khepri_cluster:stop/1.

+ +

get_store_ids/0

+
+

get_store_ids() -> [StoreId] +

+

+

Returns the list of running stores. +

+

See also: khepri_cluster:get_store_ids/0.

+ +

is_empty/0

+
+

is_empty() -> IsEmpty | Error +

+

+

Indicates if the store is empty or not.

+ + Calling this function is the same as calling is_empty(StoreId) with the + default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: is_empty/1, is_empty/2.

+ +

is_empty/1

+
+

is_empty(StoreId) -> IsEmpty | Error +

is_empty(Options) -> IsEmpty | Error +

+

+

+

Indicates if the store is empty or not.

+ + This function accepts the following two forms: + +

+

See also: is_empty/2.

+ +

is_empty/2

+
+

is_empty(StoreId, Options) -> IsEmpty | Error +

+

StoreId: the name of the Khepri store.
+Options: query options such as favor. +
+

+

returns: true if the store is empty, false if it is not, or an {error, + Reason} tuple.

+

Indicates if the store is empty or not. +

+ +

get/1

+
+

get(PathPattern) -> Ret +

+

+

Returns the payload of the tree node pointed to by the given path +pattern.

+ + Calling this function is the same as calling get(StoreId, PathPattern) + with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: get/2, get/3.

+ +

get/2

+
+

get(StoreId, PathPattern) -> Ret +

get(PathPattern, Options) -> Ret +

+

+

+

Returns the payload of the tree node pointed to by the given path +pattern.

+ + This function accepts the following two forms: + +

+

See also: get/3.

+ +

get/3

+
+

get(StoreId, PathPattern, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree node to get.
+Options: query options. +
+

+

returns: an {ok, Payload | undefined} tuple or an {error, Reason} + tuple. +

+

Returns the payload of the tree node pointed to by the given path +pattern.

+ +

The PathPattern can be provided as a native path pattern (a list of tree + node names and conditions) or as a string. See khepri_path:from_string/1.

+ +

The PathPattern must target a specific tree node. In other words, +updating many nodes with the same payload is denied. That fact is checked +before the tree node is looked up: so if a condition in the path could +potentially match several nodes, an exception is raised, even though only +one tree node would match at the time.

+ +

The returned {ok, Payload} tuple contains the payload of the targeted + tree node, or {ok, undefined} if the tree node had no payload.

+ + Example: query a tree node which holds the atom value +
%% Query the tree node at `/:foo/:bar'.
+{ok, value} = khepri:get(StoreId, [foo, bar]).
+ + Example: query an existing tree node with no payload +
%% Query the tree node at `/:no_payload'.
+{ok, undefined} = khepri:get(StoreId, [no_payload]).
+ + Example: query a non-existent tree node +
%% Query the tree node at `/:non_existent'.
+{error, ?khepri_error(node_not_found, _)} = khepri:get(
+                                              StoreId, [non_existent]).
+

+

See also: get_many/3, get_or/3, khepri_adv:get/3.

+ +

get_or/2

+
+

get_or(PathPattern, Default) -> Ret +

+

+

Returns the payload of the tree node pointed to by the given path +pattern, or a default value.

+ + Calling this function is the same as calling get_or(StoreId, PathPattern, + Default) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: get_or/3, get_or/4.

+ +

get_or/3

+
+

get_or(StoreId, PathPattern, Default) -> Ret +

get_or(PathPattern, Default, Options) -> Ret +

+

+

+

Returns the payload of the tree node pointed to by the given path +pattern, or a default value.

+ + This function accepts the following two forms: + +

+

See also: get_or/4.

+ +

get_or/4

+
+

get_or(StoreId, PathPattern, Default, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree node to get.
+Default: the default value to return in case the tree node has no + payload or does not exist.
+Options: query options. +
+

+

returns: an {ok, Payload | Default} tuple or an {error, Reason} tuple. +

+

Returns the payload of the tree node pointed to by the given path +pattern, or a default value.

+ +

The PathPattern can be provided as a native path pattern (a list of tree + node names and conditions) or as a string. See khepri_path:from_string/1.

+ +

The PathPattern must target a specific tree node. In other words, +updating many nodes with the same payload is denied. That fact is checked +before the tree node is looked up: so if a condition in the path could +potentially match several nodes, an exception is raised, even though only +one tree node would match at the time.

+ +

The returned {ok, Payload} tuple contains the payload of the targeted + tree node, or {ok, Default} if the tree node had no payload or was not +found.

+ + Example: query a tree node which holds the atom value +
%% Query the tree node at `/:foo/:bar'.
+{ok, value} = khepri:get_or(StoreId, [foo, bar], default).
+ + Example: query an existing tree node with no payload +
%% Query the tree node at `/:no_payload'.
+{ok, default} = khepri:get_or(StoreId, [no_payload], default).
+ + Example: query a non-existent tree node +
%% Query the tree node at `/:non_existent'.
+{ok, default} = khepri:get_or(StoreId, [non_existent], default).
+

+

See also: get/3, get_many_or/4, khepri_adv:get/3.

+ +

get_many/1

+
+

get_many(PathPattern) -> Ret +

+

+

Returns payloads of all the tree nodes matching the given path +pattern.

+ + Calling this function is the same as calling get_many(StoreId, + PathPattern) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: get_many/2, get_many/3.

+ +

get_many/2

+
+

get_many(StoreId, PathPattern) -> Ret +

get_many(PathPattern, Options) -> Ret +

+

+

+

Returns payloads of all the tree nodes matching the given path +pattern.

+ + This function accepts the following two forms: + +

+

See also: get_many/3.

+ +

get_many/3

+
+

get_many(StoreId, PathPattern, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree nodes to get.
+Options: query options. +
+

+

returns: an {ok, PayloadsMap} tuple or an {error, Reason} tuple. +

+

Returns payloads of all the tree nodes matching the given path +pattern.

+ +

Calling this function is the same as calling get_many_or(StoreId, + PathPattern, undefined, Options).

+ +

The PathPattern can be provided as a native path pattern (a list of tree + node names and conditions) or as a string. See khepri_path:from_string/1.

+ +

The returned {ok, PayloadsMap} tuple contains a map where keys correspond + to the path to a tree node matching the path pattern. Each key then points + to the payload of that matching tree node, or Default if the tree node +had no payload.

+ + Example: query all nodes in the tree +
%% Get all nodes in the tree. The tree is:
+%% <root>
+%% `-- foo
+%%     `-- bar = value
+{ok, #{[foo] := undefined,
+       [foo, bar] := value}} = khepri:get_many(
+                                 StoreId,
+                                 [?KHEPRI_WILDCARD_STAR_STAR]).
+

+

See also: get/3, get_many_or/4, khepri_adv:get_many/3.

+ +

get_many_or/2

+
+

get_many_or(PathPattern, Default) -> Ret +

+

+

Returns payloads of all the tree nodes matching the given path +pattern, or a default payload.

+ + Calling this function is the same as calling get_many_or(StoreId, + PathPattern, Default) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: get_many_or/3, get_many_or/4.

+ +

get_many_or/3

+
+

get_many_or(StoreId, PathPattern, Default) -> Ret +

get_many_or(PathPattern, Default, Options) -> Ret +

+

+

+

Returns payloads of all the tree nodes matching the given path +pattern, or a default payload.

+ + This function accepts the following two forms: + +

+

See also: get_many_or/4.

+ +

get_many_or/4

+
+

get_many_or(StoreId, PathPattern, Default, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree nodes to get.
+Default: the default value to set in PayloadsMap for tree nodes + with no payload.
+Options: query options. +
+

+

returns: an {ok, PayloadsMap} tuple or an {error, Reason} tuple. +

+

Returns payloads of all the tree nodes matching the given path +pattern, or a default payload.

+ +

The PathPattern can be provided as a native path pattern (a list of tree + node names and conditions) or as a string. See khepri_path:from_string/1.

+ +

The returned {ok, PayloadsMap} tuple contains a map where keys correspond + to the path to a tree node matching the path pattern. Each key then points + to the payload of that matching tree node, or Default if the tree node +had no payload.

+ + Example: query all nodes in the tree +
%% Get all nodes in the tree. The tree is:
+%% <root>
+%% `-- foo
+%%     `-- bar = value
+{ok, #{[foo] := default,
+       [foo, bar] := value}} = khepri:get_many_or(
+                                 StoreId,
+                                 [?KHEPRI_WILDCARD_STAR_STAR],
+                                 default).
+

+

See also: get_many/3, get_or/4, khepri_adv:get_many/3.

+ +

exists/1

+
+

exists(PathPattern) -> Exists | Error +

+

+

Indicates if the tree node pointed to by the given path exists or not.

+ + Calling this function is the same as calling exists(StoreId, PathPattern) + with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: exists/2, exists/3.

+ +

exists/2

+
+

exists(StoreId, PathPattern) -> Exists | Error +

exists(PathPattern, Options) -> Exists | Error +

+

+

+

Indicates if the tree node pointed to by the given path exists or not.

+ + This function accepts the following two forms: + +

+

See also: exists/3.

+ +

exists/3

+
+

exists(StoreId, PathPattern, Options) -> Exists | Error +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the nodes to check.
+Options: query options such as favor. +
+

+

returns: true if the tree node exists, false if it does not, or an + {error, Reason} tuple. +

+

Indicates if the tree node pointed to by the given path exists or not.

+ +

The PathPattern can be provided as a native path pattern (a list of tree + node names and conditions) or as a string. See khepri_path:from_string/1.

+ + The PathPattern must target a specific tree node. In other words, + updating many nodes with the same payload is denied. That fact is checked + before the tree node is looked up: so if a condition in the path could + potentially match several nodes, an exception is raised, even though only + one tree node would match at the time. +

+

See also: get/3.

+ +

has_data/1

+
+

has_data(PathPattern) -> HasData | Error +

+

+

Indicates if the tree node pointed to by the given path has data or +not.

+ + Calling this function is the same as calling has_data(StoreId, + PathPattern) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: has_data/2, has_data/3.

+ +

has_data/2

+
+

has_data(StoreId, PathPattern) -> HasData | Error +

has_data(PathPattern, Options) -> HasData | Error +

+

+

+

Indicates if the tree node pointed to by the given path has data or +not.

+ + This function accepts the following two forms: + +

+

See also: has_data/3.

+ +

has_data/3

+
+

has_data(StoreId, PathPattern, Options) -> HasData | Error +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the nodes to check.
+Options: query options such as favor. +
+

+

returns: true if tree the node holds data, false if it does not exist, + has no payload or holds a stored procedure, or an {error, Reason} tuple. +

+

Indicates if the tree node pointed to by the given path has data or +not.

+ +

The PathPattern can be provided as a native path pattern (a list of tree + node names and conditions) or as a string. See khepri_path:from_string/1.

+ + The PathPattern must target a specific tree node. In other words, + updating many nodes with the same payload is denied. That fact is checked + before the tree node is looked up: so if a condition in the path could + potentially match several nodes, an exception is raised, even though only + one tree node would match at the time. +

+

See also: get/3.

+ +

is_sproc/1

+
+

is_sproc(PathPattern) -> IsSproc | Error +

+

+

Indicates if the tree node pointed to by the given path holds a stored +procedure or not.

+ + Calling this function is the same as calling is_sproc(StoreId, + PathPattern) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: is_sproc/2, is_sproc/3.

+ +

is_sproc/2

+
+

is_sproc(StoreId, PathPattern) -> IsSproc | Error +

is_sproc(PathPattern, Options) -> IsSproc | Error +

+

+

+

Indicates if the tree node pointed to by the given path holds a stored +procedure or not.

+ + This function accepts the following two forms: + +

+

See also: is_sproc/3.

+ +

is_sproc/3

+
+

is_sproc(StoreId, PathPattern, Options) -> IsSproc | Error +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the nodes to check.
+Options: query options such as favor. +
+

+

returns: true if the tree node holds a stored procedure, false if it + does not exist, has no payload or holds data, or an {error, Reason} + tuple. +

+

Indicates if the tree node pointed to by the given path holds a stored +procedure or not.

+ +

The PathPattern can be provided as a native path pattern (a list of tree + node names and conditions) or as a string. See khepri_path:from_string/1.

+ + The PathPattern must target a specific tree node. In other words, + updating many nodes with the same payload is denied. That fact is checked + before the tree node is looked up: so if a condition in the path could + potentially match several nodes, an exception is raised, even though only + one tree node would match at the time. +

+

See also: get/3.

+ +

count/1

+
+

count(PathPattern) -> Ret +

+

+

Counts all tree nodes matching the given path pattern.

+ + Calling this function is the same as calling count(StoreId, + PathPattern) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: count/2, count/3.

+ +

count/2

+
+

count(StoreId, PathPattern) -> Ret +

count(PathPattern, Options) -> Ret +

+

+

+

Counts all tree nodes matching the given path pattern.

+ + This function accepts the following two forms: + +

+

See also: count/3.

+ +

count/3

+
+

count(StoreId, PathPattern, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the nodes to count.
+Options: query options such as favor. +
+

+

returns: an {ok, Count} tuple with the number of matching tree nodes, or + an {error, Reason} tuple.

+

Counts all tree nodes matching the given path pattern.

+ +

The PathPattern can be provided as a native path pattern (a list of tree + node names and conditions) or as a string. See khepri_path:from_string/1.

+ +

The root node is not included in the count.

+ + Example: +
%% Query the tree node at `/:foo/:bar'.
+{ok, 3} = khepri:count(StoreId, [foo, ?KHEPRI_WILDCARD_STAR]).
+

+ +

fold/3

+
+

fold(PathPattern, Fun, Acc) -> Ret +

+

+

Calls Fun on successive tree nodes matching the given path pattern, + starting with Acc.

+ + Calling this function is the same as calling fold(StoreId, PathPattern, + Fun, Acc) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: fold/4, fold/5.

+ +

fold/4

+
+

fold(StoreId, PathPattern, Fun, Acc) -> Ret +

fold(PathPattern, Fun, Acc, Options) -> Ret +

+

+

+

Calls Fun on successive tree nodes matching the given path pattern, + starting with Acc.

+ + This function accepts the following two forms: + +

+

See also: fold/5.

+ +

fold/5

+
+

fold(StoreId, PathPattern, Fun, Acc, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree nodes to get.
+Fun: the function to call for each matching tree node.
+Acc: the Erlang term to pass to the first call to Fun.
+Options: query options. +
+

+

returns: an {ok, NewAcc} tuple or an {error, Reason} tuple.

+

Calls Fun on successive tree nodes matching the given path pattern, + starting with Acc.

+ +

The PathPattern can be provided as a native path pattern (a list of tree + node names and conditions) or as a string. See khepri_path:from_string/1.

+ + Like the function passed to maps:fold/3, Fun must accept the + following arguments: +
    +
  1. the native path of the tree node being handled
  2. +
  3. the tree node properties map
  4. +
  5. an Erlang term which is either Acc for the first matched tree node, or + the return value of the previous call to Fun
  6. +
+ +

The returned {ok, NewAcc} tuple contains the return value of the last call + to Fun, or Acc if no tree nodes matched the given path pattern.

+ + Example: list all nodes in the tree +
%% List all tree node paths in the tree. The tree is:
+%% <root>
+%% `-- foo
+%%     `-- bar = value
+{ok, [[foo], [foo, bar]]} = khepri:fold(
+                              StoreId,
+                              [?KHEPRI_WILDCARD_STAR_STAR],
+                              fun(Path, _NodeProps, Acc) ->
+                                  [Path | Acc]
+                              end, []).
+

+ +

foreach/2

+
+

foreach(PathPattern, Fun) -> Ret +

+

+

Calls Fun for each tree node matching the given path pattern.

+ + Calling this function is the same as calling foreach(StoreId, PathPattern, + Fun) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: foreach/3, foreach/4.

+ +

foreach/3

+
+

foreach(StoreId, PathPattern, Fun) -> Ret +

foreach(PathPattern, Fun, Options) -> Ret +

+

+

+

Calls Fun for each tree node matching the given path pattern.

+ + This function accepts the following two forms: + +

+

See also: foreach/4.

+ +

foreach/4

+
+

foreach(StoreId, PathPattern, Fun, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree nodes to get.
+Fun: the function to call for each matching tree node.
+Options: query options. +
+

+

returns: ok or an {error, Reason} tuple.

+

Calls Fun for each tree node matching the given path pattern.

+ +

The PathPattern can be provided as a native path pattern (a list of tree + node names and conditions) or as a string. See khepri_path:from_string/1.

+ + Like the function passed to maps:foreach/2, Fun must accept the + following arguments: +
    +
  1. the native path of the tree node being handled
  2. +
  3. the tree node properties map
  4. +
+ + Example: print all nodes in the tree +
%% Print all tree node paths in the tree. The tree is:
+%% <root>
+%% `-- foo
+%%     `-- bar = value
+ok = khepri:foreach(
+          StoreId,
+          [?KHEPRI_WILDCARD_STAR_STAR],
+          fun(Path, _NodeProps) ->
+              io:format("Path ~0p~n", [Path])
+          end).
+

+ +

map/2

+
+

map(PathPattern, Fun) -> Ret +

+

+

Produces a new map by calling Fun for each tree node matching the +given path pattern.

+ + Calling this function is the same as calling map(StoreId, PathPattern, + Fun) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: map/3, map/4.

+ +

map/3

+
+

map(StoreId, PathPattern, Fun) -> Ret +

map(PathPattern, Fun, Options) -> Ret +

+

+

+

Produces a new map by calling Fun for each tree node matching the +given path pattern.

+ + This function accepts the following two forms: + +

+

See also: map/4.

+ +

map/4

+
+

map(StoreId, PathPattern, Fun, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree nodes to get.
+Fun: the function to call for each matching tree node.
+Options: query options. +
+

+

returns: {ok, Map} or an {error, Reason} tuple.

+

Produces a new map by calling Fun for each tree node matching the +given path pattern.

+ +

The produced map uses the tree node path as the key, like get_many/3 + and the return value of Fun as the value.

+ +

The PathPattern can be provided as a native path pattern (a list of tree + node names and conditions) or as a string. See khepri_path:from_string/1.

+ + Like the function passed to maps:map/2, Fun must accept the + following arguments: +
    +
  1. the native path of the tree node being handled
  2. +
  3. the tree node properties map
  4. +
+ + Example: create a map of the form "native path => string path" +
%% The tree is:
+%% <root>
+%% `-- foo
+%%     `-- bar = value
+{ok, #{[foo] => "/:foo",
+       [foo, bar] => "/:foo/:bar"}} = khepri:map(
+                                        StoreId,
+                                        [?KHEPRI_WILDCARD_STAR_STAR],
+                                        fun(Path, _NodeProps) ->
+                                            khepri_path:to_string(Path)
+                                        end).
+

+ +

filter/2

+
+

filter(PathPattern, Pred) -> Ret +

+

+

Returns a map for which predicate Pred holds true in tree nodes +matching the given path pattern.

+ + Calling this function is the same as calling filter(StoreId, PathPattern, + Pred) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: filter/3, filter/4.

+ +

filter/3

+
+

filter(StoreId, PathPattern, Pred) -> Ret +

filter(PathPattern, Pred, Options) -> Ret +

+

+

+

Returns a map for which predicate Pred holds true in tree nodes +matching the given path pattern.

+ + This function accepts the following two forms: + +

+

See also: filter/4.

+ +

filter/4

+
+

filter(StoreId, PathPattern, Pred, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree nodes to get.
+Pred: the function to call for each matching tree node.
+Options: query options. +
+

+

returns: {ok, Map} or an {error, Reason} tuple.

+

Returns a map for which predicate Pred holds true in tree nodes +matching the given path pattern.

+ +

The produced map only contains tree nodes for which Pred returned true. + The map has the same form as the one returned by get_many/3 +otherwise.

+ +

The PathPattern can be provided as a native path pattern (a list of tree + node names and conditions) or as a string. See khepri_path:from_string/1.

+ + Like the function passed to maps:filter/2, Pred must accept the + following arguments: +
    +
  1. the native path of the tree node being handled
  2. +
  3. the tree node properties filter
  4. +
+ + Example: only keep tree nodes under /:foo +
%% The tree is:
+%% <root>
+%% `-- foo
+%%     `-- bar = value
+{ok, #{[foo] => undefined,
+       [foo, bar] => value}} = khepri:filter(
+                                 StoreId,
+                                 [?KHEPRI_WILDCARD_STAR_STAR],
+                                 fun
+                                   ([foo | _], _NodeProps) -> true;
+                                   (_Path, _NodeProps)     -> false
+                                 end).
+

+ +

run_sproc/2

+
+

run_sproc(PathPattern, Args) -> Ret +

+

+

Runs the stored procedure pointed to by the given path and returns the +result.

+ + Calling this function is the same as calling run_sproc(StoreId, + PathPattern, Args) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: run_sproc/3, run_sproc/4.

+ +

run_sproc/3

+
+

run_sproc(StoreId, PathPattern, Args) -> Ret +

run_sproc(PathPattern, Args, Options) -> Ret +

+

+

+

Runs the stored procedure pointed to by the given path and returns the +result.

+ + This function accepts the following two forms: + +

+

See also: run_sproc/4.

+ +

run_sproc/4

+
+

run_sproc(StoreId, PathPattern, Args, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree node holding the + stored procedure.
+Args: the list of args to pass to the stored procedure; its length + must be equal to the stored procedure arity.
+Options: query options. +
+

+

returns: the result of the stored procedure execution, or throws an + exception if the tree node does not exist, does not hold a stored procedure + or if there was an error. +

+

Runs the stored procedure pointed to by the given path and returns the +result.

+ +

The PathPattern can be provided as a native path pattern (a list of tree + node names and conditions) or as a string. See khepri_path:from_string/1.

+ +

The PathPattern must target a specific tree node. In other words, +updating many nodes with the same payload is denied. That fact is checked +before the tree node is looked up: so if a condition in the path could +potentially match several nodes, an exception is raised, even though only +one tree node would match at the time.

+ + The length of the Args list must match the number of arguments expected by + the stored procedure. +

+

See also: is_sproc/3.

+ +

put/2

+
+

put(PathPattern, Data) -> Ret +

+

+

Sets the payload of the tree node pointed to by the given path +pattern.

+ + Calling this function is the same as calling put(StoreId, PathPattern, + Data) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: put/3, put/4.

+ +

put/3

+
+

put(StoreId, PathPattern, Data) -> Ret +

+

+

Sets the payload of the tree node pointed to by the given path +pattern.

+ + Calling this function is the same as calling put(StoreId, PathPattern, + Data, #{}). +

+

See also: put/4.

+ +

put/4

+
+

put(StoreId, PathPattern, Data, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree node to create or + modify.
+Data: the Erlang term or function to store, or a khepri_payload:payload() structure.
+Options: command options. +
+

+

returns: in the case of a synchronous call, ok or an {error, Reason} + tuple; in the case of an asynchronous call, always ok (the actual return + value may be sent by a message if a correlation ID was specified). +

+

Sets the payload of the tree node pointed to by the given path +pattern.

+ +

The PathPattern can be provided as a native path pattern (a list of tree + node names and conditions) or as a string. See khepri_path:from_string/1.

+ +

The PathPattern must target a specific tree node. In other words, +updating many nodes with the same payload is denied. That fact is checked +before the tree node is looked up: so if a condition in the path could +potentially match several nodes, an exception is raised, even though only +one tree node would match at the time.

+ +

When using a simple path (i.e. without conditions), if the targeted tree +node does not exist, it is created using the given payload. If the +targeted tree node exists, it is updated with the given payload and its +payload version is increased by one. Missing parent nodes are created on +the way.

+ +

When using a path pattern, the behavior is the same. However if a condition +in the path pattern is not met, an error is returned and the tree structure +is not modified.

+ + The payload must be one of the following form: + + +

It is possible to wrap the payload in its internal structure explicitly + using the khepri_payload module directly.

+ +

The Options map may specify command-level options; see khepri:command_options(), khepri:tree_options() and khepri:put_options().

+ +

When doing an asynchronous update, the handle_async_ret/2 +function can be used to handle the message received from Ra.

+ + Example: +
%% Insert a tree node at `/:foo/:bar', overwriting the previous value.
+ok = khepri:put(StoreId, [foo, bar], new_value).
+

+

See also: compare_and_swap/5, create/4, put_many/4, update/4, khepri_adv:put/4.

+ +

put_many/2

+
+

put_many(PathPattern, Data) -> Ret +

+

+

Sets the payload of all the tree nodes matching the given path pattern.

+ + Calling this function is the same as calling put_many(StoreId, PathPattern, + Data) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: put_many/3, put_many/4.

+ +

put_many/3

+
+

put_many(StoreId, PathPattern, Data) -> Ret +

+

+

Sets the payload of all the tree nodes matching the given path pattern.

+ + Calling this function is the same as calling put_many(StoreId, PathPattern, + Data, #{}). +

+

See also: put_many/4.

+ +

put_many/4

+
+

put_many(StoreId, PathPattern, Data, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree node to create or + modify.
+Data: the Erlang term or function to store, or a khepri_payload:payload() structure.
+Options: command options. +
+

+

returns: in the case of a synchronous call, ok or an {error, Reason} + tuple; in the case of an asynchronous call, always ok (the actual return + value may be sent by a message if a correlation ID was specified). +

+

Sets the payload of all the tree nodes matching the given path pattern.

+ +

The PathPattern can be provided as a native path pattern (a list of tree + node names and conditions) or as a string. See khepri_path:from_string/1.

+ +

When using a simple path (i.e. without conditions), if the targeted tree +node does not exist, it is created using the given payload. If the +targeted tree node exists, it is updated with the given payload and its +payload version is increased by one. Missing parent nodes are created on +the way.

+ +

When using a path pattern, the behavior is the same. However if a condition +in the path pattern is not met, an error is returned and the tree structure +is not modified.

+ + The payload must be one of the following form: + + +

It is possible to wrap the payload in its internal structure explicitly + using the khepri_payload module directly.

+ +

The Options map may specify command-level options; see khepri:command_options(), khepri:tree_options() and khepri:put_options().

+ +

When doing an asynchronous update, the handle_async_ret/2 +function can be used to handle the message received from Ra.

+ + Example: +
%% Insert a tree node at `/:foo/:bar', overwriting the previous value.
+ok = khepri:put(StoreId, [foo, bar], new_value).
+

+

See also: put/4, khepri_adv:put_many/4.

+ +

create/2

+
+

create(PathPattern, Data) -> Ret +

+

+

Creates a tree node with the given payload.

+ + Calling this function is the same as calling create(StoreId, PathPattern, + Data) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: create/3, create/4.

+ +

create/3

+
+

create(StoreId, PathPattern, Data) -> Ret +

+

+

Creates a tree node with the given payload.

+ + Calling this function is the same as calling create(StoreId, PathPattern, + Data, #{}). +

+

See also: create/4.

+ +

create/4

+
+

create(StoreId, PathPattern, Data, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree node to create.
+Data: the Erlang term or function to store, or a khepri_payload:payload() structure.
+Options: command options. +
+

+

returns: in the case of a synchronous call, ok or an {error, Reason} + tuple; in the case of an asynchronous call, always ok (the actual return + value may be sent by a message if a correlation ID was specified). +

+

Creates a tree node with the given payload.

+ +

The behavior is the same as put/4 except that if the tree node + already exists, an {error, ?khepri_error(mismatching_node, Info)} tuple is +returned.

+ + Internally, the PathPattern is modified to include an + #if_node_exists{exists = false} condition on its last component. +

+

See also: compare_and_swap/5, put/4, update/4, khepri_adv:create/4.

+ +

update/2

+
+

update(PathPattern, Data) -> Ret +

+

+

Updates an existing tree node with the given payload.

+ + Calling this function is the same as calling update(StoreId, PathPattern, + Data) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: update/3, update/4.

+ +

update/3

+
+

update(StoreId, PathPattern, Data) -> Ret +

+

+

Updates an existing tree node with the given payload.

+ + Calling this function is the same as calling update(StoreId, PathPattern, + Data, #{}). +

+

See also: update/4.

+ +

update/4

+
+

update(StoreId, PathPattern, Data, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree node to modify.
+Data: the Erlang term or function to store, or a khepri_payload:payload() structure.
+Options: command options. +
+

+

returns: in the case of a synchronous call, ok or an {error, Reason} + tuple; in the case of an asynchronous call, always ok (the actual return + value may be sent by a message if a correlation ID was specified). +

+

Updates an existing tree node with the given payload.

+ +

The behavior is the same as put/4 except that if the tree node + already exists, an {error, ?khepri_error(mismatching_node, Info)} tuple is +returned.

+ + Internally, the PathPattern is modified to include an + #if_node_exists{exists = true} condition on its last component. +

+

See also: compare_and_swap/5, create/4, put/4, khepri_adv:update/4.

+ +

compare_and_swap/3

+
+

compare_and_swap(PathPattern, DataPattern, Data) -> Ret +

+

+

Updates an existing tree node with the given payload only if its data +matches the given pattern.

+ + Calling this function is the same as calling compare_and_swap(StoreId, + PathPattern, DataPattern, Data) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: compare_and_swap/4, compare_and_swap/5.

+ +

compare_and_swap/4

+
+

compare_and_swap(StoreId, PathPattern, DataPattern, Data) -> Ret +

+

+

Updates an existing tree node with the given payload only if its data +matches the given pattern.

+ + Calling this function is the same as calling compare_and_swap(StoreId, + PathPattern, DataPattern, Data, #{}). +

+

See also: compare_and_swap/5.

+ +

compare_and_swap/5

+
+

compare_and_swap(StoreId, PathPattern, DataPattern, Data, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree node to modify.
+Data: the Erlang term or function to store, or a khepri_payload:payload() structure.
+Options: command options. +
+

+

returns: in the case of a synchronous call, ok or an {error, Reason} + tuple; in the case of an asynchronous call, always ok (the actual return + value may be sent by a message if a correlation ID was specified). +

+

Updates an existing tree node with the given payload only if its data +matches the given pattern.

+ +

The behavior is the same as put/4 except that if the tree node + already exists, an {error, ?khepri_error(mismatching_node, Info)} tuple is +returned.

+ + Internally, the PathPattern is modified to include an + #if_data_matches{pattern = DataPattern} condition on its last component. +

+

See also: create/4, put/4, update/4, khepri_adv:compare_and_swap/5.

+ +

delete/1

+
+

delete(PathPattern) -> Ret +

+

+

Deletes the tree node pointed to by the given path pattern.

+ + Calling this function is the same as calling delete(StoreId, PathPattern) + with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: delete/2, delete/3.

+ +

delete/2

+
+

delete(StoreId, PathPattern) -> Ret +

delete(PathPattern, Options) -> Ret +

+

+

+

Deletes the tree node pointed to by the given path pattern.

+ + This function accepts the following two forms: + +

+

See also: delete/3.

+ +

delete/3

+
+

delete(StoreId, PathPattern, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the node to delete.
+Options: command options. +
+

+

returns: in the case of a synchronous call, ok or an {error, Reason} + tuple; in the case of an asynchronous call, always ok (the actual return + value may be sent by a message if a correlation ID was specified). +

+

Deletes the tree node pointed to by the given path pattern.

+ +

The PathPattern can be provided as a native path pattern (a list of tree + node names and conditions) or as a string. See khepri_path:from_string/1.

+ +

The PathPattern must target a specific tree node. In other words, deleting + many nodes is denied. That fact is checked before the tree node is looked + up: so if a condition in the path could potentially match several nodes, an + exception is raised, even though only one tree node would match at the time. + If you want to delete multiple nodes at once, use delete_many/3.

+ + Example: +
%% Delete the tree node at `/:foo/:bar'.
+ok = khepri:delete(StoreId, [foo, bar]).
+

+

See also: delete_many/3, khepri_adv:delete/3.

+ +

delete_many/1

+
+

delete_many(PathPattern) -> Ret +

+

+

Deletes all tree nodes matching the given path pattern.

+ + Calling this function is the same as calling delete_many(StoreId, + PathPattern) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: delete_many/2, delete_many/3.

+ +

delete_many/2

+
+

delete_many(StoreId, PathPattern) -> Ret +

delete_many(PathPattern, Options) -> Ret +

+

+

+

Deletes all tree nodes matching the given path pattern.

+ + This function accepts the following two forms: + +

+

See also: delete_many/3.

+ +

delete_many/3

+
+

delete_many(StoreId, PathPattern, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the nodes to delete.
+Options: command options. +
+

+

returns: in the case of a synchronous call, ok or an {error, Reason} + tuple; in the case of an asynchronous call, always ok (the actual return + value may be sent by a message if a correlation ID was specified). +

+

Deletes all tree nodes matching the given path pattern.

+ +

The PathPattern can be provided as a native path pattern (a list of tree + node names and conditions) or as a string. See khepri_path:from_string/1.

+ + Example: +
%% Delete all nodes in the tree.
+ok = khepri:delete_many(StoreId, [?KHEPRI_WILDCARD_STAR]).
+

+

See also: delete/3.

+ +

clear_payload/1

+
+

clear_payload(PathPattern) -> Ret +

+

+

Deletes the payload of the tree node pointed to by the given path +pattern.

+ + Calling this function is the same as calling clear_payload(StoreId, + PathPattern) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: clear_payload/2, clear_payload/3.

+ +

clear_payload/2

+
+

clear_payload(StoreId, PathPattern) -> Ret +

+

+

Deletes the payload of the tree node pointed to by the given path +pattern.

+ + Calling this function is the same as calling clear_payload(StoreId, + PathPattern, #{}). +

+

See also: clear_payload/3.

+ +

clear_payload/3

+
+

clear_payload(StoreId, PathPattern, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree node to modify.
+Options: command options. +
+

+

returns: in the case of a synchronous call, ok or an {error, Reason} + tuple; in the case of an asynchronous call, always ok (the actual return + value may be sent by a message if a correlation ID was specified). +

+

Deletes the payload of the tree node pointed to by the given path +pattern.

+ + In other words, the payload is set to khepri_payload:no_payload(). + Otherwise, the behavior is that of put/4. +

+

See also: put/4, khepri_adv:clear_payload/3.

+ +

clear_many_payloads/1

+
+

clear_many_payloads(PathPattern) -> Ret +

+

+

Deletes the payload of all tree nodes matching the given path pattern.

+ + Calling this function is the same as calling clear_many_payloads(StoreId, + PathPattern) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: clear_many_payloads/2, clear_many_payloads/3.

+ +

clear_many_payloads/2

+
+

clear_many_payloads(StoreId, PathPattern) -> Ret +

+

+

Deletes the payload of all tree nodes matching the given path pattern.

+ + Calling this function is the same as calling clear_many_payloads(StoreId, + PathPattern, #{}). +

+

See also: clear_many_payloads/3.

+ +

clear_many_payloads/3

+
+

clear_many_payloads(StoreId, PathPattern, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree nodes to modify.
+Options: command options. +
+

+

returns: in the case of a synchronous call, ok or an {error, Reason} + tuple; in the case of an asynchronous call, always ok (the actual return + value may be sent by a message if a correlation ID was specified). +

+

Deletes the payload of all tree nodes matching the given path pattern.

+ + In other words, the payload is set to khepri_payload:no_payload(). + Otherwise, the behavior is that of put/4. +

+

See also: delete_many/3, put/4, khepri_adv:clear_many_payloads/3.

+ +

register_trigger/3

+
+

register_trigger(TriggerId, EventFilter, StoredProcPath) -> Ret +

+

+

Registers a trigger.

+ + Calling this function is the same as calling register_trigger(StoreId, + TriggerId, EventFilter, StoredProcPath) with the default store ID (see + khepri_cluster:get_default_store_id/0). +

+

See also: register_trigger/4.

+ +

register_trigger/4

+
+

register_trigger(StoreId, TriggerId, EventFilter, StoredProcPath) -> Ret +

register_trigger(TriggerId, EventFilter, StoredProcPath, Options) -> Ret +

+

+

+

Registers a trigger.

+ + This function accepts the following two forms: + +

+

See also: register_trigger/5.

+ +

register_trigger/5

+
+

register_trigger(StoreId, TriggerId, EventFilter, StoredProcPath, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+TriggerId: the name of the trigger.
+EventFilter: the event filter used to associate an event with a + stored procedure.
+StoredProcPath: the path to the stored procedure to execute when the + corresponding event occurs. +
+

+

returns: ok if the trigger was registered, an {error, Reason} tuple + otherwise.

+

Registers a trigger.

+ +

A trigger is based on an event filter. It associates an event with a stored +procedure. When an event matching the event filter is emitted, the stored +procedure is executed.

+ +

The following event filters are documented by khepri_evf:event_filter().

+ +

Here are examples of event filters:

+ +
%% An event filter can be explicitly created using the `khepri_evf'
+%% module. This is possible to specify properties at the same time.
+EventFilter = khepri_evf:tree([stock, wood, <<"oak">>], %% Required
+                              #{on_actions => [delete], %% Optional
+                                priority => 10}).       %% Optional
+
%% For ease of use, some terms can be automatically converted to an event
+%% filter. In this example, a Unix-like path can be used as a tree event
+%% filter.
+EventFilter = "/:stock/:wood/oak".
+ +

The stored procedure is expected to accept a single argument. This argument +is a map containing the event properties. Here is an example:

+ +
my_stored_procedure(Props) ->
+    #{path := Path},
+      on_action => Action} = Props.
+ +

The stored procedure is executed on the leader's Erlang node.

+ + It is guaranteed to run at least once. It could be executed multiple times + if the Ra leader changes, therefore the stored procedure must be + idempotent. +

+ +

register_projection/2

+
+

register_projection(PathPattern, Projection) -> Ret +

+

+

Registers a projection.

+ + Calling this function is the same as calling + register_projection(StoreId, PathPattern, Projection) with the default + store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: register_projection/3.

+ +

register_projection/3

+
+

register_projection(StoreId, PathPattern, Projection) -> Ret +

register_projection(PathPattern, Projection, Options) -> Ret +

+

+

+

Registers a projection.

+ + This function accepts the following two forms: + +

+

See also: register_projection/4.

+ +

register_projection/4

+
+

register_projection(StoreId, PathPattern, Projection, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the pattern of tree nodes which the projection should + watch.
+Projection: the projection resource, created with + khepri_projection:new/3.
+Options: command options for registering the projection.
+

+

returns: ok if the projection was registered, an {error, Reason} tuple + otherwise.

+

Registers a projection.

+ +

A projection is a replicated ETS cache which is kept up to date by + Khepri. See the khepri_projection module-docs for more information +about projections.

+ +

This function associates a projection created with khepri_projection:new/3 with a pattern. Any changes to tree nodes matching + the provided pattern will be turned into records using the projection's + khepri_projection:projection_fun() and then applied to the +projection's ETS table.

+ + Registering a projection fills the projection's ETS table with records from + any tree nodes which match the PathPattern and are already in the store. +

+ +

unregister_projections/1

+
+

unregister_projections(Names) -> Ret +

+

+

Removes the given projections from the store.

+ + Calling this function is the same as calling + unregister_projections(StoreId, Names) with the default store ID (see + khepri_cluster:get_default_store_id/0). +

+

See also: unregister_projections/2.

+ +

unregister_projections/2

+
+

unregister_projections(StoreId, Names) -> Ret +

unregister_projections(Names, Options) -> Ret +

+

+

+

Removes the given projections from the store.

+ + This function accepts the following two forms: + +

+

See also: unregister_projections/3.

+ +

unregister_projections/3

+
+

unregister_projections(StoreId, Names, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+Names: the names of projections to unregister or the atom all to + remove all projections.
+Options: command options for unregistering the projections.
+

+

returns: ok if the projections were unregistered, an {error, Reason} + tuple otherwise. +

+

Removes the given projections from the store.

+ + Names may either be a list of projection names to remove or the atom + all. When all is passed, every projection in the store is removed. +

+

See also: khepri_adv:unregister_projections/3.

+ +

has_projection/1

+
+

has_projection(ProjectionName) -> Ret +

+

+

Determines whether the store has a projection registered with the given +name.

+ + Calling this function is the same as calling + has_projection(StoreId, ProjectionName) with the default store ID + (see khepri_cluster:get_default_store_id/0). +

+

See also: has_projection/2.

+ +

has_projection/2

+
+

has_projection(StoreId, ProjectionName) -> Ret +

has_projection(ProjectionName, Options) -> Ret +

+

+

+

Determines whether the store has a projection registered with the given +name.

+ + This function accepts the following two forms: + +

+

See also: has_projection/3.

+ +

has_projection/3

+
+

has_projection(StoreId, ProjectionName, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+ProjectionName: the name of the projection to has as passed to + khepri_projection:new/3.
+Options: query options.
+

+

returns: true if the store contains a projection registered with the given + name, false if it does not, or an {error, Reason} tuple if the query + failed.

+

Determines whether the store has a projection registered with the given + name. +

+ +

transaction/1

+
+

transaction(FunOrPath) -> Ret +

+

+

Runs a transaction and returns its result.

+ + Calling this function is the same as calling transaction(FunOrPath, []) + with the default store ID. +

+

See also: transaction/2.

+ +

transaction/2

+
+

transaction(StoreId, FunOrPath) -> Ret +

transaction(FunOrPath, Args) -> Ret +

transaction(FunOrPath, ReadWriteOrOptions) -> Ret +

+

+

+

+

Runs a transaction and returns its result.

+ + This function accepts the following two forms: + +

+

See also: transaction/3.

+ +

transaction/3

+
+

transaction(StoreId, FunOrPath, Args) -> Ret +

transaction(StoreId, FunOrPath, ReadWriteOrOptions) -> Ret +

transaction(FunOrPath, Args, ReadWriteOrOptions) -> Ret +

transaction(FunOrPath, ReadWrite, Options) -> Ret +

+

+

+

+

+

Runs a transaction and returns its result.

+ + This function accepts the following two forms: + +

+

See also: transaction/4.

+ +

transaction/4

+
+

transaction(StoreId, FunOrPath, Args, ReadWrite) -> Ret +

transaction(StoreId, FunOrPath, Args, Options) -> Ret +

transaction(StoreId, FunOrPath, ReadWrite, Options) -> Ret +

transaction(FunOrPath, Args, ReadWrite, Options) -> Ret +

+

+

+

+

+

Runs a transaction and returns its result.

+ + This function accepts the following three forms: + +

+

See also: transaction/5.

+ +

transaction/5

+
+

transaction(StoreId, FunOrPath, Args, ReadWrite, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+FunOrPath: an arbitrary anonymous function or a path pattern pointing + to a stored procedure.
+Args: a list of arguments to pass to FunOrPath.
+ReadWrite: the read/write or read-only nature of the transaction.
+Options: command options such as the command type. +
+

+

returns: in the case of a synchronous transaction, {ok, Result} where + Result is the return value of FunOrPath, or {error, Reason} if the + anonymous function was aborted; in the case of an asynchronous transaction, + always ok (the actual return value may be sent by a message if a + correlation ID was specified).

+

Runs a transaction and returns its result.

+ +

Fun is an arbitrary anonymous function which takes the content of Args + as its arguments. In other words, the length of Args must correspond to + the arity of Fun.

+ +

Instead of Fun, PathPattern can be passed. It must point to an existing + stored procedure. The length to Args must correspond to the arity of that +stored procedure.

+ +

The ReadWrite flag determines what the Fun anonymous function is +allowed to do and in which context it runs:

+ + + +

When using PathPattern, a ReadWrite of auto is synonymous of rw.

+ +

Options is relevant for both read-only and read-write transactions +(including audetected ones). However note that both types expect different +options.

+ + The result of FunOrPath can be any term. That result is returned in an + {ok, Result} tuple if the transaction is synchronous. The result is sent + by message if the transaction is asynchronous and a correlation ID was + specified. +

+ +

fence/0

+
+

fence() -> Ret +

+

+

Blocks until all updates received by the cluster leader are applied +locally.

+ + Calling this function is the same as calling fence(StoreId) with the + default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: fence/1, fence/2.

+ +

fence/1

+
+

fence(Timeout::StoreId | Timeout) -> Ret +

+

+

Blocks until all updates received by the cluster leader are applied +locally.

+ + This function accepts the following two forms: + +

+

See also: fence/2.

+ +

fence/2

+
+

fence(StoreId, Timeout) -> Ret +

+

StoreId: the name of the Khepri store.
+Timeout: the time limit after which the call returns with an error. +
+

+

returns: ok or an {error, Reason} tuple.

+

Blocks until all updates received by the cluster leader are applied +locally.

+ +

This ensures that a subsequent query will see the result of synchronous and +asynchronous updates.

+ + This can't work however if: + +

+ +

handle_async_ret/1

+
+

handle_async_ret(RaEvent) -> Ret +

+

+

Handles the Ra event sent for asynchronous call results.

+ + Calling this function is the same as calling + handle_async_ret(StoreId, RaEvent) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: handle_async_ret/2.

+ +

handle_async_ret/2

+
+

handle_async_ret(StoreId, RaEvent) -> Ret +

+

+

Handles the Ra event sent for asynchronous call results.

+ +

When sending commands with async command_options(), the calling +process will receive Ra events with the following structure:

+ +

{ra_event, CurrentLeader, {applied, [{Correlation1, Reply1}, ...]}}

+ +

or

+ +

{ra_event, + FromId, + {rejected, {not_leader, Leader | undefined, Correlation}}}

+ +

The first event acknowledges all commands handled in a batch while the +second is sent per-command when commands are sent against a non-leader +member.

+ +

These events should be passed to this function in order to map the return +values from the async commands and to update leader information. This +function does not handle retrying rejected commands or return values from +applied commands - the caller is responsible for those tasks.

+ + Example: +
ok = khepri:put(StoreId, [stock, wood, <<"oak">>], 200, #{async => 1}),
+ok = khepri:put(StoreId, [stock, wood, <<"maple">>], 150, #{async => 2}),
+RaEvent = receive {ra_event, _, _} = Event -> Event end,
+?assertMatch(
+  [{1, {ok, #{[stock, wood, <<"oak">>] => _}}},
+   {2, {ok, #{[stock, wood, <<"maple">>] => _}}}],
+  khepri:handle_async_ret(RaEvent)).
+

+

See also: async_option(), ra:pipeline_command/4.

+ +

'get!'/1

+
+

'get!'(PathPattern) -> Payload +

+

+
+ +

'get!'/2

+
+

'get!'(StoreId, PathPattern) -> Payload +

'get!'(PathPattern, Options) -> Payload +

+

+

+
+ +

'get!'/3

+
+

'get!'(StoreId, PathPattern, Options) -> Payload +

+

+
+ +

'get_or!'/2

+
+

'get_or!'(PathPattern, Default) -> Payload +

+

+
+ +

'get_or!'/3

+
+

'get_or!'(StoreId, PathPattern, Default) -> Payload +

'get_or!'(PathPattern, Default, Options) -> Payload +

+

+

+
+ +

'get_or!'/4

+
+

'get_or!'(StoreId, PathPattern, Default, Options) -> Payload +

+

+
+ +

'get_many!'/1

+
+

'get_many!'(PathPattern) -> Payload +

+

+
+ +

'get_many!'/2

+
+

'get_many!'(StoreId, PathPattern) -> Payload +

'get_many!'(PathPattern, Options) -> Payload +

+

+

+
+ +

'get_many!'/3

+
+

'get_many!'(StoreId, PathPattern, Options) -> Payload +

+

+
+ +

'get_many_or!'/2

+
+

'get_many_or!'(PathPattern, Default) -> Payload +

+

+
+ +

'get_many_or!'/3

+
+

'get_many_or!'(StoreId, PathPattern, Default) -> Payload +

'get_many_or!'(PathPattern, Default, Options) -> Payload +

+

+

+
+ +

'get_many_or!'/4

+
+

'get_many_or!'(StoreId, PathPattern, Default, Options) -> Payload +

+

+
+ +

'exists!'/1

+
+

'exists!'(PathPattern) -> Exists +

+

+
+ +

'exists!'/2

+
+

'exists!'(StoreId, PathPattern) -> Exists +

'exists!'(PathPattern, Options) -> Exists +

+

+

+
+ +

'exists!'/3

+
+

'exists!'(StoreId, PathPattern, Options) -> Exists +

+

+
+ +

'has_data!'/1

+
+

'has_data!'(PathPattern) -> HasData +

+

+
+ +

'has_data!'/2

+
+

'has_data!'(StoreId, PathPattern) -> HasData +

'has_data!'(PathPattern, Options) -> HasData +

+

+

+
+ +

'has_data!'/3

+
+

'has_data!'(StoreId, PathPattern, Options) -> HasData +

+

+
+ +

'is_sproc!'/1

+
+

'is_sproc!'(PathPattern) -> IsSproc +

+

+
+ +

'is_sproc!'/2

+
+

'is_sproc!'(StoreId, PathPattern) -> IsSproc +

'is_sproc!'(PathPattern, Options) -> IsSproc +

+

+

+
+ +

'is_sproc!'/3

+
+

'is_sproc!'(StoreId, PathPattern, Options) -> IsSproc +

+

+
+ +

'count!'/1

+
+

'count!'(PathPattern) -> Payload +

+

+
+ +

'count!'/2

+
+

'count!'(StoreId, PathPattern) -> Payload +

'count!'(PathPattern, Options) -> Payload +

+

+

+
+ +

'count!'/3

+
+

'count!'(StoreId, PathPattern, Options) -> Payload +

+

+
+ +

'put!'/2

+
+

'put!'(PathPattern, Data) -> Payload +

+

+
+ +

'put!'/3

+
+

'put!'(StoreId, PathPattern, Data) -> Payload +

+

+
+ +

'put!'/4

+
+

'put!'(StoreId, PathPattern, Data, Options) -> Ret +

+

+
+ +

'put_many!'/2

+
+

'put_many!'(PathPattern, Data) -> Payload +

+

+
+ +

'put_many!'/3

+
+

'put_many!'(StoreId, PathPattern, Data) -> Payload +

+

+
+ +

'put_many!'/4

+
+

'put_many!'(StoreId, PathPattern, Data, Options) -> Ret +

+

+
+ +

'create!'/2

+
+

'create!'(PathPattern, Data) -> Payload +

+

+
+ +

'create!'/3

+
+

'create!'(StoreId, PathPattern, Data) -> Payload +

+

+
+ +

'create!'/4

+
+

'create!'(StoreId, PathPattern, Data, Options) -> Ret +

+

+
+ +

'update!'/2

+
+

'update!'(PathPattern, Data) -> Payload +

+

+
+ +

'update!'/3

+
+

'update!'(StoreId, PathPattern, Data) -> Payload +

+

+
+ +

'update!'/4

+
+

'update!'(StoreId, PathPattern, Data, Options) -> Ret +

+

+
+ +

'compare_and_swap!'/3

+
+

'compare_and_swap!'(PathPattern, DataPattern, Data) -> Payload +

+

+
+ +

'compare_and_swap!'/4

+
+

'compare_and_swap!'(StoreId, PathPattern, DataPattern, Data) -> Payload +

+

+
+ +

'compare_and_swap!'/5

+
+

'compare_and_swap!'(StoreId, PathPattern, DataPattern, Data, Options) -> Ret +

+

+
+ +

'delete!'/1

+
+

'delete!'(PathPattern) -> Payload +

+

+
+ +

'delete!'/2

+
+

'delete!'(StoreId, PathPattern) -> Payload +

'delete!'(PathPattern, Options) -> Ret +

+

+

+
+ +

'delete!'/3

+
+

'delete!'(StoreId, PathPattern, Options) -> Ret +

+

+
+ +

'delete_many!'/1

+
+

'delete_many!'(PathPattern) -> Payload +

+

+
+ +

'delete_many!'/2

+
+

'delete_many!'(StoreId, PathPattern) -> Payload +

'delete_many!'(PathPattern, Options) -> Ret +

+

+

+
+ +

'delete_many!'/3

+
+

'delete_many!'(StoreId, PathPattern, Options) -> Ret +

+

+
+ +

'clear_payload!'/1

+
+

'clear_payload!'(PathPattern) -> Payload +

+

+
+ +

'clear_payload!'/2

+
+

'clear_payload!'(StoreId, PathPattern) -> Payload +

+

+
+ +

'clear_payload!'/3

+
+

'clear_payload!'(StoreId, PathPattern, Options) -> Ret +

+

+
+ +

'clear_many_payloads!'/1

+
+

'clear_many_payloads!'(PathPattern) -> Payload +

+

+
+ +

'clear_many_payloads!'/2

+
+

'clear_many_payloads!'(StoreId, PathPattern) -> Payload +

+

+
+ +

'clear_many_payloads!'/3

+
+

'clear_many_payloads!'(StoreId, PathPattern, Options) -> Ret +

+

+
+ +

export/2

+
+

export(Module, ModulePriv) -> Ret +

+

+

Exports a Khepri store using the Module callback module.

+ + Calling this function is the same as calling export(StoreId, Module, + ModulePriv) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: export/3, export/4.

+ +

export/3

+
+

export(StoreId::StoreId | PathPattern, Module, ModulePriv) -> Ret +

+

+

Exports a Khepri store using the Module callback module.

+ + This function accepts the following two forms: + +

+

See also: export/4.

+ +

export/4

+
+

export(StoreId, PathPattern, Module, ModulePriv) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path pattern matching the tree nodes to export.
+Module: the callback module to use to export.
+ModulePriv: arguments passed to Module:open_write/1. +
+

+

returns: ok or an {ok, Term} tuple if the export succeeded (the actual + return value depends on whether the callback module wants to return anything + to the caller), or an {error, Reason} tuple if it failed. +

+

Exports a Khepri store using the Module callback module.

+ +

The PathPattern allows to filter which tree nodes are exported. The path + pattern can be provided as a native path pattern (a list of tree node names + and conditions) or as a string. See khepri_path:from_string/1.

+ +

Module is the callback module called to perform the actual export. It + must conform to the Mnesia Backup & Restore API. See khepri_import_export for more details.

+ +

ModulePriv is the term passed to Module:open_write/1.

+ + Example: export the full Khepri store using khepri_export_erlang as + the callback module +
ok = khepri:export(StoreId, khepri_export_erlang, "export-1.erl").
+ + Example: export a subset of the Khepri store +
ok = khepri:export(
+       StoreId,
+       "/:stock/:wood/**",
+       khepri_export_erlang,
+       "export-wood-stock-1.erl").
+

+

See also: import/3.

+ +

import/2

+
+

import(Module, ModulePriv) -> Ret +

+

+

Imports a previously exported set of tree nodes using the Module +callback module.

+ + Calling this function is the same as calling import(StoreId, Module, + ModulePriv) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: import/3.

+ +

import/3

+
+

import(StoreId, Module, ModulePriv) -> Ret +

+

StoreId: the name of the Khepri store.
+Module: the callback module to use to import.
+ModulePriv: arguments passed to Module:open_read/1. +
+

+

returns: ok or an {ok, Term} tuple if the import succeeded (the actual + return value depends on whether the callback module wants to return anything + to the caller), or an {error, Reason} tuple if it failed. +

+

Imports a previously exported set of tree nodes using the Module +callback module.

+ +

Module is the callback module called to perform the actual import. It + must conform to the Mnesia Backup & Restore API. See khepri_import_export for more details.

+ +

ModulePriv is the term passed to Module:open_read/1.

+ +

Importing something doesn't delete existing tree nodes. The caller is +responsible for deleting the existing content of a store if he needs to.

+ + Example: import a set of tree nodes using khepri_export_erlang as + the callback module +
ok = khepri:import(StoreId, khepri_export_erlang, "export-1.erl").
+

+

See also: export/3.

+ +

info/0

+
+

info() -> ok

+

+

Lists the running stores on stdout.

+ +

info/1

+
+

info(StoreId) -> ok +

+

StoreId: the name of the Khepri store.
+

+

Lists the content of specified store on stdout. +

+ +

info/2

+
+

info(StoreId, Options) -> ok +

+

StoreId: the name of the Khepri store.
+Options: query options.
+

+

Lists the content of specified store on stdout. +

+
+ + +

Generated by EDoc

+ + diff --git a/khepri_adv.html b/khepri_adv.html new file mode 100644 index 00000000..689941ac --- /dev/null +++ b/khepri_adv.html @@ -0,0 +1,888 @@ + + + + +Module khepri_adv + + + + + + +
+ +

Module khepri_adv

+Khepri database advanced API. + + +

Description

Khepri database advanced API.

+ +

This module exposes variants of the functions in khepri which +return more detailed return values for advanced use cases. Here are some +examples of what can be achieved with this module:

+ + + + Functions provided by khepri are implemented on top of this module + and simplify the return value for the more common use cases. +

Data Types

+ +

many_results()

+

many_results() = khepri_machine:common_ret()

+

Return value of a query or synchronous command targeting many tree nodes.

+ +

node_props_map()

+

node_props_map() = #{khepri_path:native_path() => khepri:node_props()}

+

Structure used to return a map of nodes and their associated properties, + payload and child nodes.

+ +

single_result()

+

single_result() = khepri:ok(khepri:node_props() | #{}) | khepri:error()

+

Return value of a query or synchronous command targeting one specific tree + node.

+ +

Function Index

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
get/1Returns the properties and payload of the tree node pointed to by the +given path pattern.
get/2Returns the properties and payload of the tree node pointed to by the +given path pattern.
get/3Returns the properties and payload of the tree node pointed to by the +given path pattern.
get_many/1Returns properties and payloads of all the tree nodes matching the +given path pattern.
get_many/2Returns properties and payloads of all the tree nodes matching the +given path pattern.
get_many/3Returns properties and payloads of all the tree nodes matching the +given path pattern.
put/2Sets the payload of the tree node pointed to by the given path +pattern.
put/3Sets the payload of the tree node pointed to by the given path +pattern.
put/4Sets the payload of the tree node pointed to by the given path +pattern.
put_many/2Sets the payload of all the tree nodes matching the given path pattern.
put_many/3Sets the payload of all the tree nodes matching the given path pattern.
put_many/4Sets the payload of all the tree nodes matching the given path pattern.
create/2Creates a tree node with the given payload.
create/3Creates a tree node with the given payload.
create/4Creates a tree node with the given payload.
update/2Updates an existing tree node with the given payload.
update/3Updates an existing tree node with the given payload.
update/4Updates an existing tree node with the given payload.
compare_and_swap/3Updates an existing tree node with the given payload only if its data +matches the given pattern.
compare_and_swap/4Updates an existing tree node with the given payload only if its data +matches the given pattern.
compare_and_swap/5Updates an existing tree node with the given payload only if its data +matches the given pattern.
delete/1Deletes the tree node pointed to by the given path pattern.
delete/2Deletes the tree node pointed to by the given path pattern.
delete/3Deletes the tree node pointed to by the given path pattern.
delete_many/1Deletes all tree nodes matching the given path pattern.
delete_many/2Deletes all tree nodes matching the given path pattern.
delete_many/3Deletes all tree nodes matching the given path pattern.
clear_payload/1Deletes the payload of the tree node pointed to by the given path +pattern.
clear_payload/2Deletes the payload of the tree node pointed to by the given path +pattern.
clear_payload/3Deletes the payload of the tree node pointed to by the given path +pattern.
clear_many_payloads/1Deletes the payload of all tree nodes matching the given path pattern.
clear_many_payloads/2Deletes the payload of all tree nodes matching the given path pattern.
clear_many_payloads/3Deletes the payload of all tree nodes matching the given path pattern.
unregister_projections/1Removes the given projections from the store.
unregister_projections/2Removes the given projections from the store.
unregister_projections/3Removes the given projections from the store.
+ +

Function Details

+ +

get/1

+
+

get(PathPattern) -> Ret +

+

+

Returns the properties and payload of the tree node pointed to by the +given path pattern.

+ + Calling this function is the same as calling get(StoreId, PathPattern) + with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: get/2, get/3.

+ +

get/2

+
+

get(StoreId, PathPattern) -> Ret +

get(PathPattern, Options) -> Ret +

+

+

+

Returns the properties and payload of the tree node pointed to by the +given path pattern.

+ + This function accepts the following two forms: + +

+

See also: get/3.

+ +

get/3

+
+

get(StoreId, PathPattern, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree node to get.
+Options: query options. +
+

+

returns: an {ok, NodeProps} tuple or an {error, Reason} tuple. +

+

Returns the properties and payload of the tree node pointed to by the +given path pattern.

+ +

The PathPattern can be provided as a native path pattern (a list of tree + node names and conditions) or as a string. See khepri_path:from_string/1.

+ +

The PathPattern must target a specific tree node. In other words, + updating many nodes with the same payload is denied. That fact is checked + before the tree node is looked up: so if a condition in the path could + potentially match several nodes, an exception is raised, even though only + one tree node would match at the time. If you want to get multiple nodes at + once, use get_many/3.

+ +

The returned {ok, NodeProps} tuple contains a map with the properties and + payload (if any) of the targeted tree node. If the tree node is not found, + {error, ?khepri_error(node_not_found, Info)} is returned.

+ + Example: query a tree node which holds the atom value +
%% Query the tree node at `/:foo/:bar'.
+{ok, #{data := value,
+       payload_version := 1}} = khepri_adv:get(StoreId, [foo, bar]).
+ + Example: query an existing tree node with no payload +
%% Query the tree node at `/:no_payload'.
+{ok, #{payload_version := 1}} = khepri_adv:get(StoreId, [no_payload]).
+ + Example: query a non-existent tree node +
%% Query the tree node at `/:non_existent'.
+{error, ?khepri_error(node_not_found, _)} = khepri_adv:get(
+                                              StoreId, [non_existent]).
+

+

See also: get_many/3, khepri:get/3.

+ +

get_many/1

+
+

get_many(PathPattern) -> Ret +

+

+

Returns properties and payloads of all the tree nodes matching the +given path pattern.

+ + Calling this function is the same as calling get_many(StoreId, + PathPattern) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: get_many/2, get_many/3.

+ +

get_many/2

+
+

get_many(StoreId, PathPattern) -> Ret +

get_many(PathPattern, Options) -> Ret +

+

+

+

Returns properties and payloads of all the tree nodes matching the +given path pattern.

+ + This function accepts the following two forms: + +

+

See also: get_many/3.

+ +

get_many/3

+
+

get_many(StoreId, PathPattern, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree nodes to get.
+Options: query options such as favor. +
+

+

returns: an {ok, NodePropsMap} tuple or an {error, Reason} tuple. +

+

Returns properties and payloads of all the tree nodes matching the +given path pattern.

+ +

The PathPattern can be provided as a native path pattern (a list of tree + node names and conditions) or as a string. See khepri_path:from_string/1.

+ +

The returned {ok, NodePropsMap} tuple contains a map where keys correspond +to the path to a tree node matching the path pattern. Each key then points +to a map containing the properties and payload (if any) of that matching +tree node.

+ + Example: query all nodes in the tree +
%% Get all nodes in the tree. The tree is:
+%% <root>
+%% `-- foo
+%%     `-- bar = value
+{ok, #{[foo] :=
+       #{payload_version := 1},
+       [foo, bar] :=
+       #{data := value,
+         payload_version := 1}}} = khepri_adv:get_many(
+                                     StoreId,
+                                     [?KHEPRI_WILDCARD_STAR_STAR]).
+

+

See also: get/3, khepri:get_many/3.

+ +

put/2

+
+

put(PathPattern, Data) -> Ret +

+

+

Sets the payload of the tree node pointed to by the given path +pattern.

+ + Calling this function is the same as calling put(StoreId, PathPattern, + Data) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: put/3, put/4.

+ +

put/3

+
+

put(StoreId, PathPattern, Data) -> Ret +

+

+

Sets the payload of the tree node pointed to by the given path +pattern.

+ + Calling this function is the same as calling put(StoreId, PathPattern, + Data, #{}). +

+

See also: put/4.

+ +

put/4

+
+

put(StoreId, PathPattern, Data, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree node to create or + modify.
+Data: the Erlang term or function to store, or a khepri_payload:payload() structure.
+Options: command options. +
+

+

returns: in the case of a synchronous call, an {ok, NodeProps} tuple or + an {error, Reason} tuple; in the case of an asynchronous call, always + ok (the actual return value may be sent by a message if a correlation ID + was specified). +

+

Sets the payload of the tree node pointed to by the given path +pattern.

+ +

The PathPattern can be provided as a native path pattern (a list of tree + node names and conditions) or as a string. See khepri_path:from_string/1.

+ +

The PathPattern must target a specific tree node. In other words, +updating many nodes with the same payload is denied. That fact is checked +before the tree node is looked up: so if a condition in the path could +potentially match several nodes, an exception is raised, even though only +one tree node would match at the time.

+ +

When using a simple path (i.e. without conditions), if the targeted tree +node does not exist, it is created using the given payload. If the +targeted tree node exists, it is updated with the given payload and its +payload version is increased by one. Missing parent nodes are created on +the way.

+ +

When using a path pattern, the behavior is the same. However if a condition +in the path pattern is not met, an error is returned and the tree structure +is not modified.

+ +

The returned {ok, NodeProps} tuple contains a map with the properties and + payload (if any) of the targeted tree node: the payload was the one before + the update, other properties like the payload version correspond to the + updated node. If the targeted tree node didn't exist, NodeProps will be +an empty map.

+ + The payload must be one of the following form: + + +

It is possible to wrap the payload in its internal structure explicitly + using the khepri_payload module directly.

+ +

The Options map may specify command-level options; see khepri:command_options(), khepri:tree_options() and khepri:put_options().

+ +

When doing an asynchronous update, the handle_async_ret/1 +function should be used to handle the message received from Ra.

+ +

The returned {ok, NodeProps} tuple contains a map with the properties and +payload (if any) of the targeted tree node as they were before the put.

+ + Example: +
%% Insert a tree node at `/:foo/:bar', overwriting the previous value.
+{ok, #{data := value,
+       payload_version := 1}} = khepri_adv:put(
+                                  StoreId, [foo, bar], new_value).
+

+

See also: compare_and_swap/5, create/4, update/4, khepri:put/4.

+ +

put_many/2

+
+

put_many(PathPattern, Data) -> Ret +

+

+

Sets the payload of all the tree nodes matching the given path pattern.

+ + Calling this function is the same as calling put_many(StoreId, PathPattern, + Data) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: put_many/3, put_many/4.

+ +

put_many/3

+
+

put_many(StoreId, PathPattern, Data) -> Ret +

+

+

Sets the payload of all the tree nodes matching the given path pattern.

+ + Calling this function is the same as calling put_many(StoreId, PathPattern, + Data, #{}). +

+

See also: put_many/4.

+ +

put_many/4

+
+

put_many(StoreId, PathPattern, Data, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree node to create or + modify.
+Data: the Erlang term or function to store, or a khepri_payload:payload() structure.
+Options: command options. +
+

+

returns: in the case of a synchronous call, an {ok, NodePropsMap} tuple or + an {error, Reason} tuple; in the case of an asynchronous call, always ok + (the actual return value may be sent by a message if a correlation ID was + specified). +

+

Sets the payload of all the tree nodes matching the given path pattern.

+ +

The PathPattern can be provided as a native path pattern (a list of tree + node names and conditions) or as a string. See khepri_path:from_string/1.

+ +

When using a simple path (i.e. without conditions), if the targeted tree +node does not exist, it is created using the given payload. If the +targeted tree node exists, it is updated with the given payload and its +payload version is increased by one. Missing parent nodes are created on +the way.

+ +

When using a path pattern, the behavior is the same. However if a condition +in the path pattern is not met, an error is returned and the tree structure +is not modified.

+ +

The returned {ok, NodePropsMap} tuple contains a map where keys +correspond to the path to a tree node matching the path pattern. Each key +then points to a map containing the properties and payload (if any) of the +targeted tree node: the payload was the one before the update, other +properties like the payload version correspond to the updated node.

+ + The payload must be one of the following form: + + +

It is possible to wrap the payload in its internal structure explicitly + using the khepri_payload module directly.

+ +

The Options map may specify command-level options; see khepri:command_options(), khepri:tree_options() and khepri:put_options().

+ +

When doing an asynchronous update, the handle_async_ret/1 +function should be used to handle the message received from Ra.

+ + Example: +
%% Set value of all tree nodes matching `/*/:bar', to `new_value'.
+{ok, #{[foo, bar] :=
+       #{data := value,
+         payload_version := 1},
+       [baz, bar] :=
+       #{payload_version := 1}}} = khepri_adv:put_many(
+                                     StoreId,
+                                     [?KHEPRI_WILDCARD_STAR, bar],
+                                     new_value).
+

+

See also: put/4, khepri:put_many/4.

+ +

create/2

+
+

create(PathPattern, Data) -> Ret +

+

+

Creates a tree node with the given payload.

+ + Calling this function is the same as calling create(StoreId, PathPattern, + Data) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: create/3, create/4.

+ +

create/3

+
+

create(StoreId, PathPattern, Data) -> Ret +

+

+

Creates a tree node with the given payload.

+ + Calling this function is the same as calling create(StoreId, PathPattern, + Data, #{}). +

+

See also: create/4.

+ +

create/4

+
+

create(StoreId, PathPattern, Data, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree node to create.
+Data: the Erlang term or function to store, or a khepri_payload:payload() structure.
+Options: command options. +
+

+

returns: in the case of a synchronous call, an {ok, NodeProps} tuple or + an {error, Reason} tuple; in the case of an asynchronous call, always + ok (the actual return value may be sent by a message if a correlation ID + was specified). +

+

Creates a tree node with the given payload.

+ +

The behavior is the same as put/4 except that if the tree node + already exists, an {error, ?khepri_error(mismatching_node, Info)} tuple +is returned.

+ + Internally, the PathPattern is modified to include an + #if_node_exists{exists = false} condition on its last component. +

+

See also: put/4, update/4, khepri:create/4.

+ +

update/2

+
+

update(PathPattern, Data) -> Ret +

+

+

Updates an existing tree node with the given payload.

+ + Calling this function is the same as calling update(StoreId, PathPattern, + Data) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: update/3, update/4.

+ +

update/3

+
+

update(StoreId, PathPattern, Data) -> Ret +

+

+

Updates an existing tree node with the given payload.

+ + Calling this function is the same as calling update(StoreId, PathPattern, + Data, #{}). +

+

See also: update/4.

+ +

update/4

+
+

update(StoreId, PathPattern, Data, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree node to modify.
+Data: the Erlang term or function to store, or a khepri_payload:payload() structure.
+Options: command options. +
+

+

returns: in the case of a synchronous call, an {ok, NodeProps} tuple or + an {error, Reason} tuple; in the case of an asynchronous call, always + ok (the actual return value may be sent by a message if a correlation ID + was specified). +

+

Updates an existing tree node with the given payload.

+ +

The behavior is the same as put/4 except that if the tree node + already exists, an {error, ?khepri_error(mismatching_node, Info)} tuple +is returned.

+ + Internally, the PathPattern is modified to include an + #if_node_exists{exists = true} condition on its last component. +

+

See also: create/4, put/4, khepri:update/4.

+ +

compare_and_swap/3

+
+

compare_and_swap(PathPattern, DataPattern, Data) -> Ret +

+

+

Updates an existing tree node with the given payload only if its data +matches the given pattern.

+ + Calling this function is the same as calling compare_and_swap(StoreId, + PathPattern, DataPattern, Data) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: compare_and_swap/4, compare_and_swap/5.

+ +

compare_and_swap/4

+
+

compare_and_swap(StoreId, PathPattern, DataPattern, Data) -> Ret +

+

+

Updates an existing tree node with the given payload only if its data +matches the given pattern.

+ + Calling this function is the same as calling compare_and_swap(StoreId, + PathPattern, DataPattern, Data, #{}). +

+

See also: compare_and_swap/5.

+ +

compare_and_swap/5

+
+

compare_and_swap(StoreId, PathPattern, DataPattern, Data, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree node to modify.
+Data: the Erlang term or function to store, or a khepri_payload:payload() structure.
+Options: command options. +
+

+

returns: in the case of a synchronous call, an {ok, NodeProps} tuple or + an {error, Reason} tuple; in the case of an asynchronous call, always + ok (the actual return value may be sent by a message if a correlation ID + was specified). +

+

Updates an existing tree node with the given payload only if its data +matches the given pattern.

+ +

The behavior is the same as put/4 except that if the tree node + already exists, an {error, ?khepri_error(mismatching_node, Info)} tuple +is returned.

+ + Internally, the PathPattern is modified to include an + #if_data_matches{pattern = DataPattern} condition on its last component. +

+

See also: put/4, khepri:compare_and_swap/5.

+ +

delete/1

+
+

delete(PathPattern) -> Ret +

+

+

Deletes the tree node pointed to by the given path pattern.

+ + Calling this function is the same as calling delete(StoreId, PathPattern) + with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: delete/2, delete/3.

+ +

delete/2

+
+

delete(StoreId, PathPattern) -> Ret +

delete(PathPattern, Options) -> Ret +

+

+

+

Deletes the tree node pointed to by the given path pattern.

+ + This function accepts the following two forms: + +

+

See also: delete/3.

+ +

delete/3

+
+

delete(StoreId, PathPattern, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the nodes to delete.
+Options: command options such as the command type. +
+

+

returns: in the case of a synchronous call, an {ok, NodeProps} tuple or + an {error, Reason} tuple; in the case of an asynchronous call, always + ok (the actual return value may be sent by a message if a correlation ID + was specified). +

+

Deletes the tree node pointed to by the given path pattern.

+ +

The PathPattern can be provided as a native path pattern (a list of tree + node names and conditions) or as a string. See khepri_path:from_string/1.

+ +

The PathPattern must target a specific tree node. In other words, + updating many nodes with the same payload is denied. That fact is checked + before the tree node is looked up: so if a condition in the path could + potentially match several nodes, an exception is raised, even though only + one tree node would match at the time. If you want to delete multiple nodes + at once, use delete_many/3.

+ +

The returned {ok, NodeProps} tuple contains a map with the properties and + payload (if any) of the targeted tree node as they were before the delete. + If the targeted tree node didn't exist, NodeProps will be an empty map.

+ +

When doing an asynchronous update, the handle_async_ret/1 +function should be used to handle the message received from Ra.

+ + Example: +
%% Delete the tree node at `/:foo/:bar'.
+{ok, #{data := value,
+       payload_version := 1}} = khepri_adv:delete(StoreId, [foo, bar]).
+

+

See also: delete_many/3, khepri:delete/3.

+ +

delete_many/1

+
+

delete_many(PathPattern) -> Ret +

+

+

Deletes all tree nodes matching the given path pattern.

+ + Calling this function is the same as calling delete_many(StoreId, + PathPattern) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: delete_many/2, delete_many/3.

+ +

delete_many/2

+
+

delete_many(StoreId, PathPattern) -> Ret +

delete_many(PathPattern, Options) -> Ret +

+

+

+

Deletes all tree nodes matching the given path pattern.

+ + This function accepts the following two forms: + +

+

See also: delete_many/3.

+ +

delete_many/3

+
+

delete_many(StoreId, PathPattern, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the nodes to delete.
+Options: command options such as the command type. +
+

+

returns: in the case of a synchronous call, an {ok, NodePropsMap} tuple + or an {error, Reason} tuple; in the case of an asynchronous call, always + ok (the actual return value may be sent by a message if a correlation ID + was specified). +

+

Deletes all tree nodes matching the given path pattern.

+ +

The PathPattern can be provided as a native path pattern (a list of tree + node names and conditions) or as a string. See khepri_path:from_string/1.

+ +

The returned {ok, NodePropsMap} tuple contains a map where keys +correspond to the path to a deleted tree node. Each key then points to a +map containing the properties and payload (if any) of that deleted tree +node as they were before the delete.

+ +

When doing an asynchronous update, the handle_async_ret/1 +function should be used to handle the message received from Ra.

+ + Example: +
%% Delete the tree node at `/:foo/:bar'.
+{ok, #{[foo, bar] := #{data := value,
+                       payload_version := 1},
+       [baz, bar] := #{payload_version := 1}}} = khepri_adv:delete_many(
+                                                   StoreId, [foo, bar]).
+

+

See also: delete/3, khepri:delete/3.

+ +

clear_payload/1

+
+

clear_payload(PathPattern) -> Ret +

+

+

Deletes the payload of the tree node pointed to by the given path +pattern.

+ + Calling this function is the same as calling clear_payload(StoreId, + PathPattern) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: clear_payload/2, clear_payload/3.

+ +

clear_payload/2

+
+

clear_payload(StoreId, PathPattern) -> Ret +

+

+

Deletes the payload of the tree node pointed to by the given path +pattern.

+ + Calling this function is the same as calling clear_payload(StoreId, + PathPattern, #{}). +

+

See also: clear_payload/3.

+ +

clear_payload/3

+
+

clear_payload(StoreId, PathPattern, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree node to modify.
+Options: command options. +
+

+

returns: in the case of a synchronous call, an {ok, NodeProps} tuple or + an {error, Reason} tuple; in the case of an asynchronous call, always + ok (the actual return value may be sent by a message if a correlation ID + was specified). +

+

Deletes the payload of the tree node pointed to by the given path +pattern.

+ + In other words, the payload is set to khepri_payload:no_payload(). + Otherwise, the behavior is that of update/4. +

+

See also: update/4, khepri:clear_payload/3.

+ +

clear_many_payloads/1

+
+

clear_many_payloads(PathPattern) -> Ret +

+

+

Deletes the payload of all tree nodes matching the given path pattern.

+ + Calling this function is the same as calling clear_many_payloads(StoreId, + PathPattern) with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: clear_many_payloads/2, clear_many_payloads/3.

+ +

clear_many_payloads/2

+
+

clear_many_payloads(StoreId, PathPattern) -> Ret +

+

+

Deletes the payload of all tree nodes matching the given path pattern.

+ + Calling this function is the same as calling clear_many_payloads(StoreId, + PathPattern, #{}). +

+

See also: clear_many_payloads/3.

+ +

clear_many_payloads/3

+
+

clear_many_payloads(StoreId, PathPattern, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+PathPattern: the path (or path pattern) to the tree nodes to modify.
+Options: command options. +
+

+

returns: in the case of a synchronous call, an {ok, NodePropsMap} tuple + or an {error, Reason} tuple; in the case of an asynchronous call, always + ok (the actual return value may be sent by a message if a correlation ID + was specified). +

+

Deletes the payload of all tree nodes matching the given path pattern.

+ + In other words, the payload is set to khepri_payload:no_payload(). + Otherwise, the behavior is that of put/4. +

+

See also: delete_many/3, put/4, khepri:clear_many_payloads/3.

+ +

unregister_projections/1

+
+

unregister_projections(Names) -> Ret +

+

+

Removes the given projections from the store.

+ + Calling this function is the same as calling + unregister_projections(StoreId, Names) with the default store ID (see + khepri_cluster:get_default_store_id/0). +

+

See also: unregister_projections/2.

+ +

unregister_projections/2

+
+

unregister_projections(StoreId, Names) -> Ret +

unregister_projections(Names, Options) -> Ret +

+

+

+

Removes the given projections from the store.

+ + This function accepts the following two forms: + +

+

See also: unregister_projections/3.

+ +

unregister_projections/3

+
+

unregister_projections(StoreId, Names, Options) -> Ret +

+

StoreId: the name of the Khepri store.
+Names: the names of projections to unregister or the atom all to + remove all projections.
+Options: command options for unregistering the projections.
+

+

returns: ok if the projections were unregistered, an {error, Reason} + tuple otherwise.

+

Removes the given projections from the store.

+ + Names may either be a list of projection names to remove or the atom + all. When all is passed, every projection in the store is removed. +

+
+ + +

Generated by EDoc

+ + diff --git a/khepri_cluster.html b/khepri_cluster.html new file mode 100644 index 00000000..cb135124 --- /dev/null +++ b/khepri_cluster.html @@ -0,0 +1,494 @@ + + + + +Module khepri_cluster + + + + + + +
+ +

Module khepri_cluster

+Khepri service and cluster management API. + + +

Description

Khepri service and cluster management API.

+ +

This module provides the public API for the service and cluster management. + For convenience, some functions of this API are repeated in the khepri module for easier access.

+ +

The Khepri store and the Ra cluster

+ +

A Khepri store is a Ra server inside a Ra cluster. The Khepri store and the +Ra cluster share the same name in fact. The only constraint is that the name +must be an atom, even though Ra accepts other Erlang types as cluster names.

+ +

By default, Khepri uses khepri as the store ID (and thus Ra cluster name). + This default can be overridden using an argument to the start() functions + or the default_store_id application environment variable.

+ + Examples: + + +

The data directory and the Ra system

+ +

A Ra server relies on a Ra system to provide various functions and to +configure the directory where the data should be stored on disk.

+ +

By default, Khepri will configure its own Ra system to write data under + khepri#Nodename in the current working directory, where Nodename is +the name of the Erlang node.

+ +
{ok, StoreId} = khepri:start().
+ 
+%% If the Erlang node was started without distribution (the default), the
+%% statement above will start a Ra system called like the store (`khepri')
+%% and will use the `khepri#nonode@nohost' directory.
+ +

The default data directory or Ra system name can be overridden using an + argument to the start() or the default_ra_system application environment +variable. Both a directory (string or binary) or the name of an already +running Ra system are accepted.

+ + Examples: + + +

Please refer to Ra + documentation to learn more about Ra systems and Ra clusters.

+ +

Managing Ra cluster members

+ +

A Khepri/Ra cluster can be expanded by telling a node to join a remote +cluster. Note that the Khepri store/Ra server to add to the cluster must run +before it can join.

+ +
%% Start the local Khepri store.
+{ok, StoreId} = khepri:start().
+ 
+%% Join a remote cluster.
+ok = khepri_cluster:join(RemoteNode).
+ +

To remove the local Khepri store node from the cluster, it must be reset.

+ +
%% Start the local Khepri store.
+ok = khepri_cluster:reset().
+

Data Types

+ +

incomplete_ra_server_config()

+

incomplete_ra_server_config() = map()

+

A Ra server config map.

+ + This configuration map can lack the required parameters, Khepri will fill + them if necessary. Important parameters for Khepri (e.g. machine) will be + overridden anyway. +

+ +

Function Index

+ + + + + + + + + + + + + + + + + + + + + + + + +
start/0Starts a store.
start/1Starts a store.
start/2Starts a store.
start/3Starts a store.
stop/0Stops a store.
stop/1Stops a store.
join/1Adds the local running Khepri store to a remote cluster.
join/2Adds the local running Khepri store to a remote cluster.
reset/0Resets the store on this Erlang node.
reset/1Resets the store on this Erlang node.
reset/2Resets the store on this Erlang node.
get_default_ra_system_or_data_dir/0Returns the default Ra system name or data directory.
get_default_store_id/0Returns the default Khepri store ID.
members/0Returns the list of Ra members that are part of the cluster.
members/1Returns the list of Ra members that are part of the cluster.
members/2Returns the list of Ra members that are part of the cluster.
nodes/0Returns the list of Erlang nodes that are part of the cluster.
nodes/1Returns the list of Erlang nodes that are part of the cluster.
nodes/2Returns the list of Erlang nodes that are part of the cluster.
wait_for_leader/0Waits for a leader to be elected.
wait_for_leader/1Waits for a leader to be elected.
wait_for_leader/2Waits for a leader to be elected.
get_store_ids/0Returns the list of running stores.
is_store_running/1Indicates if StoreId is running or not.
+ +

Function Details

+ +

start/0

+
+

start() -> Ret +

+

+

Starts a store.

+ + Calling this function is the same as calling start(DefaultRaSystem) where + DefaultRaSystem is returned by get_default_ra_system_or_data_dir/0. +

+

See also: start/1, ra_server:ra_server_config().

+ +

start/1

+
+

start(RaSystemOrDataDir::RaSystem | DataDir) -> Ret +

+

+

Starts a store.

+ + Calling this function is the same as calling start(RaSystemOrDataDir, + DefaultStoreId) where DefaultStoreId is returned by get_default_store_id/0. +

+

See also: start/2.

+ +

start/2

+
+

start(RaSystemOrDataDir::RaSystem | DataDir, StoreIdOrRaServerConfig::StoreId | RaServerConfig) -> Ret +

+

+

Starts a store.

+ + Calling this function is the same as calling start(RaSystemOrDataDir, + StoreIdOrRaServerConfig, DefaultTimeout) where DefaultTimeout is + returned by khepri_app:get_default_timeout/0. +

+

See also: start/3.

+ +

start/3

+
+

start(RaSystemOrDataDir::RaSystem | DataDir, StoreIdOrRaServerConfig::StoreId | RaServerConfig, Timeout) -> Ret +

+

+

returns: the ID of the started store in an "ok" tuple, or an error tuple if + the store couldn't be started.

+

Starts a store.

+ +

It accepts either a Ra system name (atom) or a data directory (string or +binary) as its first argument. If a Ra system name is given, that Ra system +must be running when this function is called. If a data directory is given, +a new Ra system will be started, using this directory. The directory will +be created automatically if it doesn't exist. The Ra system will use the +same name as the Khepri store.

+ +

It accepts a Khepri store ID or a Ra server configuration as its second +argument. If a store ID is given, a Ra server configuration will be created +based on it. If a Ra server configuration is given, the name of the Khepri +store will be derived from it.

+ +

If this is a new store, the Ra server is started and an election is +triggered so that it becomes its own leader and is ready to process +commands and queries.

+ + If the store was started in the past and stopped, it will be restarted. In + this case, RaServerConfig will be ignored. Ra will take care of the + eletion automatically. +

+ +

stop/0

+
+

stop() -> Ret +

+

+

Stops a store.

+ + Calling this function is the same as calling stop(DefaultStoreId) + where DefaultStoreId is returned by get_default_store_id/0. +

+

See also: stop/1.

+ +

stop/1

+
+

stop(StoreId) -> Ret +

+

StoreId: the ID of the store to stop. +
+

+

returns: ok if it succeeds, an error tuple otherwise.

+

Stops a store. +

+ +

join/1

+
+

join(RemoteNode::RemoteMember | RemoteNode) -> Ret +

+

+

Adds the local running Khepri store to a remote cluster.

+ + This function accepts the following forms: + +

+

See also: join/2.

+ +

join/2

+
+

join(RemoteNode::RemoteMember | RemoteNode | StoreId, Timeout::Timeout | RemoteNode) -> Ret +

+

+

Adds the local running Khepri store to a remote cluster.

+ + This function accepts the following forms: + +

+

See also: join/3.

+ +

reset/0

+
+

reset() -> Ret +

+

+

Resets the store on this Erlang node.

+ +

reset/1

+
+

reset(Timeout::StoreId | Timeout) -> Ret +

+

+

Resets the store on this Erlang node.

+ +

reset/2

+
+

reset(StoreId, Timeout) -> Ret +

+

StoreId: the name of the Khepri store.
+

+

Resets the store on this Erlang node.

+ +

It does that by force-deleting the Ra local server.

+ + This function is also used to gracefully remove the local Khepri store node + from a cluster. +

+ +

get_default_ra_system_or_data_dir/0

+
+

get_default_ra_system_or_data_dir() -> RaSystem | DataDir +

+

+

returns: the value of the default_ra_system application environment + variable.

+

Returns the default Ra system name or data directory.

+ +

This is based on Khepri's default_ra_system` application environment + variable. The variable can be set to: + <ul> + <li>A directory (a string or binary) where data should be stored. A new Ra + system called `khepri` will be initialized with this directory.</li> + <li>A Ra system name (an atom). In this case, the user is expected to + configure and start the Ra system before starting Khepri.</li> + </ul> + + If this application environment variable is unset, the default is to + configure a Ra system called `khepri which will write data in + "khepri-$NODE" in the current working directory where $NODE is the +Erlang node name.

+ + Example of an Erlang configuration file for Khepri: +
{khepri, [{default_ra_system, "/var/db/khepri"}]}.
+

+ +

get_default_store_id/0

+
+

get_default_store_id() -> StoreId +

+

+

returns: the value of the default_store_id application environment + variable.

+

Returns the default Khepri store ID.

+ + This is based on Khepri's default_store_id application environment + variable. The variable can be set to an atom. The default is khepri. +

+ +

members/0

+
+

members() -> Ret +

+

+

Returns the list of Ra members that are part of the cluster.

+ + Calling this function is the same as calling members(StoreId) with the + default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: members/1, members/2.

+ +

members/1

+
+

members(StoreId::StoreId | Options) -> Ret +

+

+

Returns the list of Ra members that are part of the cluster.

+ + This function accepts the following two forms: + +

+

See also: members/2.

+ +

members/2

+
+

members(StoreId, Options) -> Ret +

+

StoreId: the ID of the store to stop.
+Options: query options such as favor. +
+

+

returns: an {ok, Members} tuple or an {error, Reason} tuple. Members + is a non-empty list of Ra server IDs.

+

Returns the list of Ra members that are part of the cluster.

+ +

If the favor option is set to consistency, the Ra leader is queried for +the list of members, therefore the membership view is consistent with the +rest of the cluster.

+ +

If the favor option is set to low_latency, the local Ra server is +queried. The query will be faster at the cost of returning a possibly +out-of-date result.

+ + The condition query option is unsupported. +

+ +

nodes/0

+
+

nodes() -> Ret +

+

+

Returns the list of Erlang nodes that are part of the cluster.

+ + Calling this function is the same as calling nodes(StoreId) with the + default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: nodes/1, nodes/2.

+ +

nodes/1

+
+

nodes(StoreIdOrOptions::StoreId | Options) -> Ret +

+

+

Returns the list of Erlang nodes that are part of the cluster.

+ + This function accepts the following two forms: + +

+

See also: nodes/2.

+ +

nodes/2

+
+

nodes(StoreId, Options) -> Ret +

+

+

Returns the list of Erlang nodes that are part of the cluster.

+ +

If the favor option is set to consistency, the Ra leader is queried for +the list of members, therefore the membership view is consistent with the +rest of the cluster.

+ +

If the favor option is set to low_latency, the local Ra server is +queried. The query will be faster at the cost of returning a possibly +out-of-date result.

+ + The condition query option is unsupported. +

+

See also: members/2.

+ +

wait_for_leader/0

+
+

wait_for_leader() -> Ret +

+

+

Waits for a leader to be elected.

+ + Calling this function is the same as calling wait_for_leader(StoreId) + with the default store ID (see khepri_cluster:get_default_store_id/0). +

+

See also: wait_for_leader/1, wait_for_leader/2.

+ +

wait_for_leader/1

+
+

wait_for_leader(StoreIdOrRaServer) -> Ret +

+

+

Waits for a leader to be elected.

+ + Calling this function is the same as calling wait_for_leader(StoreId, + DefaultTimeout) where DefaultTimeout is returned by khepri_app:get_default_timeout/0. +

+

See also: wait_for_leader/2.

+ +

wait_for_leader/2

+
+

wait_for_leader(StoreIdOrRaServer, Timeout) -> Ret +

+

Timeout: the timeout. +
+

+

returns: ok if a leader was elected or an {error, Reason} tuple.

+

Waits for a leader to be elected.

+ + This is useful if you want to be sure the clustered store is ready before + issueing writes and queries. Note that there are obviously no guaranties + that the Raft quorum will be lost just after this call. +

+ +

get_store_ids/0

+
+

get_store_ids() -> [StoreId] +

+

+

Returns the list of running stores.

+ +

is_store_running/1

+
+

is_store_running(StoreId) -> IsRunning +

+

+

Indicates if StoreId is running or not.

+
+ + +

Generated by EDoc

+ + diff --git a/khepri_condition.html b/khepri_condition.html new file mode 100644 index 00000000..1720dc79 --- /dev/null +++ b/khepri_condition.html @@ -0,0 +1,320 @@ + + + + +Module khepri_condition + + + + + + +
+ +

Module khepri_condition

+Condition support. + + +

Description

Condition support.

+ +

Conditions can be used in path patterns and keep_while conditions. They +allow to point to a specific node only if conditions are met, or to match +several tree nodes with a single path pattern.

+ +

A condition is an Erlang record defining a specific property. Some of them +have arguments to further define the condition.

+ +

Path components (atoms and binaries) also act as conditions which check + equality with the path of the tested node. This can be useful for + conditions which compose other conditions: if_not(), + if_all() and if_any().

+ +

Example:

+ +
%% Matches `[stock, wood, <<"birch">>]' but not `[stock, wood, <<"oak">>]'
+[stock, wood, #if_not{condition = <<"oak">>}]
+ + All supported conditions are described in the Data Types + section. +

Data Types

+ +

comparison_op()

+

comparison_op(Type) = {eq, Type} | {ne, Type} | {lt, Type} | {le, Type} | {gt, Type} | {ge, Type}

+

Comparison operator in some condition().

+ +

condition()

+

condition() = if_name_matches() | if_path_matches() | if_has_payload() | if_has_data() | if_has_sproc() | if_data_matches() | if_node_exists() | if_payload_version() | if_child_list_version() | if_child_list_length() | if_not() | if_all() | if_any()

+

All supported conditions.

+ +

if_all()

+

if_all() = #if_all{conditions = [khepri_path:pattern_component()]}

+

Condition. Evaluates to true if all inner conditions evaluate to true.

+ + Record fields: + + + Example: +
#if_all{conditions = [#if_name_matches{regex = "^a"},
+                      #if_has_data{has_data = true}]}.

+ +

if_any()

+

if_any() = #if_any{conditions = [khepri_path:pattern_component()]}

+

Condition. Evaluates to true if any of the inner conditions evaluate to +true.

+ + Record fields: + + + Example: +
#if_any{conditions = [#if_name_matches{regex = "^a"},
+                      #if_has_data{has_data = true}]}.

+ +

if_child_list_length()

+

if_child_list_length() = #if_child_list_length{count = non_neg_integer() | khepri_condition:comparison_op(non_neg_integer())}

+

Condition. Evaluates to true if the tested node's child list size +corresponds to the expected value.

+ + Record fields: + + + Example: +
#if_child_list_length{count = 1}.
+#if_child_list_length{count = {gt, 10}}.

+ +

if_child_list_version()

+

if_child_list_version() = #if_child_list_version{version = khepri:child_list_version() | khepri_condition:comparison_op(khepri:child_list_version())}

+

Condition. Evaluates to true if the tested node's child list version +corresponds to the expected value.

+ + Record fields: + + + Example: +
#if_child_list_version{version = 1}.
+#if_child_list_version{version = {gt, 10}}.

+ +

if_data_matches()

+

if_data_matches() = #if_data_matches{pattern = ets:match_pattern() | term(), conditions = [any()], compiled = ets:comp_match_spec() | undefined}

+

Condition. Evaluates to true if the tested node has a data payload and the + data payload term matches the given pattern and all conditions evaluates +to true.

+ + Record fields: + + + Examples: +
%% The data must be of the form `{user, _}', so a tuple of arity 2 with the
+%% first element being the `user' atom. The second element can be anything.
+#if_data_matches{pattern = {user, '_'}}.
+
%% The data must be of the form `{age, Age}' and `Age' must be an
+%% integer greater than or equal to 18.
+#if_data_matches{pattern = {age, '$1'},
+                 conditions = [{is_integer, '$1'},
+                               {'>=', '$1', 18}]}.
+ + See Match + Specifications in Erlang for a detailed documentation of how it works.

+ +

if_has_data()

+

if_has_data() = #if_has_data{has_data = boolean()}

+

Condition. Evaluates to true if the tested node's data payload presence +corresponds to the expected state.

+ + Record fields: + + +

Data absence is either no payload or a non-data type of payload.

+ + Example: +
#if_has_data{has_data = false}.

+ +

if_has_payload()

+

if_has_payload() = #if_has_payload{has_payload = boolean()}

+

Condition. Evaluates to true if the tested node has any kind of payload.

+ + Record fields: + + + Example: +
#if_has_payload{has_payload = true}.

+ +

if_has_sproc()

+

if_has_sproc() = #if_has_sproc{has_sproc = boolean()}

+

Condition. Evaluates to true if the tested node's stored procedure payload +presence corresponds to the expected state.

+ + Record fields: + + +

Stored procedure absence is either no payload or a non-sproc type of +payload.

+ + Example: +
#if_has_sproc{has_sproc = false}.

+ +

if_name_matches()

+

if_name_matches() = #if_name_matches{regex = any | iodata() | unicode:charlist(), compiled = khepri_condition:re_compile_ret() | undefined}

+

Condition. Evaluates to true if the name of the tested node matches the +condition pattern.

+ + Record fields: + + + Example: +
#if_name_matches{regex = "^user_"}.
+#if_name_matches{regex = any}.

+ +

if_node_exists()

+

if_node_exists() = #if_node_exists{exists = boolean()}

+

Condition. Evaluates to true if the tested node existence corresponds to +the expected state.

+ + Record fields: + + + Example: +
#if_node_exists{exists = false}.

+ +

if_not()

+

if_not() = #if_not{condition = khepri_path:pattern_component()}

+

Condition. Evaluates to true if the inner condition evaluates to false.

+ + Record fields: + + + Example: +
#if_not{condition = #if_name_matches{regex = "^a"}}.

+ +

if_path_matches()

+

if_path_matches() = #if_path_matches{regex = any | iodata() | unicode:charlist(), compiled = khepri_condition:re_compile_ret() | undefined}

+

Condition. Evaluates to true if the name of the tested node matches the +condition pattern. If it does not match, child node names are tested +recursively.

+ + Record fields: + + + Example: +
#if_path_matches{regex = "^user_"}.
+#if_path_matches{regex = any}.

+ +

if_payload_version()

+

if_payload_version() = #if_payload_version{version = khepri:payload_version() | khepri_condition:comparison_op(khepri:payload_version())}

+

Condition. Evaluates to true if the tested node's payload version +corresponds to the expected value.

+ + Record fields: + + + Example: +
#if_payload_version{version = 1}.
+#if_payload_version{version = {gt, 10}}.

+ +

keep_while()

+

keep_while() = #{khepri_path:path() => condition()}

+

An association between a path and a condition. As long as the condition +evaluates to true, the tree node is kept. Once the condition evaluates to +false, the tree node is deleted.

+ +

If the keep_while conditions are false at the time of the insert, the + insert fails. The only exception to that is if the keep_while condition +is on the inserted node itself.

+ +

Paths in the map can be native paths or Unix-like paths. However, having +two entries that resolve to the same node (one native path entry and one +Unix-like path entry for instance) is undefined behavior: one of them will +overwrite the other.

+ + Example: +
khepri:put(
+  StoreId,
+  [foo],
+  Payload,
+  #{keep_while => #{
+    %% The node `[foo]' will be removed as soon as `[bar]' is removed
+    %% because the condition associated with `[bar]' will not be true
+    %% anymore.
+    [bar] => #if_node_exists{exists = true}
+  }}
+).

+ +

native_keep_while()

+

native_keep_while() = #{khepri_path:native_path() => condition()}

+

An association between a native path and a condition.

+ + This is the same as keep_while() but the paths in the map keys were + converted to native paths if necessary.

+ +

re_compile_ret()

+

re_compile_ret() = {ok, {re_pattern, term(), term(), term(), term()}} | {error, {string(), non_neg_integer()}}

+

Return value of re:compile/1.

+ + The opaque compiled regex type, re:mp(), is unfortunately not + exported by re, neither is the error tuple (at least up to + Erlang/OTP 25.1).

+ +

Function Index

+ +
ensure_native_keep_while/1
+ +

Function Details

+ +

ensure_native_keep_while/1

+
+

ensure_native_keep_while(KeepWhile) -> NativeKeepWhile +

+

+
+
+ + +

Generated by EDoc

+ + diff --git a/khepri_evf.html b/khepri_evf.html new file mode 100644 index 00000000..746f3dde --- /dev/null +++ b/khepri_evf.html @@ -0,0 +1,134 @@ + + + + +Module khepri_evf + + + + + + +
+ +

Module khepri_evf

+Khepri event filters. + + +

Description

Khepri event filters. +

Data Types

+ +

event_filter()

+

event_filter() = tree_event_filter()

+

An event filter.

+ + The following event filters are supported: + + + An event filter can be explicitly constructed using the functions provided + in this module. However, some common types will be automatically detected + and converted to an event filter with default properties. See each event + filter type for more details.

+ +

priority()

+

priority() = integer()

+

An event filter priority.

+ +

This is an integer to prioritize event filters: the greater the priority, +the more it is prioritized. Negative integers are allowed.

+ + The default priority is 0.

+ +

tree_event_filter()

+

tree_event_filter() = #evf_tree{path = khepri_path:native_pattern(), props = khepri_evf:tree_event_filter_props()}

+

A tree event filter.

+ + It takes a path pattern to monitor and optionally properties.

+ +

tree_event_filter_props()

+

tree_event_filter_props() = #{on_actions => [create | update | delete], priority => khepri_evf:priority()}

+

Tree event filter properties.

+ + The properties are: + + + A Khepri path, whether it is a native path or a Unix-like path, can be used + as a tree event filter. It will be automatically converted to a tree event + filter with default properties.

+ +

Function Index

+ + + + + +
tree/1Constructs a tree event filter.
tree/2Constructs a tree event filter.
wrap/1Automatically detects the event filter type and ensures it is wrapped + in one of the internal types.
get_priority/1Returns the priority of the event filter.
set_priority/2Sets the priority of the event filter.
+ +

Function Details

+ +

tree/1

+
+

tree(PathPattern) -> EventFilter +

+

+

Constructs a tree event filter. +

+

See also: tree/2.

+ +

tree/2

+
+

tree(PathPattern, Props) -> EventFilter +

+

+

Constructs a tree event filter. +

+

See also: tree_event_filter().

+ +

wrap/1

+
+

wrap(Input) -> EventFilter +

+

Input: an already created event filter, or any term which can be + automatically converted to an event filter. +
+

+

returns: the created event filter.

+

Automatically detects the event filter type and ensures it is wrapped + in one of the internal types. +

+ +

get_priority/1

+
+

get_priority(EventFilter) -> Priority +

+

EventFilter: the event filter to update. +
+

+

returns: the priority.

+

Returns the priority of the event filter. +

+ +

set_priority/2

+
+

set_priority(EventFilter, Priority) -> EventFilter +

+

EventFilter: the event filter to update.
+Priority: the new priority. +
+

+

returns: the updated event filter.

+

Sets the priority of the event filter. +

+
+ + +

Generated by EDoc

+ + diff --git a/khepri_export_erlang.html b/khepri_export_erlang.html new file mode 100644 index 00000000..ffeb271d --- /dev/null +++ b/khepri_export_erlang.html @@ -0,0 +1,40 @@ + + + + +Module khepri_export_erlang + + + + + + +
+ +

Module khepri_export_erlang

+Khepri import/export callback module using Erlang terms formatted as +plain text as its external format. + + +

Description

Khepri import/export callback module using Erlang terms formatted as +plain text as its external format.

+ +

The exported file could be read using file:consult/1 to get back +the list of backup items.

+ +

This callback module takes a filename or an opened file descriptor as its + private data passed to khepri:export/4 and khepri:import/3.

+ + Example: +
ok = khepri:put(StoreId, "/:stock/:wood/Oak", 100).
+ok = khepri:put(StoreId, "/:stock/:wood/Mapple", 55).
+ok = khepri:export(StoreId, khepri_export_erlang, "export.erl").
+ + Content of export.erl: +
{put,[stock,wood,<<"Mapple">>],{p_data,55},#{},#{}}.
+{put,[stock,wood,<<"Oak">>],{p_data,100},#{},#{}}.

+ + +

Generated by EDoc

+ + diff --git a/khepri_import_export.html b/khepri_import_export.html new file mode 100644 index 00000000..4b5bc903 --- /dev/null +++ b/khepri_import_export.html @@ -0,0 +1,142 @@ + + + + +Module khepri_import_export + + + + + + +
+ +

Module khepri_import_export

+Wrapper around Mnesia Backup & Restore callback modules. + +

This module defines the khepri_import_export behaviour.
Required callback functions: open_write/1, write/2, commit_write/1, abort_write/1, open_read/1, read/1, close_read/1.

+ +

Description

Wrapper around Mnesia Backup & Restore callback modules.

+ +

Khepri uses the same callback module as Mnesia to implement its import and + export feature. The Mnesia Backup + & Restore API is described in the Mnesia documentation.

+ + Mnesia doesn't provide an Erlang behavior module to ease development. This + module can be used as the behavior instead. For example: +
-module(my_export_module).
+-behavior(khepri_import_export).
+ 
+-export([open_write/1,
+         write/2,
+         commit_write/1,
+         abort_write/1,
+ 
+         open_read/1,
+         read/1,
+         close_read/1]).
+ +

There are two sets of callback functions to implement: one used during +export (or "backup" in Mnesia terms), one used during import (or "restore" +in Mnesia terms).

+ +

Export callback API

+ +
    +
  1. +

    Module:open_write(Args)

    +

    This function is called at the beginning of an export. It is responsible + for any initialization and must return an {ok, ModulePriv} tuple. + ModulePriv is a private state passed to the following functions.

    +
  2. +
  3. +

    Module:write(ModulePriv, BackupItems)

    +

    This function is called for each subset of the items to export.

    +

    BackupItems is a list of opaque Erlang terms. The callback module + can't depend on the structure of these Erlang terms. Only the fact that it + is a list is guarantied.

    +

    An empty list of BackupItems means this is the last call to + Module:write/2. Otherwise, the way all items to export are split into + several subsets and thus several calls to Module:write/2 is + undefined.

    +

    At the end of each call, the function must return {ok, NewModulePriv}. + This new private state is passed to the subsequent calls.

    +
  4. +
  5. +

    Module:commit_write(ModulePriv)

    +

    This function is called after successful calls to Module:write/2. It + is responsible for doing any cleanup.

    +

    This function can return ok or {ok, Ret} if it wants to return some + Erlang terms to the caller.

    +
  6. +
  7. +

    Module:abort_write(ModulePriv)

    +

    This function is called after failed call to Module:write/2, or if + reading from the Khepri store fails. It is responsible for doing any + cleanup.

    +

    This function can return ok or {ok, Ret} if it wants to return some + Erlang terms to the caller.

    +
  8. +
+ +

Import callback API

+ +
    +
  1. +

    Module:open_read(Args)

    +

    This function is called at the beginning of an import. It is responsible + for any initialization and must return an {ok, ModulePriv} tuple. + ModulePriv is a private state passed to the following functions.

    +
  2. +
  3. +

    Module:read(ModulePriv)

    +

    This function is one or more times until there is nothing left to + import.

    +

    The function must return {ok, BackupItems, NewModulePriv}. This new + private state is passed to the subsequent calls.

    +

    BackupItems is a list of opaque Erlang terms. The callback module + can't depend on the structure of these Erlang terms. The backup items must + be returned exactly as they were at the time of the export and in the same + order.

    +

    An empty list of BackupItems means this is the last batch. + Module:read/1 won't be called anymore. Module:close_read/1 will be + called next instead.

    +
  4. +
  5. +

    Module:close_read(ModulePriv)

    +

    This function is called after the last call to Module:read/1, + successful or not, or if the actual import into the Khepri store fails. It + is responsible for doing any cleanup.

    +

    This function can return ok or {ok, Ret} if it wants to return some + Erlang terms to the caller.

    +
  6. +
+ +

Available callback modules

+ + Khepri comes with khepri_export_erlang. This module offers to + export Khepri tree nodes in a plain text file where backup items are + formatted as Erlang terms. +

Data Types

+ +

backup_item()

+

backup_item() = term()

+

An opaque term passed in a list to Module:write/2 and returned by + Module:read/1.

+ + The callback module must not depend on the content of this term.

+ +

module_priv()

+

module_priv() = any()

+

Private data passed to Module:open_write/1 or Module:open_read/1 +initially.

+ + The actual term is specific to the callback module implementation. The + callback can use this term however it wants. It is returned from most + callback functions and passed to the next one.

+
+ + +

Generated by EDoc

+ + diff --git a/khepri_machine.html b/khepri_machine.html new file mode 100644 index 00000000..2d21bcc1 --- /dev/null +++ b/khepri_machine.html @@ -0,0 +1,299 @@ + + + + +Module khepri_machine + + + + + + +
+ +

Module khepri_machine

+ +Khepri private low-level API. + +

Behaviours: ra_machine.

+ +

Description

+Khepri private low-level API.

+ +

This module exposes the private "low-level" API to the Khepri database and + state machine. Main functions correspond to Ra commands implemented by the + state machine. All functions in khepri are built on top of this +module.

+ +

This module is private. The documentation is still visible because it may +help understand some implementation details. However, this module should +never be called directly outside of Khepri.

+ +

State machine history

+ + + + + + + + + + + + + + + +
VersionWhat changed
0Initial version
1 +
    +
  • Added deduplication mechanism: +
      +
    • new command option protect_against_dups
    • +
    • new commands #dedup{} and #dedup_ack{}
    • +
    • new state field dedups
    • +
  • +
  • Added command #unregister_projections{}. The previous + #unregister_projection{} is still supported for backward-compatibility but + it is no longer created.
  • +
+
2 +
    +
  • Changed the data structure for the reverse index used to track + keep-while conditions to be a prefix tree (see khepri_prefix_tree). +
  • +
+
+

Data Types

+ +

async_ret()

+

async_ret() = ok

+ + +

command()

+

command() = #put{path = khepri_path:native_pattern(), payload = khepri_payload:payload(), options = khepri:tree_options() | khepri:put_options()} | #delete{path = khepri_path:native_pattern(), options = khepri:tree_options()} | #tx{'fun' = horus:horus_fun() | khepri_path:pattern(), args = list()} | #register_trigger{id = khepri:trigger_id(), event_filter = khepri_evf:event_filter(), sproc = khepri_path:native_path()} | #ack_triggered{triggered = [khepri_machine:triggered()]} | #register_projection{pattern = khepri_path:native_pattern(), projection = khepri_projection:projection()} | #unregister_projections{names = all | [khepri_projection:name()]} | #dedup{ref = reference(), expiry = integer(), command = khepri_machine:command()} | #dedup_ack{ref = reference()}

+

Commands specific to this Ra machine.

+ +

common_ret()

+

common_ret() = khepri:ok(khepri_adv:node_props_map()) | khepri:error()

+ + +

dedups_map()

+

dedups_map() = #{reference() => {any(), integer()}}

+

Map to handle command deduplication.

+ +

machine_config()

+

machine_config() = #config{store_id = khepri:store_id(), member = ra:server_id(), snapshot_interval = non_neg_integer()}

+

Configuration record, holding read-only or rarely changing fields.

+ +

machine_init_args()

+

machine_init_args() = #{store_id := khepri:store_id(), member := ra:server_id(), snapshot_interval => non_neg_integer(), commands => [command()], atom() => any()}

+

Structure passed to init/1.

+ +

metrics()

+

metrics() = #{applied_command_count => non_neg_integer()}

+

Internal state machine metrics.

+ +

old_command()

+

old_command() = #unregister_projection{name = khepri_projection:name()}

+

Old commands that are still accepted by the Ra machine but never created.

+ +

Even though Khepri no longer creates these commands, they may still be +present in existing Ra log files and thus be applied after an ugprade of +Khepri.

+ + We keep them supported for backward-compatibility.

+ +

projection_map()

+

projection_map() = #{khepri_projection:name() => khepri_path:pattern()}

+

A mapping between the names of projections and patterns to which each + projection is registered.

+ +

projection_tree()

+

projection_tree() = khepri_pattern_tree:tree([khepri_projection:projection()])

+

A pattern tree that holds all registered projections in the machine's state.

+ +

props()

+

props() = #{payload_version := khepri:payload_version(), child_list_version := khepri:child_list_version()}

+

Properties attached to each node in the tree structure.

+ +

state()

+

state() = state_v1() | khepri_machine_v0:state()

+

State of this Ra state machine.

+ +

state_v1()

+

abstract datatype: state_v1()

+

State of this Ra state machine, version 1.

+ + Note that this type is used also for machine version 2. Machine version 2 + changes the type of an opaque member of the khepri_tree record and + doesn't need any changes to the khepri_machine type. See the moduledoc of + this module for more information about version 2.

+ +

triggered()

+

triggered() = #triggered{id = khepri:trigger_id(), event_filter = khepri_evf:event_filter(), sproc = horus:horus_fun(), props = map()}

+ + +

triggers_map()

+

triggers_map() = #{khepri:trigger_id() => #{sproc := khepri_path:native_path(), event_filter := khepri_evf:event_filter()}}

+

Internal triggers map in the machine state.

+ +

tx_ret()

+

tx_ret() = khepri:ok(khepri_tx:tx_fun_result()) | khepri_tx:tx_abort() | no_return()

+ + +

Function Index

+ + + + + + + + + + +
fold/5Returns all tree nodes matching the given path pattern.
fence/2Blocks until all updates received by the cluster leader are applied + locally.
delete/3Deletes all tree nodes matching the path pattern.
transaction/5Runs a transaction and returns the result.
handle_tx_exception/1
register_trigger/5Registers a trigger.
register_projection/4Registers a projection.
unregister_projections/3Removes the given projections from the store.
version/0Returns the state machine version.
which_module/1Returns the state machine module corresponding to the given version.
+ +

Function Details

+ +

fold/5

+
+

fold(StoreId, PathPattern, Fun, Acc, Options) -> Ret +

+

StoreId: the name of the Ra cluster.
+PathPattern: the path (or path pattern) to the nodes to get.
+Options: query options such as favor. +
+

+

returns: an {ok, NodePropsMap} tuple with a map with zero, one or more + entries, or an {error, Reason} tuple.

+

Returns all tree nodes matching the given path pattern. +

+ +

fence/2

+
+

fence(StoreId, Timeout) -> Ret +

+

StoreId: the name of the Ra cluster
+Timeout: the time limit after which the call returns with an error. +
+

+

returns: ok or an {error, Reason} tuple.

+

Blocks until all updates received by the cluster leader are applied + locally. +

+ +

delete/3

+
+

delete(StoreId, PathPattern, Options) -> Ret +

+

StoreId: the name of the Ra cluster.
+PathPattern: the path (or path pattern) to the nodes to delete.
+Options: command options such as the command type. +
+

+

returns: in the case of a synchronous delete, an {ok, NodePropsMap} tuple + with a map with zero, one or more entries, or an {error, Reason} tuple; + in the case of an asynchronous put, always ok (the actual return value + may be sent by a message if a correlation ID was specified).

+

Deletes all tree nodes matching the path pattern. +

+ +

transaction/5

+
+

transaction(StoreId, FunOrPath, Args, ReadWrite, Options) -> Ret +

+

StoreId: the name of the Ra cluster.
+FunOrPath: an arbitrary anonymous function or a path pattern pointing + to a stored procedure.
+Args: a list of arguments to pass to FunOrPath.
+ReadWrite: the read/write or read-only nature of the transaction.
+Options: command options such as the command type. +
+

+

returns: in the case of a synchronous transaction, {ok, Result} where + Result is the return value of FunOrPath, or {error, Reason} if the + anonymous function was aborted; in the case of an asynchronous transaction, + always ok (the actual return value may be sent by a message if a + correlation ID was specified).

+

Runs a transaction and returns the result. +

+ +

handle_tx_exception/1

+
+

handle_tx_exception(X1) -> any()

+

+
+ +

register_trigger/5

+
+

register_trigger(StoreId, TriggerId, EventFilter, StoredProcPath, Options) -> Ret +

+

StoreId: the name of the Ra cluster.
+TriggerId: the name of the trigger.
+EventFilter: the event filter used to associate an event with a + stored procedure.
+StoredProcPath: the path to the stored procedure to execute when the + corresponding event occurs. +
+

+

returns: ok if the trigger was registered, an {error, Reason} tuple + otherwise.

+

Registers a trigger. +

+ +

register_projection/4

+
+

register_projection(StoreId, PathPattern, Projection, Options) -> Ret +

+

StoreId: the name of the Ra cluster.
+PathPattern: the pattern of tree nodes which should be projected.
+Projection: the projection record created with khepri_projection:new/3.
+Options: command options such as the command type. +
+

+

returns: ok if the projection was registered, an {error, Reason} tuple + otherwise.

+

Registers a projection. +

+ +

unregister_projections/3

+
+

unregister_projections(StoreId, Names, Options) -> Ret +

+

StoreId: the name of the Ra cluster.
+Names: the names of projections to unregister or the atom all to + remove all projections.
+Options: command options such as the command type. +
+

+

returns: {ok, ProjectionMap} if the command succeeds, {error, Reason} + otherwise. The ProjectionMap is a map with projection names (khepri_projection:name() keys associated to the pattern to which each + projection was registered.

+

Removes the given projections from the store.

+ + Names may either be a list of projection names to remove or the atom + all. When all is passed, every projection in the store is removed. +

+ +

version/0

+
+

version() -> MacVer +

+

+

Returns the state machine version.

+ +

which_module/1

+
+

which_module(MacVer) -> Module +

+

+

Returns the state machine module corresponding to the given version.

+
+ + +

Generated by EDoc

+ + diff --git a/khepri_path.html b/khepri_path.html new file mode 100644 index 00000000..54ec8059 --- /dev/null +++ b/khepri_path.html @@ -0,0 +1,280 @@ + + + + +Module khepri_path + + + + + + +
+ +

Module khepri_path

+Khepri path API. + + +

Description

Khepri path API.

+ +

A path is the type used by Khepri to reference nodes in the tree structure. +A path describes how to reach a node from the root node.

+ +

A path, or native path, is a list of components. Components can be +Erlang atoms and binaries. Example:

+ +
%% Native path.
+Path = [stock, wood, <<"oak">>].
+ +

A path may contain conditions to tune how a node is matched or to match + multiple nodes at once. This is called a path pattern. A path + pattern may contain conditions in addition to regular components (Erlang + atoms and binaries). See khepri_condition to learn more about +conditions. Example:

+ +
%% Path pattern with a condition on `wood'.
+PathPattern = [stock,
+               #if_all{conditions = [wood,
+                                     #if_node_exists{exists = true}]},
+               oak].
+ +

To be user-friendly, string-based and binary-based Unix-like paths + are accepted by most functions. The syntax of these Unix paths is + described in the unix_path() type documentation. Example:

+ +
%% Unix path, equivalent of the first native path example.
+UnixPath = "/:stock/:wood/oak".
+

Data Types

+ +

component()

+

component() = node_id() | 47 | 46 | 94

+

Component name in a path to a node.

+ +

native_path()

+

native_path() = [component()]

+

Native path to a node.

+ +

A native path is a list of atoms, binaries and special components.

+ +

It is called native because it requires no further processing + (unlike unix_path()) and is the format used internally by the state +machine.

+ + Special components are: +
    +
  1. ?KHEPRI_ROOT_NODE to explicitly mark the root node. A path is absolute + by default. Using ?KHEPRI_ROOT_NODE is only useful when manipulating the + root node itself (querying it or storing something in the root node).
  2. +
  3. ?THIS_KHEPRI_NODE to make a relative path (the default being an + absolute path). This is mostly useful for khepri_condition:keep_while() to make it easy to put a condition on the + node itself.
  4. +
  5. ?PARENT_KHEPRI_NODE to target the parent of a node, with the same + benefits and use cases as ?THIS_KHEPRI_NODE.
  6. +
+ +

Example:

+ +
%% Native path.
+Path = [stock, wood, <<"oak">>].

+ +

native_pattern()

+

native_pattern() = [pattern_component()]

+

Path pattern which may match zero, one or more nodes.

+ +

A native pattern is a list of atoms, binaries, special components and +conditions.

+ +

It is called native because it requires no further processing + (unlike unix_pattern()) and is the format used internally by the +state machine.

+ +

See native_path() for a description of special components.

+ +

Conditions are any condition defined by khepri_condition:condition().

+ +

Example:

+ +
%% Path pattern with a condition on `wood'.
+PathPattern = [stock,
+               #if_all{conditions = [wood,
+                                     #if_node_exists{exists = true}]},
+               oak].

+ +

node_id()

+

node_id() = atom() | binary()

+

A node name.

+ +

path()

+

path() = native_path() | unix_path()

+

Path to a node.

+ +

pattern()

+

pattern() = native_pattern() | unix_pattern()

+

Path pattern which may match zero, one or more nodes.

+ +

pattern_component()

+

pattern_component() = component() | khepri_condition:condition()

+

Path pattern component which may match zero, one or more nodes.

+ +

unix_path()

+

unix_path() = string() | binary()

+

Unix-like path to a node.

+ +

These Unix paths have the following syntax:

+ + + +

Warning: There is no special handling of Unicode in tree +node names. To use Unicode, it is recommended to either use a native path or +a binary-based Unix-like path. If using a string-based Unix-like path, the +behavior is undefined and the call may crash. Matching against node names is +also undefined behavior and may crash, regardless of the type of path being +used. It will be improved in the future.

+ + Example: +
%% Unix path, equivalent of the first native path example.
+UnixPath = "/:stock/:wood/oak".

+ +

unix_pattern()

+

unix_pattern() = string() | binary()

+

Unix-like path pattern to a node.

+ + It accepts the following special characters: +
    +
  1. * anywhere in a path component behaves like a khepri_condition:if_name_matches().
  2. +
  3. ** as a path component behaves like a khepri_condition:if_path_matches().
  4. +
+ +

A Unix-like path pattern can't express all the conditions of a native path +pattern currently.

+ +

Otherwise it works as a unix_path() and has the same syntax and +limitations.

+ + Example: +
%% Unix path pattern, matching multiple types of oak.
+UnixPathPattern = "/:stock/:wood/*oak".

+ +

Function Index

+ + + + + + + + + + + +
from_string/1Converts a Unix-like path to a native path.
from_binary/1Converts a Unix-like path to a native path.
to_string/1Converts a native path to a string.
to_binary/1Converts a native path to a binary.
combine_with_conditions/2
targets_specific_node/1
is_valid/1
ensure_is_valid/1
abspath/2
realpath/1
pattern_includes_root_node/1
+ +

Function Details

+ +

from_string/1

+
+

from_string(String) -> PathPattern +

+

+

Converts a Unix-like path to a native path.

+ +

The Unix-like string can be either an Erlang string or an Erlang binary.

+ + For convenience, a native path is also accepted and returned as-is.

+ +

from_binary/1

+
+

from_binary(String) -> PathPattern +

+

+

Converts a Unix-like path to a native path.

+ + This is the same as calling from_string(String). Therefore, it accepts + Erlang strings or binaries and native paths. +

+

See also: from_string/1.

+ +

to_string/1

+
+

to_string(NativePath) -> UnixPath +

+

+

Converts a native path to a string.

+ +

to_binary/1

+
+

to_binary(NativePath) -> UnixPath +

+

+

Converts a native path to a binary.

+ +

combine_with_conditions/2

+
+

combine_with_conditions(PathPattern, Conditions) -> PathPattern +

+

+
+ +

targets_specific_node/1

+
+

targets_specific_node(PathPattern) -> Ret +

+

+
+ +

is_valid/1

+
+

is_valid(PathPattern) -> IsValid +

+

+
+ +

ensure_is_valid/1

+
+

ensure_is_valid(PathPattern) -> ok | no_return() +

+

+
+ +

abspath/2

+
+

abspath(Path, BasePath) -> Path +

+

+
+ +

realpath/1

+
+

realpath(Path) -> Path +

+

+
+ +

pattern_includes_root_node/1

+
+

pattern_includes_root_node(Path) -> any()

+

+
+
+ + +

Generated by EDoc

+ + diff --git a/khepri_payload.html b/khepri_payload.html new file mode 100644 index 00000000..9bb678e7 --- /dev/null +++ b/khepri_payload.html @@ -0,0 +1,118 @@ + + + + +Module khepri_payload + + + + + + +
+ +

Module khepri_payload

+Khepri payloads. + + +

Description

Khepri payloads.

+ + Payloads are the structure used to attach something to a tree node in the + store. Khepri supports the following payloads: + + + Usually, there is no need to explicitly use this module as the type of + payload will be autodetected, thanks to the wrap/1 function already + called internally. +

Data Types

+ +

data()

+

data() = #p_data{data = khepri:data()}

+

Internal structure to wrap any Erlang term before it can be stored in a +tree node.

+ + The only constraint is the conversion to an Erlang binary must be supported + by this term.

+ +

no_payload()

+

no_payload() = '$__NO_PAYLOAD__'

+

Internal value used to mark that a tree node has no payload attached.

+ +

payload()

+

payload() = no_payload() | data() | sproc()

+

All types of payload stored in the nodes of the tree structure.

+ + Beside the absence of payload, the only type of payload supported is data.

+ +

sproc()

+

sproc() = #p_sproc{sproc = horus:horus_fun(), is_valid_as_tx_fun = ro | rw | false}

+

Internal structure to wrap an anonymous function before it can be stored in + a tree node and later executed.

+ +

Function Index

+ + + + +
none/0Returns the internal value used to mark that a tree node has no + payload attached.
data/1Returns the same term wrapped into an internal structure ready to be + stored in the tree.
sproc/1Returns the same function wrapped into an internal structure ready to + be stored in the tree.
wrap/1Automatically detects the payload type and ensures it is wrapped in +one of the internal types.
+ +

Function Details

+ +

none/0

+
+

none() -> no_payload()

+

+

Returns the internal value used to mark that a tree node has no + payload attached. +

+

See also: no_payload().

+ +

data/1

+
+

data(Term) -> Payload +

+

+

Returns the same term wrapped into an internal structure ready to be + stored in the tree. +

+

See also: data().

+ +

sproc/1

+
+

sproc(Fun) -> Payload +

+

+

Returns the same function wrapped into an internal structure ready to + be stored in the tree. +

+

See also: sproc().

+ +

wrap/1

+
+

wrap(Payload) -> WrappedPayload +

+

Payload: an already wrapped payload, or any term which needs to be + wrapped. +
+

+

returns: the wrapped payload.

+

Automatically detects the payload type and ensures it is wrapped in +one of the internal types.

+ + The internal types make sure we avoid any collision between any + user-provided terms and internal structures. +

+
+ + +

Generated by EDoc

+ + diff --git a/khepri_prefix_tree.html b/khepri_prefix_tree.html new file mode 100644 index 00000000..a9a86350 --- /dev/null +++ b/khepri_prefix_tree.html @@ -0,0 +1,99 @@ + + + + +Module khepri_prefix_tree + + + + + + +
+ +

Module khepri_prefix_tree

+ + + +

Data Types

+ +

tree()

+

abstract datatype: tree(Payload)

+ + +

Function Index

+ + + + + + +
empty/0Returns a new empty tree.
is_prefix_tree/1Determines whether the given term is a prefix tree.
from_map/1Converts a map of paths to payloads into a prefix tree.
fold_prefixes_of/4Folds over all nodes in the tree which are prefixed by the given Path + building an accumulated value with the given fold function and initial + accumulator.
find_path/2Returns the payload associated with a path in the tree.
update/3Updates a given path in the tree.
+ +

Function Details

+ +

empty/0

+
+

empty() -> tree(term())

+

+

Returns a new empty tree. +

+

See also: tree().

+ +

is_prefix_tree/1

+
+

is_prefix_tree(Prefix_tree::tree(term())) -> true

is_prefix_tree(Prefix_tree::term()) -> false

+

+

+

Determines whether the given term is a prefix tree.

+ +

from_map/1

+
+

from_map(Map) -> Tree +

+

+

Converts a map of paths to payloads into a prefix tree.

+ +

fold_prefixes_of/4

+
+

fold_prefixes_of(Fun, Acc, Path, Tree) -> Ret +

+

+

Folds over all nodes in the tree which are prefixed by the given Path + building an accumulated value with the given fold function and initial + accumulator.

+ +

find_path/2

+
+

find_path(Path, Tree) -> Ret +

+

+

returns: {ok, Payload} where Payload is associated with the given path + or error if the path is not associated with a payload in the given tree.

+

Returns the payload associated with a path in the tree. +

+ +

update/3

+
+

update(Fun, Path, Tree) -> Ret +

+

+

Updates a given path in the tree.

+ +

This function can be used to create, update or delete tree nodes. If the + tree node does not exist for the given path, the update function is passed + ?NO_PAYLOAD. If the update function returns ?NO_PAYLOAD then the tree +node and all of its ancestors which do not have a payload or children are +removed.

+ + The update function is also be passed ?NO_PAYLOAD if a tree node exists + but does not have a payload: being passed ?NO_PAYLOAD is not a reliable + sign that a tree node did not exist prior to an update.

+
+ + +

Generated by EDoc

+ + diff --git a/khepri_projection.html b/khepri_projection.html new file mode 100644 index 00000000..42a068bb --- /dev/null +++ b/khepri_projection.html @@ -0,0 +1,163 @@ + + + + +Module khepri_projection + + + + + + +
+ +

Module khepri_projection

+Khepri projections. + + +

Description

Khepri projections

+ +

Projections build a replicated ETS table using tree nodes from the store + which match a khepri_path:pattern(). When a tree node matching a + projection's pattern is changed in the store, the tree node is passed + through the projection's projection_fun() to create record(s). +These records are then stored in the projection's ETS table for all members +in a Khepri cluster.

+ +

Projections provide a way to query the store as fast as possible and are +appropriate for lookups which require low latency and/or high throughput. +Projection tables contain all records matching the pattern, though, so the +memory footprint of a projection table grows with the number of tree nodes +in the store matching the pattern.

+ +

Projection ETS tables are owned by the Khepri cluster and are deleted when +the cluster stops.

+ + Updates to projection tables are immediately consistent for the member of + the cluster on which the change to the store is made and the leader member + but are eventually consistent for all other followers. +

Data Types

+ +

extended_projection_fun()

+

extended_projection_fun() = fun((Table::ets:tid(), Path::khepri_path:native_path(), OldPayload::khepri:node_props(), NewPayload::khepri:node_props()) -> any())

+

An extended projection function.

+ +

In some cases, a tree node in the store might correspond to many objects in +a projection table. Extended projection functions are allowed to call ETS +functions directly in order to build the projection table.

+ +

OldPayload or NewPayload are empty maps if there is no tree node. For + example, a newly created tree node will have an empty map for OldPayload + and a khepri:node_props() map with values for NewPayload.

+ +

This function is compiled like a transaction function except that calls + to the ets module are allowed.

+ + The return value of this function is ignored.

+ +

name()

+

name() = atom()

+

The name of a projection. +

+ +

options()

+

options() = #{type => ets:table_type(), keypos => pos_integer(), read_concurrency => boolean(), write_concurrency => boolean() | auto, compressed => boolean(), standalone_fun_options => horus:options()}

+

Options which control the created ETS table.

+ +

If provided, standalone_fun_options are merged with defaults and passed to + horus:to_standalone_fun/2. The remaining options are a subset of the + options available to ets:new/2. Refer to the ets:new/2 +documentation for a reference on each type and available values.

+ + When a projection is created from a simple_projection_fun(), the + type option may only be set or ordered_set: bag types are not + allowed. extended_projection_fun()s may use any valid ets:table_type().

+ +

projection()

+

projection() = #khepri_projection{name = atom(), projection_fun = copy | horus:horus_fun(), ets_options = [atom() | tuple()]}

+

A projection resource. +

+ +

projection_fun()

+

projection_fun() = copy | simple_projection_fun() | extended_projection_fun()

+

A function that formats an entry in the tree into a record to be stored in a +projection.

+ + Projection functions may either be: + + + The projection function is executed directly by the Ra server process. The + function should be as simple and fast as possible to avoid slowing down the + server.

+ +

simple_projection_fun()

+

simple_projection_fun() = fun((Path::khepri_path:native_path(), Payload::khepri:data()) -> Record::tuple())

+

A simple projection function.

+ +

Simple projection functions only take the path and payload for a tree node +in the store. The record produced by the function is used to create and +delete objects in the ETS table.

+ + This function is compiled the same way as a transaction function: all + side-effects are not allowed. Additionally, for any Path and Payload + inputs, this function must consistently return the same Record.

+ +

Function Index

+ + + +
new/2
new/3Creates a new projection data structure.
name/1Returns the name of the given Projection.
+ +

Function Details

+ +

new/2

+
+

new(Name, ProjectionFun) -> Projection +

+

+
+

See also: khepri:register_projection/4, khepri_projection:new/3, khepri_projection:new/3.

+ +

new/3

+
+

new(Name, ProjectionFun, Options) -> Projection +

+

Name: the name of the projection. This corresponds to the name of + the ETS table which is created when the projection is registered.
+ProjectionFun: the function which turns paths and data into records + to be stored in the projection table.
+Options: options which control properties of the projection table. +
+

+

returns: a projection() resource.

+

Creates a new projection data structure.

+ + This function throws an error in the shape of {unexpected_option, Key, + Value} when an unknown or invalid option() is passed. For example, + if the passed ProjectionFun is a simple_projection_fun() and the + Options map sets the type to bag, this function throws + {unexpected_option, type, bag} since bag is not valid for simple + projection funs. +

+ +

name/1

+
+

name(Projection) -> Name +

+

+

Returns the name of the given Projection.

+
+ + +

Generated by EDoc

+ + diff --git a/khepri_tx.html b/khepri_tx.html new file mode 100644 index 00000000..9396322f --- /dev/null +++ b/khepri_tx.html @@ -0,0 +1,723 @@ + + + + +Module khepri_tx + + + + + + +
+ +

Module khepri_tx

+Khepri API for transactional queries and updates. + + +

Description

Khepri API for transactional queries and updates.

+ +

Transactions are anonymous functions which take no arguments, much like +what Mnesia supports. However, unlike with Mnesia, transaction functions in +Khepri are restricted:

+ + + +

The reason is that the transaction function must always have the exact same +outcome given its inputs. Indeed, the transaction function is executed on +every Ra cluster members participating in the consensus. The function must +therefore modify the Khepri state (the database) identically on all Ra +members. This is also true for Ra members joining the cluster later or +catching up after a network partition.

+ + To achieve that: +
    +
  1. The code of the transaction function is extracted from the its initial + Erlang module. This way, the transaction function does not depend on the + initial module availability and is not affected by a module reload. See + Horus documentation
  2. +
  3. The code is verified to make sure it does not perform any denied + operations.
  4. +
  5. The extracted transaction function is stored as a Khepri state machine + command in the Ra journal to be replicated on all Ra members.
  6. +
+ + Functions in this module have simplified return values to cover most + frequent use cases. If you need more details about the queried or modified + tree nodes, like the ability to distinguish a non-existent tree node from a + tree node with no payload, you can use the khepri_tx_adv module. +

Data Types

+ +

tx_abort()

+

tx_abort() = khepri:error(any())

+

Return value after a transaction function aborted.

+ +

tx_fun()

+

tx_fun() = function()

+

Transaction function signature.

+ +

tx_fun_result()

+

tx_fun_result() = any() | no_return()

+

Return value of a transaction function.

+ +

Function Index

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
is_empty/0Indicates if the store is empty or not.
is_empty/1Indicates if the store is empty or not.
get/1Returns the payload of the tree node pointed to by the given path +pattern.
get/2Returns the payload of the tree node pointed to by the given path +pattern.
get_or/2Returns the payload of the tree node pointed to by the given path +pattern, or a default value.
get_or/3Returns the payload of the tree node pointed to by the given path +pattern, or a default value.
get_many/1Returns payloads of all the tree nodes matching the given path +pattern.
get_many/2Returns payloads of all the tree nodes matching the given path +pattern.
get_many_or/2Returns payloads of all the tree nodes matching the given path +pattern, or a default payload.
get_many_or/3Returns payloads of all the tree nodes matching the given path +pattern, or a default payload.
exists/1Indicates if the tree node pointed to by the given path exists or not.
exists/2Indicates if the tree node pointed to by the given path exists or not.
has_data/1Indicates if the tree node pointed to by the given path has data or +not.
has_data/2Indicates if the tree node pointed to by the given path has data or +not.
is_sproc/1Indicates if the tree node pointed to by the given path holds a stored +procedure or not.
is_sproc/2Indicates if the tree node pointed to by the given path holds a stored +procedure or not.
count/1Counts all tree nodes matching the given path pattern.
count/2Counts all tree nodes matching the given path pattern.
fold/3Calls Fun on successive tree nodes matching the given path pattern, + starting with Acc.
fold/4Calls Fun on successive tree nodes matching the given path pattern, + starting with Acc.
foreach/2Calls Fun for each tree node matching the given path pattern.
foreach/3Calls Fun for each tree node matching the given path pattern.
map/2Produces a new map by calling Fun for each tree node matching the +given path pattern.
map/3Produces a new map by calling Fun for each tree node matching the +given path pattern.
filter/2Returns a map for which predicate Pred holds true in tree nodes +matching the given path pattern.
filter/3Returns a map for which predicate Pred holds true in tree nodes +matching the given path pattern.
put/2Sets the payload of the tree node pointed to by the given path +pattern.
put/3Sets the payload of the tree node pointed to by the given path +pattern.
put_many/2Sets the payload of all the tree nodes matching the given path pattern.
put_many/3Sets the payload of all the tree nodes matching the given path pattern.
create/2Creates a tree node with the given payload.
create/3Creates a tree node with the given payload.
update/2Updates an existing tree node with the given payload.
update/3Updates an existing tree node with the given payload.
compare_and_swap/3Updates an existing tree node with the given payload only if its data +matches the given pattern.
compare_and_swap/4Updates an existing tree node with the given payload only if its data +matches the given pattern.
delete/1Deletes the tree node pointed to by the given path pattern.
delete/2Deletes the tree node pointed to by the given path pattern.
delete_many/1Deletes all tree nodes matching the given path pattern.
delete_many/2Deletes all tree nodes matching the given path pattern.
clear_payload/1Deletes the payload of the tree node pointed to by the given path +pattern.
clear_payload/2Deletes the payload of the tree node pointed to by the given path +pattern.
clear_many_payloads/1Deletes the payload of all tree nodes matching the given path pattern.
clear_many_payloads/2Deletes the payload of all tree nodes matching the given path pattern.
abort/1Aborts the transaction.
is_transaction/0Indicates if the calling function runs in the context of a transaction + function.
+ +

Function Details

+ +

is_empty/0

+
+

is_empty() -> IsEmpty | Error +

+

+

Indicates if the store is empty or not.

+ + This is the same as khepri:is_empty/1 but inside the context of a + transaction function. +

+

See also: is_empty/1, khepri:is_empty/2.

+ +

is_empty/1

+
+

is_empty(Options) -> IsEmpty | Error +

+

+

Indicates if the store is empty or not.

+ + This is the same as khepri:is_empty/2 but inside the context of a + transaction function. +

+

See also: khepri:is_empty/2.

+ +

get/1

+
+

get(PathPattern) -> Ret +

+

+

Returns the payload of the tree node pointed to by the given path +pattern.

+ + This is the same as khepri:get/2 but inside the context of a + transaction function. +

+

See also: khepri:get/2.

+ +

get/2

+
+

get(PathPattern, Options) -> Ret +

+

+

Returns the payload of the tree node pointed to by the given path +pattern.

+ + This is the same as khepri:get/3 but inside the context of a + transaction function. +

+

See also: khepri:get/3.

+ +

get_or/2

+
+

get_or(PathPattern, Default) -> Ret +

+

+

Returns the payload of the tree node pointed to by the given path +pattern, or a default value.

+ + This is the same as khepri:get_or/3 but inside the context of a + transaction function. +

+

See also: khepri:get_or/3.

+ +

get_or/3

+
+

get_or(PathPattern, Default, Options) -> Ret +

+

+

Returns the payload of the tree node pointed to by the given path +pattern, or a default value.

+ + This is the same as khepri:get_or/4 but inside the context of a + transaction function. +

+

See also: khepri:get_or/4.

+ +

get_many/1

+
+

get_many(PathPattern) -> Ret +

+

+

Returns payloads of all the tree nodes matching the given path +pattern.

+ + This is the same as khepri:get_many/2 but inside the context of a + transaction function. +

+

See also: khepri:get_many/2.

+ +

get_many/2

+
+

get_many(PathPattern, Options) -> Ret +

+

+

Returns payloads of all the tree nodes matching the given path +pattern.

+ + This is the same as khepri:get_many/3 but inside the context of a + transaction function. +

+

See also: khepri:get_many/3.

+ +

get_many_or/2

+
+

get_many_or(PathPattern, Default) -> Ret +

+

+

Returns payloads of all the tree nodes matching the given path +pattern, or a default payload.

+ + This is the same as khepri:get_many_or/3 but inside the context of a + transaction function. +

+

See also: khepri:get_many_or/3.

+ +

get_many_or/3

+
+

get_many_or(PathPattern, Default, Options) -> Ret +

+

+

Returns payloads of all the tree nodes matching the given path +pattern, or a default payload.

+ + This is the same as khepri:get_many_or/4 but inside the context of a + transaction function. +

+

See also: khepri:get_many_or/4.

+ +

exists/1

+
+

exists(PathPattern) -> Exists +

+

+

Indicates if the tree node pointed to by the given path exists or not.

+ + This is the same as khepri:exists/2 but inside the context of a + transaction function. +

+

See also: khepri:exists/2.

+ +

exists/2

+
+

exists(PathPattern, Options) -> Exists +

+

+

Indicates if the tree node pointed to by the given path exists or not.

+ + This is the same as khepri:exists/3 but inside the context of a + transaction function. +

+

See also: khepri:exists/3.

+ +

has_data/1

+
+

has_data(PathPattern) -> HasData | Error +

+

+

Indicates if the tree node pointed to by the given path has data or +not.

+ + This is the same as khepri:has_data/2 but inside the context of a + transaction function. +

+

See also: khepri:has_data/2.

+ +

has_data/2

+
+

has_data(PathPattern, Options) -> HasData | Error +

+

+

Indicates if the tree node pointed to by the given path has data or +not.

+ + This is the same as khepri:has_data/3 but inside the context of a + transaction function. +

+

See also: khepri:has_data/3.

+ +

is_sproc/1

+
+

is_sproc(PathPattern) -> IsSproc | Error +

+

+

Indicates if the tree node pointed to by the given path holds a stored +procedure or not.

+ + This is the same as khepri:is_sproc/2 but inside the context of a + transaction function. +

+

See also: khepri:is_sproc/2.

+ +

is_sproc/2

+
+

is_sproc(PathPattern, Options) -> IsSproc | Error +

+

+

Indicates if the tree node pointed to by the given path holds a stored +procedure or not.

+ + This is the same as khepri:is_sproc/3 but inside the context of a + transaction function. +

+

See also: khepri:is_sproc/3.

+ +

count/1

+
+

count(PathPattern) -> Ret +

+

+

Counts all tree nodes matching the given path pattern.

+ + This is the same as khepri:count/2 but inside the context of a + transaction function. +

+

See also: khepri:count/2.

+ +

count/2

+
+

count(PathPattern, Options) -> Ret +

+

+

Counts all tree nodes matching the given path pattern.

+ + This is the same as khepri:count/3 but inside the context of a + transaction function. +

+

See also: khepri:count/3.

+ +

fold/3

+
+

fold(PathPattern, Fun, Acc) -> Ret +

+

+

Calls Fun on successive tree nodes matching the given path pattern, + starting with Acc.

+ + This is the same as khepri:fold/4 but inside the context of a + transaction function. +

+

See also: khepri:fold/4.

+ +

fold/4

+
+

fold(PathPattern, Fun, Acc, Options) -> Ret +

+

+

Calls Fun on successive tree nodes matching the given path pattern, + starting with Acc.

+ + This is the same as khepri:fold/5 but inside the context of a + transaction function. +

+

See also: khepri:fold/5.

+ +

foreach/2

+
+

foreach(PathPattern, Fun) -> Ret +

+

+

Calls Fun for each tree node matching the given path pattern.

+ + This is the same as khepri:foreach/3 but inside the context of a + transaction function. +

+

See also: khepri:foreach/3.

+ +

foreach/3

+
+

foreach(PathPattern, Fun, Options) -> Ret +

+

+

Calls Fun for each tree node matching the given path pattern.

+ + This is the same as khepri:foreach/4 but inside the context of a + transaction function. +

+

See also: khepri:foreach/4.

+ +

map/2

+
+

map(PathPattern, Fun) -> Ret +

+

+

Produces a new map by calling Fun for each tree node matching the +given path pattern.

+ + This is the same as khepri:map/3 but inside the context of a + transaction function. +

+

See also: khepri:map/3.

+ +

map/3

+
+

map(PathPattern, Fun, Options) -> Ret +

+

+

Produces a new map by calling Fun for each tree node matching the +given path pattern.

+ + This is the same as khepri:map/4 but inside the context of a + transaction function. +

+

See also: khepri:map/4.

+ +

filter/2

+
+

filter(PathPattern, Pred) -> Ret +

+

+

Returns a map for which predicate Pred holds true in tree nodes +matching the given path pattern.

+ + This is the same as khepri:filter/3 but inside the context of a + transaction function. +

+

See also: khepri:filter/3.

+ +

filter/3

+
+

filter(PathPattern, Pred, Options) -> Ret +

+

+

Returns a map for which predicate Pred holds true in tree nodes +matching the given path pattern.

+ + This is the same as khepri:filter/4 but inside the context of a + transaction function. +

+

See also: khepri:filter/4.

+ +

put/2

+
+

put(PathPattern, Data) -> Ret +

+

+

Sets the payload of the tree node pointed to by the given path +pattern.

+ + This is the same as khepri:put/3 but inside the context of a + transaction function. +

+

See also: khepri:put/3.

+ +

put/3

+
+

put(PathPattern, Data, Options) -> Ret +

+

+

Sets the payload of the tree node pointed to by the given path +pattern.

+ + This is the same as khepri:put/4 but inside the context of a + transaction function. +

+

See also: khepri:put/4.

+ +

put_many/2

+
+

put_many(PathPattern, Data) -> Ret +

+

+

Sets the payload of all the tree nodes matching the given path pattern.

+ + This is the same as khepri:put_many/3 but inside the context of a + transaction function. +

+

See also: khepri:put_many/3.

+ +

put_many/3

+
+

put_many(PathPattern, Data, Options) -> Ret +

+

+

Sets the payload of all the tree nodes matching the given path pattern.

+ + This is the same as khepri:put_many/4 but inside the context of a + transaction function. +

+

See also: khepri:put_many/4.

+ +

create/2

+
+

create(PathPattern, Data) -> Ret +

+

+

Creates a tree node with the given payload.

+ + This is the same as khepri:create/3 but inside the context of a + transaction function. +

+

See also: khepri:create/3.

+ +

create/3

+
+

create(PathPattern, Data, Options) -> Ret +

+

+

Creates a tree node with the given payload.

+ + This is the same as khepri:create/4 but inside the context of a + transaction function. +

+

See also: khepri:create/4.

+ +

update/2

+
+

update(PathPattern, Data) -> Ret +

+

+

Updates an existing tree node with the given payload.

+ + This is the same as khepri:update/3 but inside the context of a + transaction function. +

+

See also: khepri:update/3.

+ +

update/3

+
+

update(PathPattern, Data, Options) -> Ret +

+

+

Updates an existing tree node with the given payload.

+ + This is the same as khepri:update/4 but inside the context of a + transaction function. +

+

See also: khepri:update/4.

+ +

compare_and_swap/3

+
+

compare_and_swap(PathPattern, DataPattern, Data) -> Ret +

+

+

Updates an existing tree node with the given payload only if its data +matches the given pattern.

+ + This is the same as khepri:compare_and_swap/4 but inside the context + of a transaction function. +

+

See also: khepri:compare_and_swap/4.

+ +

compare_and_swap/4

+
+

compare_and_swap(PathPattern, DataPattern, Data, Options) -> Ret +

+

+

Updates an existing tree node with the given payload only if its data +matches the given pattern.

+ + This is the same as khepri:compare_and_swap/5 but inside the context + of a transaction function. +

+

See also: khepri:compare_and_swap/5.

+ +

delete/1

+
+

delete(PathPattern) -> Ret +

+

+

Deletes the tree node pointed to by the given path pattern.

+ + This is the same as khepri:delete/2 but inside the context + of a transaction function. +

+

See also: khepri:delete/2.

+ +

delete/2

+
+

delete(PathPattern, Options) -> Ret +

+

+

Deletes the tree node pointed to by the given path pattern.

+ + This is the same as khepri:delete/3 but inside the context + of a transaction function. +

+

See also: khepri:delete/3.

+ +

delete_many/1

+
+

delete_many(PathPattern) -> Ret +

+

+

Deletes all tree nodes matching the given path pattern.

+ + This is the same as khepri:delete_many/2 but inside the context + of a transaction function. +

+

See also: khepri:delete_many/2.

+ +

delete_many/2

+
+

delete_many(PathPattern, Options) -> Ret +

+

+

Deletes all tree nodes matching the given path pattern.

+ + This is the same as khepri:delete_many/3 but inside the context + of a transaction function. +

+

See also: khepri:delete_many/3.

+ +

clear_payload/1

+
+

clear_payload(PathPattern) -> Ret +

+

+

Deletes the payload of the tree node pointed to by the given path +pattern.

+ + This is the same as khepri:clear_payload/2 but inside the context + of a transaction function. +

+

See also: khepri:clear_payload/2.

+ +

clear_payload/2

+
+

clear_payload(PathPattern, Options) -> Ret +

+

+

Deletes the payload of the tree node pointed to by the given path +pattern.

+ + This is the same as khepri:clear_payload/3 but inside the context + of a transaction function. +

+

See also: khepri:clear_payload/3.

+ +

clear_many_payloads/1

+
+

clear_many_payloads(PathPattern) -> Ret +

+

+

Deletes the payload of all tree nodes matching the given path pattern.

+ + This is the same as khepri:clear_many_payloads/2 but inside the + context of a transaction function. +

+

See also: khepri:clear_many_payloads/2.

+ +

clear_many_payloads/2

+
+

clear_many_payloads(PathPattern, Options) -> Ret +

+

+

Deletes the payload of all tree nodes matching the given path pattern.

+ + This is the same as khepri:clear_many_payloads/3 but inside the + context of a transaction function. +

+

See also: khepri:clear_many_payloads/3.

+ +

abort/1

+
+

abort(Reason) -> no_return() +

+

Reason: term to return to caller of the transaction.
+

+

Aborts the transaction.

+ +

Any changes so far are not committed to the store.

+ + khepri:transaction/1 and friends will return tx_abort(). +

+ +

is_transaction/0

+
+

is_transaction() -> boolean()

+

+

returns: true if the calling code runs inside a transaction function, + false otherwise.

+

Indicates if the calling function runs in the context of a transaction + function. +

+
+ + +

Generated by EDoc

+ + diff --git a/khepri_tx_adv.html b/khepri_tx_adv.html new file mode 100644 index 00000000..c8dc9982 --- /dev/null +++ b/khepri_tx_adv.html @@ -0,0 +1,365 @@ + + + + +Module khepri_tx_adv + + + + + + +
+ +

Module khepri_tx_adv

+Khepri advanced API for transactional queries and updates. + + +

Description

Khepri advanced API for transactional queries and updates.

+ + This module exposes variants of the functions in khepri_tx which + return more detailed return values for advanced use cases. See khepri_adv for examples of use cases where this module could be useful. +

Function Index

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
get/1Returns the payload of the tree node pointed to by the given path +pattern.
get/2Returns the payload of the tree node pointed to by the given path +pattern.
get_many/1Returns payloads of all the tree nodes matching the given path +pattern.
get_many/2Returns payloads of all the tree nodes matching the given path +pattern.
do_get_many/4
put/2Sets the payload of the tree node pointed to by the given path +pattern.
put/3Sets the payload of the tree node pointed to by the given path +pattern.
put_many/2Sets the payload of all the tree nodes matching the given path pattern.
put_many/3Sets the payload of all the tree nodes matching the given path pattern.
create/2Creates a tree node with the given payload.
create/3Creates a tree node with the given payload.
update/2Updates an existing tree node with the given payload.
update/3Updates an existing tree node with the given payload.
compare_and_swap/3Updates an existing tree node with the given payload only if its data +matches the given pattern.
compare_and_swap/4Updates an existing tree node with the given payload only if its data +matches the given pattern.
delete/1Deletes the tree node pointed to by the given path pattern.
delete/2Deletes the tree node pointed to by the given path pattern.
delete_many/1Deletes all tree nodes matching the given path pattern.
delete_many/2Deletes all tree nodes matching the given path pattern.
clear_payload/1Deletes the payload of the tree node pointed to by the given path +pattern.
clear_payload/2Deletes the payload of the tree node pointed to by the given path +pattern.
clear_many_payloads/1Deletes the payload of all tree nodes matching the given path pattern.
clear_many_payloads/2Deletes the payload of all tree nodes matching the given path pattern.
ensure_instruction_is_permitted/1
should_process_function/4
is_standalone_fun_still_needed/2
+ +

Function Details

+ +

get/1

+
+

get(PathPattern) -> Ret +

+

+

Returns the payload of the tree node pointed to by the given path +pattern.

+ + This is the same as khepri_adv:get/2 but inside the context of a + transaction function. +

+

See also: khepri_adv:get/2.

+ +

get/2

+
+

get(PathPattern, Options) -> Ret +

+

+

Returns the payload of the tree node pointed to by the given path +pattern.

+ + This is the same as khepri_adv:get/3 but inside the context of a + transaction function. +

+

See also: khepri_adv:get/3.

+ +

get_many/1

+
+

get_many(PathPattern) -> Ret +

+

+

Returns payloads of all the tree nodes matching the given path +pattern.

+ + This is the same as khepri_adv:get_many/2 but inside the context of + a transaction function. +

+

See also: khepri_adv:get_many/2.

+ +

get_many/2

+
+

get_many(PathPattern, Options) -> Ret +

+

+

Returns payloads of all the tree nodes matching the given path +pattern.

+ + This is the same as khepri_adv:get_many/3 but inside the context of + a transaction function. +

+

See also: khepri_adv:get_many/3.

+ +

do_get_many/4

+
+

do_get_many(PathPattern, Fun, Acc, Options) -> any()

+

+
+ +

put/2

+
+

put(PathPattern, Data) -> Ret +

+

+

Sets the payload of the tree node pointed to by the given path +pattern.

+ + This is the same as khepri_adv:put/3 but inside the context of a + transaction function. +

+

See also: khepri_adv:put/3.

+ +

put/3

+
+

put(PathPattern, Data, Options) -> Ret +

+

+

Sets the payload of the tree node pointed to by the given path +pattern.

+ + This is the same as khepri_adv:put/4 but inside the context of a + transaction function. +

+

See also: khepri_adv:put/4.

+ +

put_many/2

+
+

put_many(PathPattern, Data) -> Ret +

+

+

Sets the payload of all the tree nodes matching the given path pattern.

+ + This is the same as khepri_adv:put_many/3 but inside the context of + a transaction function. +

+

See also: khepri_adv:put_many/3.

+ +

put_many/3

+
+

put_many(PathPattern, Data, Options) -> Ret +

+

+

Sets the payload of all the tree nodes matching the given path pattern.

+ + This is the same as khepri_adv:put_many/4 but inside the context of + a transaction function. +

+

See also: khepri_adv:put_many/4.

+ +

create/2

+
+

create(PathPattern, Data) -> Ret +

+

+

Creates a tree node with the given payload.

+ + This is the same as khepri_adv:create/3 but inside the context of a + transaction function. +

+

See also: khepri_adv:create/3.

+ +

create/3

+
+

create(PathPattern, Data, Options) -> Ret +

+

+

Creates a tree node with the given payload.

+ + This is the same as khepri_adv:create/4 but inside the context of a + transaction function. +

+

See also: khepri_adv:create/4.

+ +

update/2

+
+

update(PathPattern, Data) -> Ret +

+

+

Updates an existing tree node with the given payload.

+ + This is the same as khepri_adv:update/3 but inside the context of a + transaction function. +

+

See also: khepri_adv:update/3.

+ +

update/3

+
+

update(PathPattern, Data, Options) -> Ret +

+

+

Updates an existing tree node with the given payload.

+ + This is the same as khepri_adv:update/4 but inside the context of a + transaction function. +

+

See also: khepri_adv:update/4.

+ +

compare_and_swap/3

+
+

compare_and_swap(PathPattern, DataPattern, Data) -> Ret +

+

+

Updates an existing tree node with the given payload only if its data +matches the given pattern.

+ + This is the same as khepri_adv:compare_and_swap/4 but inside the + context of a transaction function. +

+

See also: khepri_adv:compare_and_swap/4.

+ +

compare_and_swap/4

+
+

compare_and_swap(PathPattern, DataPattern, Data, Options) -> Ret +

+

+

Updates an existing tree node with the given payload only if its data +matches the given pattern.

+ + This is the same as khepri_adv:compare_and_swap/5 but inside the + context of a transaction function. +

+

See also: khepri_adv:compare_and_swap/5.

+ +

delete/1

+
+

delete(PathPattern) -> Ret +

+

+

Deletes the tree node pointed to by the given path pattern.

+ + This is the same as khepri_adv:delete/2 but inside the context of a + transaction function. +

+

See also: khepri_adv:delete/2.

+ +

delete/2

+
+

delete(PathPattern, Options) -> Ret +

+

+

Deletes the tree node pointed to by the given path pattern.

+ + This is the same as khepri_adv:delete/3 but inside the context of a + transaction function. +

+

See also: khepri_adv:delete/3.

+ +

delete_many/1

+
+

delete_many(PathPattern) -> Ret +

+

+

Deletes all tree nodes matching the given path pattern.

+ + This is the same as khepri_adv:delete_many/2 but inside the context + of a transaction function. +

+

See also: khepri_adv:delete_many/2.

+ +

delete_many/2

+
+

delete_many(PathPattern, Options) -> Ret +

+

+

Deletes all tree nodes matching the given path pattern.

+ + This is the same as khepri_adv:delete_many/3 but inside the context + of a transaction function. +

+

See also: khepri_adv:delete_many/3.

+ +

clear_payload/1

+
+

clear_payload(PathPattern) -> Ret +

+

+

Deletes the payload of the tree node pointed to by the given path +pattern.

+ + This is the same as khepri_adv:clear_payload/2 but inside the + context of a transaction function. +

+

See also: khepri_adv:clear_payload/2.

+ +

clear_payload/2

+
+

clear_payload(PathPattern, Options) -> Ret +

+

+

Deletes the payload of the tree node pointed to by the given path +pattern.

+ + This is the same as khepri_adv:clear_payload/3 but inside the + context of a transaction function. +

+

See also: khepri_adv:clear_payload/3.

+ +

clear_many_payloads/1

+
+

clear_many_payloads(PathPattern) -> Ret +

+

+

Deletes the payload of all tree nodes matching the given path pattern.

+ + This is the same as khepri_adv:clear_many_payloads/2 but inside the + context of a transaction function. +

+

See also: khepri_adv:clear_many_payloads/2.

+ +

clear_many_payloads/2

+
+

clear_many_payloads(PathPattern, Options) -> Ret +

+

+

Deletes the payload of all tree nodes matching the given path pattern.

+ + This is the same as khepri_adv:clear_many_payloads/3 but inside the + context of a transaction function. +

+

See also: khepri_adv:clear_many_payloads/3.

+ +

ensure_instruction_is_permitted/1

+
+

ensure_instruction_is_permitted(Unknown) -> any()

+

+
+ +

should_process_function/4

+
+

should_process_function(Module, Name, Arity, FromModule) -> any()

+

+
+ +

is_standalone_fun_still_needed/2

+
+

is_standalone_fun_still_needed(X1, X2) -> any()

+

+
+
+ + +

Generated by EDoc

+ + diff --git a/modules-frame.html b/modules-frame.html new file mode 100644 index 00000000..118a4562 --- /dev/null +++ b/modules-frame.html @@ -0,0 +1,64 @@ + + + +The khepri application + + + + + + +

khepri

+ +

Modules

+ + + + + + + + + + + + + + +
khepri
khepri_adv
khepri_cluster
khepri_condition
khepri_evf
khepri_export_erlang
khepri_import_export
khepri_machine
khepri_path
khepri_payload
khepri_prefix_tree
khepri_projection
khepri_tx
khepri_tx_adv
+ + \ No newline at end of file diff --git a/overview-summary.html b/overview-summary.html new file mode 100644 index 00000000..bf11cb44 --- /dev/null +++ b/overview-summary.html @@ -0,0 +1,566 @@ + + + + +The Khepri Database + + + + + + + +

The Khepri Database

+

Copyright © 2021-2024 Broadcom. All Rights Reserved. The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.

+

Version: Development branch +

+

Authors: Jean-Sébastien Pédron (jean-sebastien.pedron@dumbbell.fr), Michael Davis (mcarsondavis@gmail.com), Diana Parra Corbacho (dparracorbac@vmware.com), Karl Nilsson (nkarl@vmware.com), The RabbitMQ team (rabbitmq-core@groups.vmware.com).

+

+Khepri is a tree-like replicated on-disk database library for Erlang and +Elixir.

+ +

Data are stored in a tree structure. Each node in the tree is +referenced by its path from the root node. A path is a list of Erlang atoms +and/or binaries. For ease of use, Unix-like path strings are accepted as well.

+ +

For consistency and replication and to manage data on disk, +Khepri relies on Ra, an Erlang +implementation of the Raft consensus +algorithm. In Ra parlance, Khepri is a state machine in a Ra cluster.

+ +

This page describes all the concepts in Khepri and points the +reader to the modules' documentation for more details.

+ +

Fork me on GitHub

+ +
+ +

Why Khepri?

+ +

This started as an experiment to replace how data (other than message bodies) +are stored in the RabbitMQ messaging +broker. Before Khepri, those data were stored and replicated to cluster +members using Mnesia.

+ +Mnesia is very handy and powerful: + + +

However, recovering from any network partitions is quite difficult. This was +the primary reason why the RabbitMQ team started to explore other options.

+ +

Because RabbitMQ already uses an implementation of the Raft consensus algorithm +for its quorum queues, it was decided to leverage that library for all +metadata. That's how Khepri was borne.

+ +

Thanks to Ra and Raft, it is clear how Khepri will behave during a +network partition and recover from it. This makes it more comfortable +for the RabbitMQ team and users, thanks to the absence of unknowns.

+ +
+At the time of this writing, RabbitMQ does not use Khepri in a production +release yet because this library and its integration into RabbitMQ are still a +work in progress. +
+ +

The tree structure

+ +

Tree nodes

+ +Data in Khepri are organized as tree nodes (khepri_machine:tree_node()) in a tree structure. Every tree node has: + + +
o
+|
++-- orders
+|
+`-- stock
+    |
+    `-- wood
+        |-- <<"mapple">> = 12
+        `-- <<"oak">> = 41
+ +

Node ID

+ +

A tree node name is either an Erlang atom or an Erlang binary (khepri_path:node_id()).

+ +

Payload

+ +

A tree node may or may not have a payload. Khepri supports two types of +payload, the data payload and the stored procedure payload. +More payload types may be added in the future.

+ +When passed to khepri:put/2, the type of the payload is autodetected. +However if you need to prepare the payload before passing it to Khepri, you can +use the following functions: + + +

Properties

+ +Properties are: + + +

Addressing a tree node

+ +

The equivalent of a key in a key/value store is a path +(khepri_path:path()) in Khepri.

+ +A path is a list of node IDs, from the root (unnamed) tree node to the target +(khepri_path:path()). For instance: +
%% Points to "/:stock/:wood/oak" in the tree shown above:
+Path = [stock, wood, <<"oak">>].
+ +It is possible to target multiple tree nodes at once by using a path +pattern (khepri_path:pattern()). In addition to node IDs, path +patterns have conditions (khepri_condition:condition()). Conditions +allow things like: + + +For instance: +
%% Matches all varieties of wood in the stock:
+PathPattern = [stock, wood, #if_node_matches{regex = any}].
+
+%% Matches the supplier of oak if there is an active order:
+PathPattern = [order,
+               wood,
+               #if_all{conditions = [
+                 <<"oak">>,
+                 #if_data_matches{pattern = {active, true}}]},
+               supplier].
+ +Finally, a path can use some special path component names, handy when using +relative paths: + + +

Relative paths are useful when putting conditions on +tree node lifetimes.

+ +

Tree node lifetime

+ +

A tree node's lifetime starts when it is inserted the first time and ends when +it is removed from the tree. However, intermediary tree nodes created on the +way remain in the tree long after the leaf node was removed.

+ +

For instance, when [stock, wood, <<"walnut">>] was inserted, the intermediary +tree nodes stock and wood were created if they were missing. After +<<"walnut">> is removed, they will stay in the tree with possibly neither +payload nor child nodes.

+ +

Khepri has the concept of keep_while conditions. A keep_while +condition is like the conditions which can be used inside path pattern. When a +node is inserted or updated, it is possible to set keep_while conditions: +when these conditions evaluate to false, the tree node is removed from the +tree.

+ +For instance, it is possible to set the following condition on [stock, wood] +to make sure it is removed after its last child node is removed: +
%% We keep [stock, wood] as long as its child nodes count is strictly greater
+%% than zero.
+KeepWhileCondition = #{[stock, wood] => #if_child_list_length{count = {gt, 0}}}.
+ +

keep_while conditions on self (like the example above) are not evaluated on +the first insert though.

+ +

Stores

+ +

A Khepri store corresponds to one Ra cluster. In fact, the name of the Ra +cluster is the name of the Khepri store. It is possible to have multiple +database instances running on the same Erlang node or cluster by starting +multiple Ra clusters. Note that it is called a "cluster" but it can have a +single member.

+ +

You can start a Khepri store using start/0 up to start/3. See +those functions to learn more about the configuration settings.

+ +

To expand or shrink a cluster, khepri_cluster:join/1 and khepri_cluster:reset/0 allow a Khepri store node to join or leave a cluster.

+ +

Khepri API

+ +

The essential part of the public API is provided by the khepri module. +It covers most common use cases and should be straightforward to use.

+ +
ok = khepri:put([stock, wood, <<"lime tree">>], 150),
+
+{ok, 150} = khepri:get([stock, wood, <<"lime tree">>]),
+
+true = khepri:exists([stock, wood, <<"lime tree">>]),
+
+ok = khepri:delete([stock, wood, <<"lime tree">>]).
+ +

Inside transaction functions, khepri_tx must be used instead of khepri. The former provides the same API, except for functions which don't +make sense in the context of a transaction function.

+ +

khepri and khepri_tx both have counterparts for more advanced +use cases, khepri_adv and khepri_tx_adv. The return values of +the *_adv modules are maps giving more details about what was queried or +modified.

+ +

The public API is built on top of a low-level internal API, provided by the +private khepri_machine module.

+ +

Transactions

+ +

Restrictions

+ +

On the surface, Khepri transactions look like Mnesia ones: they are anonymous +functions which can do any arbitrary operations on the data and return any +result. If something goes wrong or the anonymous function aborts, nothing is +committed and the database is left untouched as if the transaction code was +never called.

+ +Under the hood, there are several restrictions and caveats that need to be +understood in order to use transactions in Khepri: + + +

The nature of the anonymous function is passed as the ReadWrite argument to +khepri:transaction/3.

+ +

The constraints imposed by Raft

+ +

The Raft algorithm is used to achieve consensus among Khepri members +participating in the database. Khepri is a state machine executed on each Ra +node and all instances of that Khepri state machine start with the same state +and modify it identically. The goal is that, after the same list of Ra +commands, all instances have the same state.

+ +

When a new Ra node joins the cluster and therefore participates to the Khepri +database, it starts a new Khepri state machine instance. This instance needs to +apply all Ra commands from an initial state to be on the same page as other +existing instances.

+ +

Likewise, if for any reason, one of the Khepri state machine instance looses +the connection to other members and can't apply Ra commands, then when the link +comes back, it has to catch up.

+ +

All this means that the code to modify the state of the state machines (i.e. +the tree) needs to run on all instances, possibly not at the same time, and +give the exact same result everywhere.

+ +

The problem with anonymous functions

+ +This is fine for inserts and deletes because the code is part of Khepri and is +deterministic. This poses a problem when transactions are anonymous functions +outside of Khepri's control: +
    +
  1. Khepri must be able to store the anonymous function as a Ra command in Ra's +log. This is the basis for replication and is mandatory to add a new cluster +member or for a lagging member to catch up.
  2. +
  3. The anonymous function must produce exactly the same result in all state +machine instances, regardless of the time it runs, the availability of other +Erlang modules, the state of Erlang processes, files on disk or network +connections, and so on.
  4. +
+ +

To achieve that, Horus and khepri_tx extract the assembly +code of the anonymous function and create a standalone Erlang module based on +it. This module can be stored in Ra's log and executed anywhere without the +presence of the initial anonymous function's module.

+ +Here is what they do in more details: +
    +
  1. The assembly code of the module hosting the anonymous function is +extracted.
  2. +
  3. The anonymous function code is located inside that assembly code.
  4. +
  5. The code is analyzed to determine: +
  6. +
  7. Based on the listed function calls, the same steps are repeated for all of +them (extract, verify, list calls).
  8. +
  9. Once all the assembly code to have a standalone anonymous function is +collected, an Erlang module is generated.
  10. +
+ +

How to handle side effects?

+ +

The consequence of the above constraints is that a transaction function can't +depend on anything else than the tree and it can't have any side effects +outside of the changes to the tree nodes.

+ +If the transaction needs to have side effects, there are two options: + + +

Here is an example of the second option:

+ +
Path = [stock, wood, <<"lime tree">>],
+{ok, #{data := Term,
+       payload_version := PayloadVersion}} =
+  khepri_adv:get(StoredId, Path),
+
+%% Do anything with `Term` that depend on external factors and could have side
+%% effects.
+Term1 = do_something_with_side_effects(Term),
+
+PathPattern = [stock,
+               wood,
+               #if_all{
+                 conditions = [
+                   <<"lime tree">>,
+                   #if_payload_version{version = PayloadVersion}]}],
+case khepri:put(StoredId, PathPattern, Term1) of
+    ok ->
+        ok; %% `Term1` was stored successfully.
+    {error, ?khepri_error(mismatching_node, _)} ->
+        loop() %% Restart the whole function to read/modify/write again.
+end.
+ +

Import and export

+ +

To backup and restore a Khepri store, you can use khepri:export/4 and +khepri:import/3.

+ +

They both rely on a backend module which follows the Mnesia Backup & +Restore API. Khepri comes with one backend module called khepri_export_erlang. It uses plaintext files containing opaque Erlang terms +formatted as text. It is possible to provide your own backend module obviously.

+ +

To export the content of a running store:

+ +
ok = khepri:export(StoreId, khepri_export_erlang, "export.erl").
+ +

This will export the entire store. It is possible to export a part of it by +passing a path patterns to select the tree nodes you want to export.

+ +

Later, to import to a running store:

+ +
ok = khepri:import(StoreId, khepri_export_erlang, "export.erl").
+ +

Importing a backup does not touch unrelated tree nodes. In other words, the +content of the exported store is imported but whatever exists in the target +store remains (except if the import overwrites some tree nodes of course). In +particular, the target store is not reset.

+ +

You can learn more about this import/export feature in the khepri_import_export module documentation.

+ +

You can lean more about the provide backend module by reading the documentation +of khepri_export_erlang.

+ +

Stored procedures and triggers

+ +

Triggering a function after some event

+ +

It is possible to associate events with an anonymous function to trigger its +execution when something happens. This is what is usually called a +trigger in databases and Khepri supports this feature.

+ +

Currently, Khepri supports a single type of event, tree changes. This +event is emitted whenever a tree node is being created, updated or deleted.

+ +Here is a summary of what happens when such an event is emitted: +
    +
  1. Khepri looks up any event filters which could match the emitted +event.
  2. +
  3. If one or more event filters are found, their corresponding stored +procedures are executed.
  4. +
+ +

The indicated stored procedure must have been stored in the tree first.

+ +

Storing an anonymous function

+ +

This is possible to store an anonymous function as the payload of a tree node:

+ +
khepri:put(
+  StoreId,
+  StoredProcPath,
+  fun() -> do_something() end).
+ +

The StoredProcPath can be any path in the +tree.

+ +

Unlike transaction functions, a stored procedure has no restrictions on what +it is allowed to do. Therefore, a stored procedure can send or receive +messages, read or write from a disk, generate random numbers and so on.

+ +

A stored procedure can accept any numbers of arguments too.

+ +

It is possible to execute a stored procedure directly without configuring any +triggers. To execute a stored procedure, you can call khepri:run_sproc/3. Here is an example:

+ +
Ret = khepri:run_sproc(
+        StoreId,
+        StoredProcPath,
+        [] = _Args).
+ +

This works exactly like erlang:apply/2. The list of arguments passed +to khepri:run_sproc/3 must correspond to the stored procedure +arity.

+ +

Configuring a trigger

+ +

Khepri uses event filters to associate a type of events with a stored +procedure. Khepri supports tree changes events and thus only supports a single +event filter called khepri_evf:tree_event_filter().

+ +

An event filter is registered using khepri:register_trigger/4:

+ +
%% An event filter can be explicitly created using the `khepri_evf'
+%% module. This is possible to specify properties at the same time.
+EventFilter = khepri_evf:tree([stock, wood, <<"oak">>], %% Required
+                              #{on_actions => [delete], %% Optional
+                                priority => 10}),       %% Optional
+
+%% For ease of use, some terms can be automatically converted to an event
+%% filter. Here, a Unix-like path could be used as a tree event filter, though
+%% it would have default properties unlike the previous line:
+EventFilter = "/:stock/:wood/oak".
+
+ok = khepri:register_trigger(
+       StoreId,
+       TriggerId,
+       EventFilter,
+       StoredProcPath).
+ +

In this example, the khepri_evf:tree_event_filter() structure only +requires the path to monitor. The path can be any path pattern and thus can +have conditions to monitor several nodes at once.

+ +

The on_actions property is optional. By default the event filter matches all +tree changes (create, update or delete).

+ +

The priority property is also optional and defaults to 0. When several event +filters match a given event, they are sorted by priority (a greater integer +means the event filter will be considered first), then by TriggerId in +alphabetical order.

+ +

Neither the monitored path nor the stored procedure (pointed to by +StoredProcPath) need to exist when the event filter is registered. If the +stored procedure doesn't exist when an event occurs, the event filter is +simply ignored. A stored procedure can change after an event filter is +registered as well.

+ +

The stored procedure used for a trigger must accept a single argument, a map +containing properties of the emitted event:

+ +
my_stored_procedure(Props) ->
+    #{path := Path,
+      on_action := Action} = Props.
+ +

Path is the path to the tree node created, updated or deleted.

+ +

Action is the nature of the change (create, update or delete).

+ +

The return value of this stored procedure is ignored in the context of a +trigger.

+ +

Execution guarantees

+ +

The stored procedure associated with a trigger (event filter) is executed on +the Ra leader node.

+ +

If the stored procedure throws an exception, it is logged and there is no +retry.

+ +

There is an internal ack mechanism to make sure the stored procedure is +executed at least once. Therefore, if the Ra leader changes before the +execution of the stored procedure could be confirmed to the Khepri state +machine, the execution will be retried on the new Ra leader.

+ +

This means that the stored procedure could be executed multiple times. +Therefore it is important it is idempotent.

+ +

Differences with triggers in RDBMS

+ +

As described earlier, the rationale for triggers in Khepri is that sometimes, +one needs to execute some code with side effects (e.g. sending a message to a +process) after a record was modified in the database. This can't happen in a +transaction because side effects are forbidden. The caller could handle that +after he modifies the record, but the record could be indirectly modified +(deleted) as a consequence of another record being modified or deleted. In +this case, the caller can't do anything.

+ +

Because of the freedom they need, triggers are not allowed to mess +with the database directly. In other words, they must go through the +regular Khepri API like any caller. Triggers do not have any privileges or +blanket approvals to tamper with the data.

+ +

So even though Khepri uses the same naming than many RDBMS, triggers in Khepri +can't have unexpected consequences.

+ +

Projections

+ +

Projections are a system within Khepri for maintaining replicated ETS caches +for tree nodes matching a given path pattern.

+ +

Projection resources are created with the khepri_projection:new/3 +function and are registered in a store with khepri:register_projection/4:

+ +
ProjectionName = wood_stocks,
+ProjectionFun = fun([stock, wood, Kind], Stock) -> {Kind, Stock} end,
+Options = #{type => set, read_concurrency => true},
+Projection = khepri_projection:new(ProjectionName, ProjectionFun, Options).
+
+StoreId = stock,
+PathPattern = "/:stock/:wood/*",
+khepri:register_projection(StoreId, PathPattern, Projection).
+ +

When a path within the store which matches PathPattern changes, the changed +path and data are passed to the given ProjectionFun to create ETS objects +which are then stored in the projection's ETS table.

+ +

Projection tables may be queried directly with functions from the ets +module.

+ +
khepri:put(StoreId, "/:stock/:wood/oak", 100),
+ets:lookup(ProjectionName, <<"oak">>, 2).
+%%=> [{<<"oak">>,100}]
+
+khepri:put(StoreId, "/:stock/:wood/oak", 80),
+ets:lookup(ProjectionName, <<"oak">>, 2).
+%%=> [{<<"oak">>,80}]
+
+khepri:delete(StoreId, "/:stock/:wood/oak"),
+ets:member(ProjectionName, <<"oak">>).
+%%=> false
+ +

Use projections to maximize query throughput and/or minimize query latency.

+ +Projections have some costs though. Expect some increased memory consumption +since information in the projection tables is duplicated between the Khepri +store and ETS. Khepri may also take longer to accomplish writes since +projections are updated by Khepri synchronously when writing to the store. + +
+ +

Generated by EDoc

+ + diff --git a/overview.edoc b/overview.edoc new file mode 100644 index 00000000..d6132be6 --- /dev/null +++ b/overview.edoc @@ -0,0 +1,589 @@ +@author Jean-Sébastien Pédron +@author Michael Davis +@author Diana Parra Corbacho +@author Karl Nilsson +@author The RabbitMQ team +@copyright 2021-2024 Broadcom. All Rights Reserved. The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +@title The Khepri Database +@version Development branch + +@doc +Khepri is a tree-like replicated on-disk database library for Erlang and +Elixir. + +Data are stored in a tree structure. Each node in the tree is +referenced by its path from the root node. A path is a list of Erlang atoms +and/or binaries. For ease of use, Unix-like path strings are accepted as well. + +For consistency and replication and to manage data on disk, +Khepri relies on Ra, an Erlang +implementation of the Raft consensus +algorithm. In Ra parlance, Khepri is a state machine in a Ra cluster. + +This page describes all the concepts in Khepri and points the +reader to the modules' documentation for more details. + +Fork me on GitHub + +
+ +== Why Khepri? == + +This started as an experiment to replace how data (other than message bodies) +are stored in the RabbitMQ messaging +broker. Before Khepri, those data were stored and replicated to cluster +members using Mnesia. + +Mnesia is very handy and powerful: +
    +
  • It comes out-of-the-box with the Erlang runtime and standard library.
  • +
  • It does all the heavy lifting and RabbitMQ just uses it as a key/value +store without thinking too much about replication.
  • +
+ +However, recovering from any network partitions is quite difficult. This was +the primary reason why the RabbitMQ team started to explore other options. + +Because RabbitMQ already uses an implementation of the Raft consensus algorithm +for its quorum queues, it was decided to leverage that library for all +metadata. That's how Khepri was borne. + +Thanks to Ra and Raft, it is clear how Khepri will behave during a +network partition and recover from it. This makes it more comfortable +for the RabbitMQ team and users, thanks to the absence of unknowns. + +
+At the time of this writing, RabbitMQ does not use Khepri in a production +release yet because this library and its integration into RabbitMQ are still a +work in progress. +
+ +== The tree structure == + +=== Tree nodes === + +Data in Khepri are organized as tree nodes ({@link +khepri_machine:tree_node()}) in a tree structure. Every tree node has: + + +```none +o +| ++-- orders +| +`-- stock + | + `-- wood + |-- <<"mapple">> = 12 + `-- <<"oak">> = 41 +''' + +=== Node ID === + +A tree node name is either an Erlang atom or an Erlang binary ({@link +khepri_path:node_id()}). + +=== Payload === + +A tree node may or may not have a payload. Khepri supports two types of +payload, the data payload and the stored procedure payload. +More payload types may be added in the future. + +When passed to {@link khepri:put/2}, the type of the payload is autodetected. +However if you need to prepare the payload before passing it to Khepri, you can +use the following functions: +
    +
  • {@link khepri_payload:none/0}
  • +
  • {@link khepri_payload:data/1}
  • +
  • {@link khepri_payload:sproc/1}
  • +
+ +=== Properties === + +Properties are: +
    +
  • The version of the payload, tracking the number of times it was modified +({@link khepri:payload_version()}).
  • +
  • The version of the list of child nodes, tracking the number of times child +nodes were added or removed ({@link khepri:child_list_version()}).
  • +
  • The number of child nodes ({@link khepri:child_list_count()}).
  • +
+ +=== Addressing a tree node === + +The equivalent of a key in a key/value store is a path +({@link khepri_path:path()}) in Khepri. + +A path is a list of node IDs, from the root (unnamed) tree node to the target +({@link khepri_path:path()}). For instance: +``` +%% Points to "/:stock/:wood/oak" in the tree shown above: +Path = [stock, wood, <<"oak">>]. +''' + +It is possible to target multiple tree nodes at once by using a path +pattern ({@link khepri_path:pattern()}). In addition to node IDs, path +patterns have conditions ({@link khepri_condition:condition()}). Conditions +allow things like: +
    +
  • checking the existence of a tree node
  • +
  • targeting all child nodes of a tree node
  • +
  • matching on node IDs using a regex
  • +
  • matching on the data payload
  • +
+ +For instance: +``` +%% Matches all varieties of wood in the stock: +PathPattern = [stock, wood, #if_node_matches{regex = any}]. + +%% Matches the supplier of oak if there is an active order: +PathPattern = [order, + wood, + #if_all{conditions = [ + <<"oak">>, + #if_data_matches{pattern = {active, true}}]}, + supplier]. +''' + +Finally, a path can use some special path component names, handy when using +relative paths: +
    +
  • `?THIS_NODE' to point to self
  • +
  • `?PARENT_NODE' to point to the parent tree node
  • +
  • `?ROOT_NODE' to explicitly point to the root unnamed node
  • +
+ +Relative paths are useful when putting conditions on +tree node lifetimes. + +=== Tree node lifetime === + +A tree node's lifetime starts when it is inserted the first time and ends when +it is removed from the tree. However, intermediary tree nodes created on the +way remain in the tree long after the leaf node was removed. + +For instance, when `[stock, wood, <<"walnut">>]' was inserted, the intermediary +tree nodes `stock' and `wood' were created if they were missing. After +`<<"walnut">>' is removed, they will stay in the tree with possibly neither +payload nor child nodes. + +Khepri has the concept of `keep_while' conditions. A `keep_while' +condition is like the conditions which can be used inside path pattern. When a +node is inserted or updated, it is possible to set `keep_while' conditions: +when these conditions evaluate to false, the tree node is removed from the +tree. + +For instance, it is possible to set the following condition on `[stock, wood]' +to make sure it is removed after its last child node is removed: +``` +%% We keep [stock, wood] as long as its child nodes count is strictly greater +%% than zero. +KeepWhileCondition = #{[stock, wood] => #if_child_list_length{count = {gt, 0}}}. +''' + +`keep_while' conditions on self (like the example above) are not evaluated on +the first insert though. + +== Stores == + +A Khepri store corresponds to one Ra cluster. In fact, the name of the Ra +cluster is the name of the Khepri store. It is possible to have multiple +database instances running on the same Erlang node or cluster by starting +multiple Ra clusters. Note that it is called a "cluster" but it can have a +single member. + +You can start a Khepri store using {@link start/0} up to {@link start/3}. See +those functions to learn more about the configuration settings. + +To expand or shrink a cluster, {@link khepri_cluster:join/1} and {@link +khepri_cluster:reset/0} allow a Khepri store node to join or leave a cluster. + +== Khepri API == + +The essential part of the public API is provided by the {@link khepri} module. +It covers most common use cases and should be straightforward to use. + +``` +ok = khepri:put([stock, wood, <<"lime tree">>], 150), + +{ok, 150} = khepri:get([stock, wood, <<"lime tree">>]), + +true = khepri:exists([stock, wood, <<"lime tree">>]), + +ok = khepri:delete([stock, wood, <<"lime tree">>]). +''' + +Inside transaction functions, {@link khepri_tx} must be used instead of {@link +khepri}. The former provides the same API, except for functions which don't +make sense in the context of a transaction function. + +{@link khepri} and {@link khepri_tx} both have counterparts for more advanced +use cases, {@link khepri_adv} and {@link khepri_tx_adv}. The return values of +the `*_adv' modules are maps giving more details about what was queried or +modified. + +The public API is built on top of a low-level internal API, provided by the +private {@link khepri_machine} module. + +== Transactions == + +=== Restrictions === + +On the surface, Khepri transactions look like Mnesia ones: they are anonymous +functions which can do any arbitrary operations on the data and return any +result. If something goes wrong or the anonymous function aborts, nothing is +committed and the database is left untouched as if the transaction code was +never called. + +Under the hood, there are several restrictions and caveats that need to be +understood in order to use transactions in Khepri: +
    +
  • If the anonymous function only reads data from the tree, +there is no specific restrictions on them.
  • +
  • If however the anonymous function needs to modify or +delete data from the database, then the constraints described in the +next section need to be taken into account.
  • +
+ +The nature of the anonymous function is passed as the `ReadWrite' argument to +{@link khepri:transaction/3}. + +=== The constraints imposed by Raft === + +The Raft algorithm is used to achieve consensus among Khepri members +participating in the database. Khepri is a state machine executed on each Ra +node and all instances of that Khepri state machine start with the same state +and modify it identically. The goal is that, after the same list of Ra +commands, all instances have the same state. + +When a new Ra node joins the cluster and therefore participates to the Khepri +database, it starts a new Khepri state machine instance. This instance needs to +apply all Ra commands from an initial state to be on the same page as other +existing instances. + +Likewise, if for any reason, one of the Khepri state machine instance looses +the connection to other members and can't apply Ra commands, then when the link +comes back, it has to catch up. + +All this means that the code to modify the state of the state machines (i.e. +the tree) needs to run on all instances, possibly not at the same time, and +give the exact same result everywhere. + +=== The problem with anonymous functions === + +This is fine for inserts and deletes because the code is part of Khepri and is +deterministic. This poses a problem when transactions are anonymous functions +outside of Khepri's control: +
    +
  1. Khepri must be able to store the anonymous function as a Ra command in Ra's +log. This is the basis for replication and is mandatory to add a new cluster +member or for a lagging member to catch up.
  2. +
  3. The anonymous function must produce exactly the same result in all state +machine instances, regardless of the time it runs, the availability of other +Erlang modules, the state of Erlang processes, files on disk or network +connections, and so on.
  4. +
+ +To achieve that, Horus and {@link khepri_tx} extract the assembly +code of the anonymous function and create a standalone Erlang module based on +it. This module can be stored in Ra's log and executed anywhere without the +presence of the initial anonymous function's module. + +Here is what they do in more details: +
    +
  1. The assembly code of the module hosting the anonymous function is +extracted.
  2. +
  3. The anonymous function code is located inside that assembly code.
  4. +
  5. The code is analyzed to determine: +
      +
    • that it does not perform any forbidden operations (sending or receiving +inter-process messages, use date and time, access files or network connections, +etc.)
    • +
    • what other functions it calls
    • +
  6. +
  7. Based on the listed function calls, the same steps are repeated for all of +them (extract, verify, list calls).
  8. +
  9. Once all the assembly code to have a standalone anonymous function is +collected, an Erlang module is generated.
  10. +
+ +=== How to handle side effects? === + +The consequence of the above constraints is that a transaction function can't +depend on anything else than the tree and it can't have any side effects +outside of the changes to the tree nodes. + +If the transaction needs to have side effects, there are two options: +
    +
  • Perform any side effects after the transaction.
  • +
  • Use {@link khepri:put/3} with {@link khepri_condition:if_payload_version()} +conditions in the path and retry if the put fails because the version changed +in between.
  • +
+ +Here is an example of the second option: + +``` +Path = [stock, wood, <<"lime tree">>], +{ok, #{data := Term, + payload_version := PayloadVersion}} = + khepri_adv:get(StoredId, Path), + +%% Do anything with `Term` that depend on external factors and could have side +%% effects. +Term1 = do_something_with_side_effects(Term), + +PathPattern = [stock, + wood, + #if_all{ + conditions = [ + <<"lime tree">>, + #if_payload_version{version = PayloadVersion}]}], +case khepri:put(StoredId, PathPattern, Term1) of + ok -> + ok; %% `Term1` was stored successfully. + {error, ?khepri_error(mismatching_node, _)} -> + loop() %% Restart the whole function to read/modify/write again. +end. +''' + +== Import and export == + +To backup and restore a Khepri store, you can use {@link khepri:export/4} and +{@link khepri:import/3}. + +They both rely on a backend module which follows the Mnesia Backup & +Restore API. Khepri comes with one backend module called {@link +khepri_export_erlang}. It uses plaintext files containing opaque Erlang terms +formatted as text. It is possible to provide your own backend module obviously. + +To export the content of a running store: + +``` +ok = khepri:export(StoreId, khepri_export_erlang, "export.erl"). +''' + +This will export the entire store. It is possible to export a part of it by +passing a path patterns to select the tree nodes you want to export. + +Later, to import to a running store: + +``` +ok = khepri:import(StoreId, khepri_export_erlang, "export.erl"). +''' + +Importing a backup does not touch unrelated tree nodes. In other words, the +content of the exported store is imported but whatever exists in the target +store remains (except if the import overwrites some tree nodes of course). In +particular, the target store is not reset. + +You can learn more about this import/export feature in the {@link +khepri_import_export} module documentation. + +You can lean more about the provide backend module by reading the documentation +of {@link khepri_export_erlang}. + +== Stored procedures and triggers == + +=== Triggering a function after some event === + +It is possible to associate events with an anonymous function to trigger its +execution when something happens. This is what is usually called a +trigger in databases and Khepri supports this feature. + +Currently, Khepri supports a single type of event, tree changes. This +event is emitted whenever a tree node is being created, updated or deleted. + +Here is a summary of what happens when such an event is emitted: +
    +
  1. Khepri looks up any event filters which could match the emitted +event.
  2. +
  3. If one or more event filters are found, their corresponding stored +procedures are executed.
  4. +
+ +The indicated stored procedure must have been stored in the tree first. + +=== Storing an anonymous function === + +This is possible to store an anonymous function as the payload of a tree node: + +``` +khepri:put( + StoreId, + StoredProcPath, + fun() -> do_something() end). +''' + +The `StoredProcPath' can be any path in the +tree. + +Unlike transaction functions, a stored procedure has no restrictions on what +it is allowed to do. Therefore, a stored procedure can send or receive +messages, read or write from a disk, generate random numbers and so on. + +A stored procedure can accept any numbers of arguments too. + +It is possible to execute a stored procedure directly without configuring any +triggers. To execute a stored procedure, you can call {@link +khepri:run_sproc/3}. Here is an example: + +``` +Ret = khepri:run_sproc( + StoreId, + StoredProcPath, + [] = _Args). +''' + +This works exactly like {@link erlang:apply/2}. The list of arguments passed +to {@link khepri:run_sproc/3} must correspond to the stored procedure +arity. + +=== Configuring a trigger === + +Khepri uses event filters to associate a type of events with a stored +procedure. Khepri supports tree changes events and thus only supports a single +event filter called {@link khepri_evf:tree_event_filter()}. + +An event filter is registered using {@link khepri:register_trigger/4}: + +``` +%% An event filter can be explicitly created using the `khepri_evf' +%% module. This is possible to specify properties at the same time. +EventFilter = khepri_evf:tree([stock, wood, <<"oak">>], %% Required + #{on_actions => [delete], %% Optional + priority => 10}), %% Optional + +%% For ease of use, some terms can be automatically converted to an event +%% filter. Here, a Unix-like path could be used as a tree event filter, though +%% it would have default properties unlike the previous line: +EventFilter = "/:stock/:wood/oak". + +ok = khepri:register_trigger( + StoreId, + TriggerId, + EventFilter, + StoredProcPath). +''' + +In this example, the {@link khepri_evf:tree_event_filter()} structure only +requires the path to monitor. The path can be any path pattern and thus can +have conditions to monitor several nodes at once. + +The `on_actions' property is optional. By default the event filter matches all +tree changes (`create', `update' or `delete'). + +The `priority' property is also optional and defaults to 0. When several event +filters match a given event, they are sorted by priority (a greater integer +means the event filter will be considered first), then by `TriggerId' in +alphabetical order. + +Neither the monitored path nor the stored procedure (pointed to by +`StoredProcPath') need to exist when the event filter is registered. If the +stored procedure doesn't exist when an event occurs, the event filter is +simply ignored. A stored procedure can change after an event filter is +registered as well. + +The stored procedure used for a trigger must accept a single argument, a map +containing properties of the emitted event: + +``` +my_stored_procedure(Props) -> + #{path := Path, + on_action := Action} = Props. +''' + +`Path' is the path to the tree node created, updated or deleted. + +`Action' is the nature of the change (`create', `update' or `delete'). + +The return value of this stored procedure is ignored in the context of a +trigger. + +=== Execution guarantees === + +The stored procedure associated with a trigger (event filter) is executed on +the Ra leader node. + +If the stored procedure throws an exception, it is logged and there is no +retry. + +There is an internal ack mechanism to make sure the stored procedure is +executed at least once. Therefore, if the Ra leader changes before the +execution of the stored procedure could be confirmed to the Khepri state +machine, the execution will be retried on the new Ra leader. + +This means that the stored procedure could be executed multiple times. +Therefore it is important it is idempotent. + +=== Differences with triggers in RDBMS === + +As described earlier, the rationale for triggers in Khepri is that sometimes, +one needs to execute some code with side effects (e.g. sending a message to a +process) after a record was modified in the database. This can't happen in a +transaction because side effects are forbidden. The caller could handle that +after he modifies the record, but the record could be indirectly modified +(deleted) as a consequence of another record being modified or deleted. In +this case, the caller can't do anything. + +Because of the freedom they need, triggers are not allowed to mess +with the database directly. In other words, they must go through the +regular Khepri API like any caller. Triggers do not have any privileges or +blanket approvals to tamper with the data. + +So even though Khepri uses the same naming than many RDBMS, triggers in Khepri +can't have unexpected consequences. + +== Projections == + +Projections are a system within Khepri for maintaining replicated ETS caches +for tree nodes matching a given path pattern. + +Projection resources are created with the {@link khepri_projection:new/3} +function and are registered in a store with {@link +khepri:register_projection/4}: + +``` +ProjectionName = wood_stocks, +ProjectionFun = fun([stock, wood, Kind], Stock) -> {Kind, Stock} end, +Options = #{type => set, read_concurrency => true}, +Projection = khepri_projection:new(ProjectionName, ProjectionFun, Options). + +StoreId = stock, +PathPattern = "/:stock/:wood/*", +khepri:register_projection(StoreId, PathPattern, Projection). +''' + +When a path within the store which matches `PathPattern' changes, the changed +path and data are passed to the given `ProjectionFun' to create ETS objects +which are then stored in the projection's ETS table. + +Projection tables may be queried directly with functions from the {@link ets} +module. + +``` +khepri:put(StoreId, "/:stock/:wood/oak", 100), +ets:lookup(ProjectionName, <<"oak">>, 2). +%%=> [{<<"oak">>,100}] + +khepri:put(StoreId, "/:stock/:wood/oak", 80), +ets:lookup(ProjectionName, <<"oak">>, 2). +%%=> [{<<"oak">>,80}] + +khepri:delete(StoreId, "/:stock/:wood/oak"), +ets:member(ProjectionName, <<"oak">>). +%%=> false +''' + +Use projections to maximize query throughput and/or minimize query latency. + +Projections have some costs though. Expect some increased memory consumption +since information in the projection tables is duplicated between the Khepri +store and ETS. Khepri may also take longer to accomplish writes since +projections are updated by Khepri synchronously when writing to the store. diff --git a/prism-core.min.js.v1.26.0 b/prism-core.min.js.v1.26.0 new file mode 100644 index 00000000..6aa6722e --- /dev/null +++ b/prism-core.min.js.v1.26.0 @@ -0,0 +1 @@ +var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(u){var t=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,n=0,e={},M={manual:u.Prism&&u.Prism.manual,disableWorkerMessageHandler:u.Prism&&u.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof W?new W(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/=l.reach);y+=m.value.length,m=m.next){var k=m.value;if(t.length>n.length)return;if(!(k instanceof W)){var x,b=1;if(h){if(!(x=z(p,y,n,f))||x.index>=n.length)break;var w=x.index,A=x.index+x[0].length,P=y;for(P+=m.value.length;P<=w;)m=m.next,P+=m.value.length;if(P-=m.value.length,y=P,m.value instanceof W)continue;for(var E=m;E!==t.tail&&(Pl.reach&&(l.reach=j);var C=m.prev;S&&(C=I(t,C,S),y+=S.length),q(t,C,b);var N=new W(o,g?M.tokenize(L,g):L,d,L);if(m=I(t,C,N),O&&I(t,m,O),1l.reach&&(l.reach=_.reach)}}}}}}(e,a,n,a.head,0),function(e){var n=[],t=e.head.next;for(;t!==e.tail;)n.push(t.value),t=t.next;return n}(a)},hooks:{all:{},add:function(e,n){var t=M.hooks.all;t[e]=t[e]||[],t[e].push(n)},run:function(e,n){var t=M.hooks.all[e];if(t&&t.length)for(var r,a=0;r=t[a++];)r(n)}},Token:W};function W(e,n,t,r){this.type=e,this.content=n,this.alias=t,this.length=0|(r||"").length}function z(e,n,t,r){e.lastIndex=n;var a=e.exec(t);if(a&&r&&a[1]){var i=a[1].length;a.index+=i,a[0]=a[0].slice(i)}return a}function i(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function I(e,n,t){var r=n.next,a={value:t,prev:n,next:r};return n.next=a,r.prev=a,e.length++,a}function q(e,n,t){for(var r=n.next,a=0;a"+a.content+""},!u.document)return u.addEventListener&&(M.disableWorkerMessageHandler||u.addEventListener("message",function(e){var n=JSON.parse(e.data),t=n.language,r=n.code,a=n.immediateClose;u.postMessage(M.highlight(r,M.languages[t],t)),a&&u.close()},!1)),M;var r=M.util.currentScript();function a(){M.manual||M.highlightAll()}if(r&&(M.filename=r.src,r.hasAttribute("data-manual")&&(M.manual=!0)),!M.manual){var l=document.readyState;"loading"===l||"interactive"===l&&r&&r.defer?document.addEventListener("DOMContentLoaded",a):window.requestAnimationFrame?window.requestAnimationFrame(a):window.setTimeout(a,16)}return M}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); \ No newline at end of file diff --git a/prism-elixir.min.js.v1.26.0 b/prism-elixir.min.js.v1.26.0 new file mode 100644 index 00000000..ec0d974d --- /dev/null +++ b/prism-elixir.min.js.v1.26.0 @@ -0,0 +1 @@ +Prism.languages.elixir={doc:{pattern:/@(?:doc|moduledoc)\s+(?:("""|''')[\s\S]*?\1|("|')(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2)/,inside:{attribute:/^@\w+/,string:/['"][\s\S]+/}},comment:{pattern:/#.*/,greedy:!0},regex:{pattern:/~[rR](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|[^\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[uismxfr]*/,greedy:!0},string:[{pattern:/~[cCsSwW](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|#\{[^}]+\}|#(?!\{)|[^#\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[csa]?/,greedy:!0,inside:{}},{pattern:/("""|''')[\s\S]*?\1/,greedy:!0,inside:{}},{pattern:/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0,inside:{}}],atom:{pattern:/(^|[^:]):\w+/,lookbehind:!0,alias:"symbol"},module:{pattern:/\b[A-Z]\w*\b/,alias:"class-name"},"attr-name":/\b\w+\??:(?!:)/,argument:{pattern:/(^|[^&])&\d+/,lookbehind:!0,alias:"variable"},attribute:{pattern:/@\w+/,alias:"variable"},function:/\b[_a-zA-Z]\w*[?!]?(?:(?=\s*(?:\.\s*)?\()|(?=\/\d))/,number:/\b(?:0[box][a-f\d_]+|\d[\d_]*)(?:\.[\d_]+)?(?:e[+-]?[\d_]+)?\b/i,keyword:/\b(?:after|alias|and|case|catch|cond|def(?:callback|delegate|exception|impl|macro|module|n|np|p|protocol|struct)?|do|else|end|fn|for|if|import|not|or|quote|raise|require|rescue|try|unless|unquote|use|when)\b/,boolean:/\b(?:false|nil|true)\b/,operator:[/\bin\b|&&?|\|[|>]?|\\\\|::|\.\.\.?|\+\+?|-[->]?|<[-=>]|>=|!==?|\B!|=(?:==?|[>~])?|[*\/^]/,{pattern:/([^<])<(?!<)/,lookbehind:!0},{pattern:/([^>])>(?!>)/,lookbehind:!0}],punctuation:/<<|>>|[.,%\[\]{}()]/},Prism.languages.elixir.string.forEach(function(e){e.inside={interpolation:{pattern:/#\{[^}]+\}/,inside:{delimiter:{pattern:/^#\{|\}$/,alias:"punctuation"},rest:Prism.languages.elixir}}}}); \ No newline at end of file diff --git a/prism-erlang.min.js.v1.26.0 b/prism-erlang.min.js.v1.26.0 new file mode 100644 index 00000000..7d041ab6 --- /dev/null +++ b/prism-erlang.min.js.v1.26.0 @@ -0,0 +1 @@ +Prism.languages.erlang={comment:/%.+/,string:{pattern:/"(?:\\.|[^\\"\r\n])*"/,greedy:!0},"quoted-function":{pattern:/'(?:\\.|[^\\'\r\n])+'(?=\()/,alias:"function"},"quoted-atom":{pattern:/'(?:\\.|[^\\'\r\n])+'/,alias:"atom"},boolean:/\b(?:false|true)\b/,keyword:/\b(?:after|case|catch|end|fun|if|of|receive|try|when)\b/,number:[/\$\\?./,/\b\d+#[a-z0-9]+/i,/(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i],function:/\b[a-z][\w@]*(?=\()/,variable:{pattern:/(^|[^@])(?:\b|\?)[A-Z_][\w@]*/,lookbehind:!0},operator:[/[=\/<>:]=|=[:\/]=|\+\+?|--?|[=*\/!]|\b(?:and|andalso|band|bnot|bor|bsl|bsr|bxor|div|not|or|orelse|rem|xor)\b/,{pattern:/(^|[^<])<(?!<)/,lookbehind:!0},{pattern:/(^|[^>])>(?!>)/,lookbehind:!0}],atom:/\b[a-z][\w@]*/,punctuation:/[()[\]{}:;,.#|]|<<|>>/}; \ No newline at end of file diff --git a/prism.css b/prism.css new file mode 100644 index 00000000..8c4cc057 --- /dev/null +++ b/prism.css @@ -0,0 +1 @@ +code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help} \ No newline at end of file diff --git a/prism.js b/prism.js new file mode 100644 index 00000000..4e1e7789 --- /dev/null +++ b/prism.js @@ -0,0 +1 @@ +var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(u){var t=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,n=0,e={},M={manual:u.Prism&&u.Prism.manual,disableWorkerMessageHandler:u.Prism&&u.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof W?new W(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/=l.reach);y+=m.value.length,m=m.next){var k=m.value;if(t.length>n.length)return;if(!(k instanceof W)){var x,b=1;if(h){if(!(x=z(p,y,n,f))||x.index>=n.length)break;var w=x.index,A=x.index+x[0].length,P=y;for(P+=m.value.length;P<=w;)m=m.next,P+=m.value.length;if(P-=m.value.length,y=P,m.value instanceof W)continue;for(var E=m;E!==t.tail&&(Pl.reach&&(l.reach=j);var C=m.prev;S&&(C=I(t,C,S),y+=S.length),q(t,C,b);var N=new W(o,g?M.tokenize(L,g):L,d,L);if(m=I(t,C,N),O&&I(t,m,O),1l.reach&&(l.reach=_.reach)}}}}}}(e,a,n,a.head,0),function(e){var n=[],t=e.head.next;for(;t!==e.tail;)n.push(t.value),t=t.next;return n}(a)},hooks:{all:{},add:function(e,n){var t=M.hooks.all;t[e]=t[e]||[],t[e].push(n)},run:function(e,n){var t=M.hooks.all[e];if(t&&t.length)for(var r,a=0;r=t[a++];)r(n)}},Token:W};function W(e,n,t,r){this.type=e,this.content=n,this.alias=t,this.length=0|(r||"").length}function z(e,n,t,r){e.lastIndex=n;var a=e.exec(t);if(a&&r&&a[1]){var i=a[1].length;a.index+=i,a[0]=a[0].slice(i)}return a}function i(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function I(e,n,t){var r=n.next,a={value:t,prev:n,next:r};return n.next=a,r.prev=a,e.length++,a}function q(e,n,t){for(var r=n.next,a=0;a"+a.content+""},!u.document)return u.addEventListener&&(M.disableWorkerMessageHandler||u.addEventListener("message",function(e){var n=JSON.parse(e.data),t=n.language,r=n.code,a=n.immediateClose;u.postMessage(M.highlight(r,M.languages[t],t)),a&&u.close()},!1)),M;var r=M.util.currentScript();function a(){M.manual||M.highlightAll()}if(r&&(M.filename=r.src,r.hasAttribute("data-manual")&&(M.manual=!0)),!M.manual){var l=document.readyState;"loading"===l||"interactive"===l&&r&&r.defer?document.addEventListener("DOMContentLoaded",a):window.requestAnimationFrame?window.requestAnimationFrame(a):window.setTimeout(a,16)}return M}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism);Prism.languages.erlang={comment:/%.+/,string:{pattern:/"(?:\\.|[^\\"\r\n])*"/,greedy:!0},"quoted-function":{pattern:/'(?:\\.|[^\\'\r\n])+'(?=\()/,alias:"function"},"quoted-atom":{pattern:/'(?:\\.|[^\\'\r\n])+'/,alias:"atom"},boolean:/\b(?:false|true)\b/,keyword:/\b(?:after|case|catch|end|fun|if|of|receive|try|when)\b/,number:[/\$\\?./,/\b\d+#[a-z0-9]+/i,/(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i],function:/\b[a-z][\w@]*(?=\()/,variable:{pattern:/(^|[^@])(?:\b|\?)[A-Z_][\w@]*/,lookbehind:!0},operator:[/[=\/<>:]=|=[:\/]=|\+\+?|--?|[=*\/!]|\b(?:and|andalso|band|bnot|bor|bsl|bsr|bxor|div|not|or|orelse|rem|xor)\b/,{pattern:/(^|[^<])<(?!<)/,lookbehind:!0},{pattern:/(^|[^>])>(?!>)/,lookbehind:!0}],atom:/\b[a-z][\w@]*/,punctuation:/[()[\]{}:;,.#|]|<<|>>/};Prism.languages.elixir={doc:{pattern:/@(?:doc|moduledoc)\s+(?:("""|''')[\s\S]*?\1|("|')(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2)/,inside:{attribute:/^@\w+/,string:/['"][\s\S]+/}},comment:{pattern:/#.*/,greedy:!0},regex:{pattern:/~[rR](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|[^\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[uismxfr]*/,greedy:!0},string:[{pattern:/~[cCsSwW](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|#\{[^}]+\}|#(?!\{)|[^#\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[csa]?/,greedy:!0,inside:{}},{pattern:/("""|''')[\s\S]*?\1/,greedy:!0,inside:{}},{pattern:/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0,inside:{}}],atom:{pattern:/(^|[^:]):\w+/,lookbehind:!0,alias:"symbol"},module:{pattern:/\b[A-Z]\w*\b/,alias:"class-name"},"attr-name":/\b\w+\??:(?!:)/,argument:{pattern:/(^|[^&])&\d+/,lookbehind:!0,alias:"variable"},attribute:{pattern:/@\w+/,alias:"variable"},function:/\b[_a-zA-Z]\w*[?!]?(?:(?=\s*(?:\.\s*)?\()|(?=\/\d))/,number:/\b(?:0[box][a-f\d_]+|\d[\d_]*)(?:\.[\d_]+)?(?:e[+-]?[\d_]+)?\b/i,keyword:/\b(?:after|alias|and|case|catch|cond|def(?:callback|delegate|exception|impl|macro|module|n|np|p|protocol|struct)?|do|else|end|fn|for|if|import|not|or|quote|raise|require|rescue|try|unless|unquote|use|when)\b/,boolean:/\b(?:false|nil|true)\b/,operator:[/\bin\b|&&?|\|[|>]?|\\\\|::|\.\.\.?|\+\+?|-[->]?|<[-=>]|>=|!==?|\B!|=(?:==?|[>~])?|[*\/^]/,{pattern:/([^<])<(?!<)/,lookbehind:!0},{pattern:/([^>])>(?!>)/,lookbehind:!0}],punctuation:/<<|>>|[.,%\[\]{}()]/},Prism.languages.elixir.string.forEach(function(e){e.inside={interpolation:{pattern:/#\{[^}]+\}/,inside:{delimiter:{pattern:/^#\{|\}$/,alias:"punctuation"},rest:Prism.languages.elixir}}}}); \ No newline at end of file diff --git a/prism.min.css.v1.26.0 b/prism.min.css.v1.26.0 new file mode 100644 index 00000000..8c4cc057 --- /dev/null +++ b/prism.min.css.v1.26.0 @@ -0,0 +1 @@ +code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help} \ No newline at end of file diff --git a/stylesheet.css b/stylesheet.css new file mode 100644 index 00000000..7d84b2e0 --- /dev/null +++ b/stylesheet.css @@ -0,0 +1,34 @@ +body > h2.indextitle:first-of-type +{ + font-size: 2em; + text-align: center; + text-transform: capitalize; + border-bottom: 0px; + + padding-top: 115px; + background-image: url("khepri-logo.svg"); + background-size: auto 100px; + background-repeat: no-repeat; + background-position: top; +} + +.navbar +{ + background-color: rgb(245, 242, 240); + border-radius: 6px; +} + +.navbar table tr +{ + background-color: transparent; +} + +.navbar table td:last-of-type +{ + display: none; +} + +.navbar + hr +{ + display: none; +}