From 52126fe290f83947c90dacd4c9f813d00afce2d8 Mon Sep 17 00:00:00 2001 From: ghalliday Date: Wed, 24 Jul 2024 13:00:42 +0000 Subject: [PATCH] deploy: c5563451f826e0c602fb48991655ba6634997889 --- .nojekyll | 0 404.html | 21 + BUILD_ME.html | 98 ++++ README.html | 60 +++ assets/BUILD_ME.md.VZvnVhVL.js | 75 +++ assets/BUILD_ME.md.VZvnVhVL.lean.js | 1 + assets/ECLIDE-AppProperties.syqSIm3l.png | Bin 0 -> 33835 bytes ...ECLIDE-WindowsProtectionError.GSXgMRbZ.png | Bin 0 -> 23825 bytes .../HPCC-12345-build-in-progress.1NPj4pB5.png | Bin 0 -> 184168 bytes assets/README.md.7Qva37_C.js | 37 ++ assets/README.md.7Qva37_C.lean.js | 1 + ...actions-secrets-and-variables.XwHB-cZ0.png | Bin 0 -> 173164 bytes assets/app.CTpl1ysr.js | 7 + assets/chunks/framework.gBlNPWt_.js | 17 + assets/chunks/theme.LCXbL-fc.js | 1 + ...cmake_modules_DOCUMENTATION.md.PBiJbB4I.js | 52 +++ ..._modules_DOCUMENTATION.md.PBiJbB4I.lean.js | 1 + assets/devdoc_CodeGenerator.md.Sr_00DEY.js | 13 + .../devdoc_CodeGenerator.md.Sr_00DEY.lean.js | 1 + assets/devdoc_CodeReviews.md.yXOcGqwe.js | 1 + assets/devdoc_CodeReviews.md.yXOcGqwe.lean.js | 1 + assets/devdoc_CodeSubmissions.md.wf0ooHZx.js | 1 + ...devdoc_CodeSubmissions.md.wf0ooHZx.lean.js | 1 + assets/devdoc_DevDocs.md.p38z6BoL.js | 8 + assets/devdoc_DevDocs.md.p38z6BoL.lean.js | 1 + assets/devdoc_Development.md.qUtv1gjz.js | 3 + assets/devdoc_Development.md.qUtv1gjz.lean.js | 1 + assets/devdoc_GitAuthenticate.md.BC7Bmr5A.js | 12 + ...devdoc_GitAuthenticate.md.BC7Bmr5A.lean.js | 1 + .../devdoc_LDAPSecurityManager.md.6QV0TOn6.js | 1 + ...oc_LDAPSecurityManager.md.6QV0TOn6.lean.js | 1 + assets/devdoc_MemoryManager.md.WzZxL7Sr.js | 1 + .../devdoc_MemoryManager.md.WzZxL7Sr.lean.js | 1 + assets/devdoc_Metrics.md.ipIaUVOL.js | 17 + assets/devdoc_Metrics.md.ipIaUVOL.lean.js | 1 + .../devdoc_NewFileProcessing.md.sdNSfUu7.js | 14 + ...vdoc_NewFileProcessing.md.sdNSfUu7.lean.js | 1 + assets/devdoc_README.md.bTEFReKq.js | 1 + assets/devdoc_README.md.bTEFReKq.lean.js | 1 + assets/devdoc_SecurityConfig.md.z0iGl4iN.js | 1 + .../devdoc_SecurityConfig.md.z0iGl4iN.lean.js | 1 + ..._SecurityUserAuthentication.md.q0qlsyXA.js | 1 + ...rityUserAuthentication.md.q0qlsyXA.lean.js | 1 + assets/devdoc_StyleGuide.md.Mjr9vf6y.js | 51 +++ assets/devdoc_StyleGuide.md.Mjr9vf6y.lean.js | 1 + assets/devdoc_UserBuildAssets.md.5pBqOi7J.js | 34 ++ ...devdoc_UserBuildAssets.md.5pBqOi7J.lean.js | 1 + assets/devdoc_VersionSupport.md.deGoLLFC.js | 1 + .../devdoc_VersionSupport.md.deGoLLFC.lean.js | 1 + assets/devdoc_Workunits.md.jFcYNljW.js | 403 +++++++++++++++++ assets/devdoc_Workunits.md.jFcYNljW.lean.js | 1 + assets/devdoc_newActivity.md.SP0TgdQz.js | 11 + assets/devdoc_newActivity.md.SP0TgdQz.lean.js | 1 + assets/devdoc_roxie.md.ucbDhtEB.js | 8 + assets/devdoc_roxie.md.ucbDhtEB.lean.js | 1 + ...doc_userdoc_AzureTipsTricks.md.aFaptA0d.js | 1 + ...serdoc_AzureTipsTricks.md.aFaptA0d.lean.js | 1 + assets/devdoc_userdoc_Blogs.md.-JKS2x0f.js | 1 + .../devdoc_userdoc_Blogs.md.-JKS2x0f.lean.js | 1 + assets/devdoc_userdoc_README.md.D4Kid-3h.js | 1 + .../devdoc_userdoc_README.md.D4Kid-3h.lean.js | 1 + ...vdoc_userdoc_WikiGuidelines.md.QHJ-8Jox.js | 1 + ...userdoc_WikiGuidelines.md.QHJ-8Jox.lean.js | 1 + ...userdoc_azure_TipsAndTricks.md.LEeeHH_U.js | 54 +++ ...oc_azure_TipsAndTricks.md.LEeeHH_U.lean.js | 1 + .../devdoc_userdoc_roxie_FAQ.md.nQti9zzo.js | 24 + ...vdoc_userdoc_roxie_FAQ.md.nQti9zzo.lean.js | 1 + ...ubleshoot_ClientsToolIssues.md.Oxqyv8dQ.js | 1 + ...hoot_ClientsToolIssues.md.Oxqyv8dQ.lean.js | 1 + ...cl_ecl-bundle_DOCUMENTATION.md.Z4TCf1dS.js | 1 + ...l-bundle_DOCUMENTATION.md.Z4TCf1dS.lean.js | 1 + assets/ecllibrary_StyleGuide.md.gc1ZYvqu.js | 20 + .../ecllibrary_StyleGuide.md.gc1ZYvqu.lean.js | 1 + assets/index.md.gO4OaP_B.js | 1 + assets/index.md.gO4OaP_B.lean.js | 1 + .../inter-italic-cyrillic-ext.OVycGSDq.woff2 | Bin 0 -> 28332 bytes assets/inter-italic-cyrillic.-nLMcIwj.woff2 | Bin 0 -> 17824 bytes assets/inter-italic-greek-ext.hznxWNZO.woff2 | Bin 0 -> 12188 bytes assets/inter-italic-greek.PSfer2Kc.woff2 | Bin 0 -> 23264 bytes assets/inter-italic-latin-ext.RnFly65-.woff2 | Bin 0 -> 63552 bytes assets/inter-italic-latin.27E69YJn.woff2 | Bin 0 -> 46048 bytes assets/inter-italic-vietnamese.xzQHe1q1.woff2 | Bin 0 -> 8784 bytes .../inter-roman-cyrillic-ext.8T9wMG5w.woff2 | Bin 0 -> 26600 bytes assets/inter-roman-cyrillic.jIZ9REo5.woff2 | Bin 0 -> 16780 bytes assets/inter-roman-greek-ext.9JiNzaSO.woff2 | Bin 0 -> 11808 bytes assets/inter-roman-greek.Cb5wWeGA.woff2 | Bin 0 -> 21776 bytes assets/inter-roman-latin-ext.GZWE-KO4.woff2 | Bin 0 -> 59608 bytes assets/inter-roman-latin.bvIUbFQP.woff2 | Bin 0 -> 42464 bytes assets/inter-roman-vietnamese.paY3CzEB.woff2 | Bin 0 -> 8492 bytes assets/repository-tag-tab.0n7rea9Z.png | Bin 0 -> 72083 bytes assets/style.BC59MYRm.css | 1 + assets/system_httplib_README.md.ayRNZub-.js | 287 ++++++++++++ .../system_httplib_README.md.ayRNZub-.lean.js | 1 + ...stem_masking_include_readme.md.i5B-dOzb.js | 93 ++++ ...masking_include_readme.md.i5B-dOzb.lean.js | 1 + ...g_plugins_datamasker_readme.md.iLx0XZB6.js | 24 + ...gins_datamasker_readme.md.iLx0XZB6.lean.js | 1 + ..._plugins_jwtSecurity_README.md.1wwh5vak.js | 8 + ...ins_jwtSecurity_README.md.1wwh5vak.lean.js | 1 + ...sting_regress_cleanupReadme.md.WHcEY6YL.js | 1 + ..._regress_cleanupReadme.md.WHcEY6YL.lean.js | 1 + ...lyzers_corporate_tmp_README.md.QgDQ6IeH.js | 1 + ...s_corporate_tmp_README.md.QgDQ6IeH.lean.js | 1 + assets/tools_esdlcmd_README.md.kugmSdg_.js | 307 +++++++++++++ .../tools_esdlcmd_README.md.kugmSdg_.lean.js | 1 + assets/tools_tagging_README.md.aY1N0cJO.js | 13 + .../tools_tagging_README.md.aY1N0cJO.lean.js | 1 + cmake_modules/DOCUMENTATION.html | 75 +++ devdoc/CodeGenerator.html | 36 ++ devdoc/CodeReviews.html | 24 + devdoc/CodeSubmissions.html | 24 + devdoc/DevDocs.html | 31 ++ devdoc/Development.html | 26 ++ devdoc/GitAuthenticate.html | 35 ++ devdoc/LDAPSecurityManager.html | 24 + devdoc/MemoryManager.html | 24 + devdoc/Metrics.html | 40 ++ devdoc/NewFileProcessing.html | 37 ++ devdoc/README.html | 24 + devdoc/SecurityConfig.html | 24 + devdoc/SecurityUserAuthentication.html | 24 + devdoc/StyleGuide.html | 74 +++ devdoc/UserBuildAssets.html | 57 +++ devdoc/VersionSupport.html | 24 + devdoc/Workunits.html | 426 ++++++++++++++++++ devdoc/hpcc-icon.png | Bin 0 -> 6513 bytes devdoc/hpccsystems.png | Bin 0 -> 28462 bytes devdoc/hpccsystemsdark.png | Bin 0 -> 21701 bytes devdoc/newActivity.html | 34 ++ devdoc/roxie.html | 31 ++ devdoc/userdoc/AzureTipsTricks.html | 24 + devdoc/userdoc/Blogs.html | 24 + devdoc/userdoc/README.html | 24 + devdoc/userdoc/WikiGuidelines.html | 24 + devdoc/userdoc/azure/TipsAndTricks.html | 77 ++++ devdoc/userdoc/roxie/FAQ.html | 47 ++ .../troubleshoot/ClientsToolIssues.html | 24 + ecl/ecl-bundle/DOCUMENTATION.html | 24 + ecllibrary/StyleGuide.html | 43 ++ hashmap.json | 1 + index.html | 24 + system/httplib/README.html | 310 +++++++++++++ system/masking/include/readme.html | 116 +++++ system/masking/plugins/datamasker/readme.html | 47 ++ .../security/plugins/jwtSecurity/README.html | 31 ++ testing/regress/cleanupReadme.html | 24 + .../ecl/analyzers/corporate/tmp/README.html | 24 + tools/esdlcmd/README.html | 330 ++++++++++++++ tools/tagging/README.html | 36 ++ 149 files changed, 4178 insertions(+) create mode 100644 .nojekyll create mode 100644 404.html create mode 100644 BUILD_ME.html create mode 100644 README.html create mode 100644 assets/BUILD_ME.md.VZvnVhVL.js create mode 100644 assets/BUILD_ME.md.VZvnVhVL.lean.js create mode 100644 assets/ECLIDE-AppProperties.syqSIm3l.png create mode 100644 assets/ECLIDE-WindowsProtectionError.GSXgMRbZ.png create mode 100644 assets/HPCC-12345-build-in-progress.1NPj4pB5.png create mode 100644 assets/README.md.7Qva37_C.js create mode 100644 assets/README.md.7Qva37_C.lean.js create mode 100644 assets/actions-secrets-and-variables.XwHB-cZ0.png create mode 100644 assets/app.CTpl1ysr.js create mode 100644 assets/chunks/framework.gBlNPWt_.js create mode 100644 assets/chunks/theme.LCXbL-fc.js create mode 100644 assets/cmake_modules_DOCUMENTATION.md.PBiJbB4I.js create mode 100644 assets/cmake_modules_DOCUMENTATION.md.PBiJbB4I.lean.js create mode 100644 assets/devdoc_CodeGenerator.md.Sr_00DEY.js create mode 100644 assets/devdoc_CodeGenerator.md.Sr_00DEY.lean.js create mode 100644 assets/devdoc_CodeReviews.md.yXOcGqwe.js create mode 100644 assets/devdoc_CodeReviews.md.yXOcGqwe.lean.js create mode 100644 assets/devdoc_CodeSubmissions.md.wf0ooHZx.js create mode 100644 assets/devdoc_CodeSubmissions.md.wf0ooHZx.lean.js create mode 100644 assets/devdoc_DevDocs.md.p38z6BoL.js create mode 100644 assets/devdoc_DevDocs.md.p38z6BoL.lean.js create mode 100644 assets/devdoc_Development.md.qUtv1gjz.js create mode 100644 assets/devdoc_Development.md.qUtv1gjz.lean.js create mode 100644 assets/devdoc_GitAuthenticate.md.BC7Bmr5A.js create mode 100644 assets/devdoc_GitAuthenticate.md.BC7Bmr5A.lean.js create mode 100644 assets/devdoc_LDAPSecurityManager.md.6QV0TOn6.js create mode 100644 assets/devdoc_LDAPSecurityManager.md.6QV0TOn6.lean.js create mode 100644 assets/devdoc_MemoryManager.md.WzZxL7Sr.js create mode 100644 assets/devdoc_MemoryManager.md.WzZxL7Sr.lean.js create mode 100644 assets/devdoc_Metrics.md.ipIaUVOL.js create mode 100644 assets/devdoc_Metrics.md.ipIaUVOL.lean.js create mode 100644 assets/devdoc_NewFileProcessing.md.sdNSfUu7.js create mode 100644 assets/devdoc_NewFileProcessing.md.sdNSfUu7.lean.js create mode 100644 assets/devdoc_README.md.bTEFReKq.js create mode 100644 assets/devdoc_README.md.bTEFReKq.lean.js create mode 100644 assets/devdoc_SecurityConfig.md.z0iGl4iN.js create mode 100644 assets/devdoc_SecurityConfig.md.z0iGl4iN.lean.js create mode 100644 assets/devdoc_SecurityUserAuthentication.md.q0qlsyXA.js create mode 100644 assets/devdoc_SecurityUserAuthentication.md.q0qlsyXA.lean.js create mode 100644 assets/devdoc_StyleGuide.md.Mjr9vf6y.js create mode 100644 assets/devdoc_StyleGuide.md.Mjr9vf6y.lean.js create mode 100644 assets/devdoc_UserBuildAssets.md.5pBqOi7J.js create mode 100644 assets/devdoc_UserBuildAssets.md.5pBqOi7J.lean.js create mode 100644 assets/devdoc_VersionSupport.md.deGoLLFC.js create mode 100644 assets/devdoc_VersionSupport.md.deGoLLFC.lean.js create mode 100644 assets/devdoc_Workunits.md.jFcYNljW.js create mode 100644 assets/devdoc_Workunits.md.jFcYNljW.lean.js create mode 100644 assets/devdoc_newActivity.md.SP0TgdQz.js create mode 100644 assets/devdoc_newActivity.md.SP0TgdQz.lean.js create mode 100644 assets/devdoc_roxie.md.ucbDhtEB.js create mode 100644 assets/devdoc_roxie.md.ucbDhtEB.lean.js create mode 100644 assets/devdoc_userdoc_AzureTipsTricks.md.aFaptA0d.js create mode 100644 assets/devdoc_userdoc_AzureTipsTricks.md.aFaptA0d.lean.js create mode 100644 assets/devdoc_userdoc_Blogs.md.-JKS2x0f.js create mode 100644 assets/devdoc_userdoc_Blogs.md.-JKS2x0f.lean.js create mode 100644 assets/devdoc_userdoc_README.md.D4Kid-3h.js create mode 100644 assets/devdoc_userdoc_README.md.D4Kid-3h.lean.js create mode 100644 assets/devdoc_userdoc_WikiGuidelines.md.QHJ-8Jox.js create mode 100644 assets/devdoc_userdoc_WikiGuidelines.md.QHJ-8Jox.lean.js create mode 100644 assets/devdoc_userdoc_azure_TipsAndTricks.md.LEeeHH_U.js create mode 100644 assets/devdoc_userdoc_azure_TipsAndTricks.md.LEeeHH_U.lean.js create mode 100644 assets/devdoc_userdoc_roxie_FAQ.md.nQti9zzo.js create mode 100644 assets/devdoc_userdoc_roxie_FAQ.md.nQti9zzo.lean.js create mode 100644 assets/devdoc_userdoc_troubleshoot_ClientsToolIssues.md.Oxqyv8dQ.js create mode 100644 assets/devdoc_userdoc_troubleshoot_ClientsToolIssues.md.Oxqyv8dQ.lean.js create mode 100644 assets/ecl_ecl-bundle_DOCUMENTATION.md.Z4TCf1dS.js create mode 100644 assets/ecl_ecl-bundle_DOCUMENTATION.md.Z4TCf1dS.lean.js create mode 100644 assets/ecllibrary_StyleGuide.md.gc1ZYvqu.js create mode 100644 assets/ecllibrary_StyleGuide.md.gc1ZYvqu.lean.js create mode 100644 assets/index.md.gO4OaP_B.js create mode 100644 assets/index.md.gO4OaP_B.lean.js create mode 100644 assets/inter-italic-cyrillic-ext.OVycGSDq.woff2 create mode 100644 assets/inter-italic-cyrillic.-nLMcIwj.woff2 create mode 100644 assets/inter-italic-greek-ext.hznxWNZO.woff2 create mode 100644 assets/inter-italic-greek.PSfer2Kc.woff2 create mode 100644 assets/inter-italic-latin-ext.RnFly65-.woff2 create mode 100644 assets/inter-italic-latin.27E69YJn.woff2 create mode 100644 assets/inter-italic-vietnamese.xzQHe1q1.woff2 create mode 100644 assets/inter-roman-cyrillic-ext.8T9wMG5w.woff2 create mode 100644 assets/inter-roman-cyrillic.jIZ9REo5.woff2 create mode 100644 assets/inter-roman-greek-ext.9JiNzaSO.woff2 create mode 100644 assets/inter-roman-greek.Cb5wWeGA.woff2 create mode 100644 assets/inter-roman-latin-ext.GZWE-KO4.woff2 create mode 100644 assets/inter-roman-latin.bvIUbFQP.woff2 create mode 100644 assets/inter-roman-vietnamese.paY3CzEB.woff2 create mode 100644 assets/repository-tag-tab.0n7rea9Z.png create mode 100644 assets/style.BC59MYRm.css create mode 100644 assets/system_httplib_README.md.ayRNZub-.js create mode 100644 assets/system_httplib_README.md.ayRNZub-.lean.js create mode 100644 assets/system_masking_include_readme.md.i5B-dOzb.js create mode 100644 assets/system_masking_include_readme.md.i5B-dOzb.lean.js create mode 100644 assets/system_masking_plugins_datamasker_readme.md.iLx0XZB6.js create mode 100644 assets/system_masking_plugins_datamasker_readme.md.iLx0XZB6.lean.js create mode 100644 assets/system_security_plugins_jwtSecurity_README.md.1wwh5vak.js create mode 100644 assets/system_security_plugins_jwtSecurity_README.md.1wwh5vak.lean.js create mode 100644 assets/testing_regress_cleanupReadme.md.WHcEY6YL.js create mode 100644 assets/testing_regress_cleanupReadme.md.WHcEY6YL.lean.js create mode 100644 assets/testing_regress_ecl_analyzers_corporate_tmp_README.md.QgDQ6IeH.js create mode 100644 assets/testing_regress_ecl_analyzers_corporate_tmp_README.md.QgDQ6IeH.lean.js create mode 100644 assets/tools_esdlcmd_README.md.kugmSdg_.js create mode 100644 assets/tools_esdlcmd_README.md.kugmSdg_.lean.js create mode 100644 assets/tools_tagging_README.md.aY1N0cJO.js create mode 100644 assets/tools_tagging_README.md.aY1N0cJO.lean.js create mode 100644 cmake_modules/DOCUMENTATION.html create mode 100644 devdoc/CodeGenerator.html create mode 100644 devdoc/CodeReviews.html create mode 100644 devdoc/CodeSubmissions.html create mode 100644 devdoc/DevDocs.html create mode 100644 devdoc/Development.html create mode 100644 devdoc/GitAuthenticate.html create mode 100644 devdoc/LDAPSecurityManager.html create mode 100644 devdoc/MemoryManager.html create mode 100644 devdoc/Metrics.html create mode 100644 devdoc/NewFileProcessing.html create mode 100644 devdoc/README.html create mode 100644 devdoc/SecurityConfig.html create mode 100644 devdoc/SecurityUserAuthentication.html create mode 100644 devdoc/StyleGuide.html create mode 100644 devdoc/UserBuildAssets.html create mode 100644 devdoc/VersionSupport.html create mode 100644 devdoc/Workunits.html create mode 100644 devdoc/hpcc-icon.png create mode 100644 devdoc/hpccsystems.png create mode 100644 devdoc/hpccsystemsdark.png create mode 100644 devdoc/newActivity.html create mode 100644 devdoc/roxie.html create mode 100644 devdoc/userdoc/AzureTipsTricks.html create mode 100644 devdoc/userdoc/Blogs.html create mode 100644 devdoc/userdoc/README.html create mode 100644 devdoc/userdoc/WikiGuidelines.html create mode 100644 devdoc/userdoc/azure/TipsAndTricks.html create mode 100644 devdoc/userdoc/roxie/FAQ.html create mode 100644 devdoc/userdoc/troubleshoot/ClientsToolIssues.html create mode 100644 ecl/ecl-bundle/DOCUMENTATION.html create mode 100644 ecllibrary/StyleGuide.html create mode 100644 hashmap.json create mode 100644 index.html create mode 100644 system/httplib/README.html create mode 100644 system/masking/include/readme.html create mode 100644 system/masking/plugins/datamasker/readme.html create mode 100644 system/security/plugins/jwtSecurity/README.html create mode 100644 testing/regress/cleanupReadme.html create mode 100644 testing/regress/ecl/analyzers/corporate/tmp/README.html create mode 100644 tools/esdlcmd/README.html create mode 100644 tools/tagging/README.html diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000000..e69de29bb2d diff --git a/404.html b/404.html new file mode 100644 index 00000000000..3a6a1b8655f --- /dev/null +++ b/404.html @@ -0,0 +1,21 @@ + + + + + + 404 | HPCC Platform + + + + + + + + + + +
Skip to content

404

PAGE NOT FOUND

But if you don't change your direction, and if you keep looking, you may end up where you are heading.

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/BUILD_ME.html b/BUILD_ME.html new file mode 100644 index 00000000000..0fca4df8317 --- /dev/null +++ b/BUILD_ME.html @@ -0,0 +1,98 @@ + + + + + + Build Instructions | HPCC Platform + + + + + + + + + + + + + +
Skip to content
HPCC SYSTEMS software Copyright (C) 2019 HPCC Systems.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+https://hpccsystems.com

Build Instructions

Prerequisites

Ubuntu

Ubuntu 19.10/19.04/18.04

sudo apt-get install cmake bison flex build-essential binutils-dev libldap2-dev libcppunit-dev libicu-dev libxslt1-dev \
+zlib1g-dev libboost-regex-dev libarchive-dev python-dev libv8-dev default-jdk libapr1-dev libaprutil1-dev libiberty-dev \
+libhiredis-dev libtbb-dev libxalan-c-dev libnuma-dev nodejs libevent-dev libatlas-base-dev libblas-dev python3-dev \
+default-libmysqlclient-dev libsqlite3-dev r-base-dev r-cran-rcpp r-cran-rinside r-cran-inline libmemcached-dev \
+libcurl4-openssl-dev pkg-config libtool autotools-dev automake libssl-dev
+

Ubuntu 16.04

sudo apt-get install cmake bison flex build-essential binutils-dev libldap2-dev libcppunit-dev libicu-dev libxslt1-dev \
+zlib1g-dev libboost-regex-dev libssl-dev libarchive-dev python-dev libv8-dev default-jdk libapr1-dev libaprutil1-dev \
+libiberty-dev libhiredis-dev libtbb-dev libxalan-c-dev libnuma-dev libevent-dev libatlas-base-dev libblas-dev \
+libatlas-dev python3-dev libcurl4-openssl-dev libtool autotools-dev automake
+
Additional information for building versions prior to 7.0 on Ubuntu 18.04

Get openssl-1.0.2o.tar.gz from https://www.openssl.org/source/

unpack, build, and install:

./config -fPIC shared
+make
+make install
+

This installs to /usr/local/ssl by default.

Build the platform with the following additional CMake options:

-DOPENSSL_LIBRARIES=/usr/local/ssl/lib/libssl.so -DOPENSSL_SSL_LIBRARY=/usr/local/ssl/lib/libssl.so
+

CentOS

Regardless of which version of CentOS you will be building on, it is suggested that you enable the EPEL repository

bash
sudo yum install -y epel-release

CentOS 7:

sudo yum install gcc-c++ gcc make bison flex binutils-devel openldap-devel libicu-devel libxslt-devel libarchive-devel \
+boost-devel openssl-devel apr-devel apr-util-devel hiredis-devel numactl-devel mariadb-devel libevent-devel tbb-devel \
+atlas-devel python34 libmemcached-devel sqlite-devel v8-devel python-devel python34-devel java-1.8.0-openjdk-devel \
+R-core-devel R-Rcpp-devel R-inline R-RInside-devel nodejs cmake3 rpm-build libcurl-devel
+

CentOS 6.4:

sudo yum install gcc-c++ gcc make bison flex binutils-devel openldap-devel libicu-devel libxslt-devel libarchive-devel \
+boost-devel openssl-devel apr-devel apr-util-devel hiredis-devel numactl-devel libmysqlclient-dev libevent-devel \
+tbb-devel atlas-devel python34 R-core-devel R-Rcpp-devel R-inline R-RInside-devel nodejs libcurl-devel
+

Other Platforms

Fedora 19:

sudo yum install gcc-c++ gcc make fedora-packager cmake bison flex binutils-devel openldap-devel libicu-devel  \
+xerces-c-devel xalan-c-devel libarchive-devel boost-devel openssl-devel apr-devel apr-util-devel
+

Fedora 23:

sudo dnf install gcc-c++ gcc make fedora-packager cmake bison flex binutils-devel openldap-devel libicu-devel \
+xerces-c-devel xalan-c-devel libarchive-devel boost-devel openssl-devel apr-devel apr-util-devel numactl-devel \
+tbb-devel libxslt-devel nodejs
+

Mac (Snow Leopard):

sudo port install bison flex binutils openldap icu xalanc zlib boost openssl libarchive
+

You can disable some functionality in order to reduce the list of required components, if necessary. Optional components, such as the plugins for interfacing to external languages, will be disabled automatically if the required libraries and headers are not found at build time.

Optional dependencies:

To build with support for all the plugins for third party embedded code, additional dependencies may be required. If not found, the system will default to skipping those components.

Windows

From 7.4 the build system has been changed to make it easier to build on windows. It is dependent on two other projects chocolatey and vcpkg for installing the dependencies:

Install chocolatey:

https://chocolatey.org/install

Use that to install bison/flex:

choco install winflexbison3

Install vcpkg:

git clone https://github.com/Microsoft/vcpkg
+cd vcpkg
+bootstrap-vcpkg

Use vcpkg to install various packages:

vcpkg install zlib
+vcpkg install boost
+vcpkg install icu
+vcpkg install libxslt
+vcpkg install tbb
+vcpkg install cppunit
+vcpkg install libarchive
+vcpkg install apr
+vcpkg install apr-util

You may need to force vcpkg to build the correct version, e.g. for 64bit:

vcpkg install zlib zlib:x64-windows

Other required third-party packages

Nodejs

NodeJS (version 8.x.x LTS recommended) is used to package ECL Watch and related web pages.

To install nodeJs on Linux based systems, try:

   curl -sL https://deb.nodesource.com/setup_8.x | sudo bash -
+   sudo apt-get install -y nodejs

If these instructions do not work on your system, refer to the detailed instructions available here

Building with R Support:

First insure that the R language is installed on your system. For Ubuntu use sudo apt-get install r-base-dev. For centos distributions use sudo yum install -y R-core-devel.

To install the prerequisites for building R support, use the following for all distros:

wget https://cran.r-project.org/src/contrib/00Archive/Rcpp/Rcpp_0.12.1.tar.gz
+wget https://cran.r-project.org/src/contrib/00Archive/RInside/RInside_0.2.12.tar.gz
+wget http://cran.r-project.org/src/contrib/inline_0.3.14.tar.gz
+sudo R CMD INSTALL Rcpp_0.12.1.tar.gz RInside_0.2.12.tar.gz inline_0.3.14.tar.gz

Get Latest HPCC Systems Sources

Visit Git-step-by-step for full instructions.

To get started quickly, simply:

bash
git clone [-b <branch name>] --recurse-submodules https://github.com/hpcc-systems/HPCC-Platform.git

Where [ ] denotes an optional argument.

CMake

The minimum version of CMake required to build the HPCC Platform is 3.3.2 on Linux. You may need to download a recent version here at cmake.org.

Now you need to run CMake to populate your build directory with Makefiles and special configuration to build HPCC, packages, tests, etc.

A separate directory is required for the build files. In the examples below, the source directory is contained in ~/hpcc and the build directory is ~/hpcc/build. mkdir ~/hpcc/build

All cmake commands would normally need to be executed within the build directory: cd ~/hpcc/build

For release builds, do: cmake ../src

To enable a specific plugin in the build:

bash
    cmake –D<Plugin Name>=ON ../src 
+    make –j6 package

These are the current supported plugins:

  • CASSANDRAEMBED
  • REMBED
  • V8EMBED
  • MEMCACHED
  • PYEMBED
  • REDIS
  • JAVAEMBED
  • KAFKA
  • SQLITE3EMBED
  • MYSQLEMBED

If testing during development and you may want to include plugins (except R) in the package: cmake -DTEST_PLUGINS=ON ../src

To produce a debug build: cmake -DCMAKE_BUILD_TYPE:STRING=Debug ../src

To build the client tools only: cmake -DCLIENTTOOLS_ONLY=1 ../src

To enable signing of the ecl standard library, ensure you have a gpg private key loaded into your gpg keychain and do: # Add -DSIGN_MODULES_KEYID and -DSIGN_MODULES_PASSPHRASE if applicable cmake -DSIGN_MODULES=ON ../src

In some cases, users have found that when packaging for Ubuntu, the dpkg-shlibdeps portion of the packaging adds an exceptional amount of time to the build process. To turn this off (and to create a package without dynamic dependency generation) do: cmake -DUSE_SHLIBDEPS=OFF ../src

CMake will check for necessary dependencies like binutils, boost regex, cppunit, pthreads, etc. If everything is correct, it'll create the necessary Makefiles and you're ready to build HPCC.

NOTE:

We default to using libxslt in place of Xalan for xslt support. Should you prefer to use libxalan, you can specify -DUSE_LIBXALAN on the cmake command line.

Building

You may build by either using make:

# Using -j option here to specify 6 compilation threads (suitable for quad core cpu)
+make -j6
+

Or, alternatively you can call a build system agnostic variant (works with make, ninja, XCode, Visual Studio etc.):

cmake --build .
+

This will make all binaries, libraries and scripts necessary to create the package.

  • Executables will be created in ./<releasemode>/bin and ./<releasemode>/libs

Creating a package

The recommended method to install HPCC Systems on your machine (even for testing) is to use distro packages. CMake has already detected your system, so it know whether to generate TGZ files, DEB or RPM packages.

Just type:

make package
+

Alternatively you can use the build system agnostic variant:

cmake --build . --target package
+

and it will create the appropriate package for you to install. The package file will be created inside the build directory.

Installing the package

Install the package:

sudo dpkg -i hpccsystems-platform-community_6.0.0-trunk0trusty_amd64.deb
+

(note that the name of the package you have just built will depend on the branch you checked out, the distro, and other options).

Hint: missing dependencies may be fixed with:

sudo apt-get -f install
+

(see here for Ubuntu based installation).

To build client tools for MacOS:

Note: These instructions may not be up to date

  • Check out sources (for example, to directory ~/hpcc)
  • Fetch all sub-modules with:
   git submodule update --init --recursive
  • You many need to install some 3rd-party dev packages using macports or brew. (brew installs shown below)
   brew install icu4c
+   brew install boost
+   brew install libarchive
+   brew install bison
+   brew install openldap

** Also make sure that bison is ahead of the system bison on your path. bison --version (The result should be > 2.4.1 )

** OS X has LDAP installed, but when compiling against it (/System/Library/Frameworks/LDAP.framework/Headers/ldap.h) you will get a #include nested too deeply, which is why you should install openldap.

  • Create a build directory - either as a child of hpcc or elsewhere
  • cd to the build directory
  • Use clang to build the clienttools (gcc4.2 cores when compiling some of the sources):
   export CC=/usr/bin/clang
+   export CXX=/usr/bin/clang++
+   cmake ../ -DICU_LIBRARIES=/usr/local/opt/icu4c/lib/libicuuc.dylib -DICU_INCLUDE_DIR=/usr/local/opt/icu4c/include \
+   -DLIBARCHIVE_INCLUDE_DIR=/usr/local/opt/libarchive/include \
+   -DLIBARCHIVE_LIBRARIES=/usr/local/opt/libarchive/lib/libarchive.dylib \
+   -DBOOST_REGEX_LIBRARIES=/usr/local/opt/boost/lib -DBOOST_REGEX_INCLUDE_DIR=/usr/local/opt/boost/include \
+   -DCLIENTTOOLS_ONLY=true \
+   -DUSE_OPENLDAP=true -DOPENLDAP_INCLUDE_DIR=/usr/local/opt/openldap/include \
+   -DOPENLDAP_LIBRARIES=/usr/local/opt/openldap/lib/libldap_r.dylib
  • To build the makefiles just created above, run make
  • Executables will be created in ./<releasemode>/bin and ./<releasemode>/libs
  • To create a .dmg to install, run make package

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/README.html b/README.html new file mode 100644 index 00000000000..f31b32dd978 --- /dev/null +++ b/README.html @@ -0,0 +1,60 @@ + + + + + + Description / Rationale | HPCC Platform + + + + + + + + + + + + + +
Skip to content

Description / Rationale

HPCC Systems offers an enterprise ready, open source supercomputing platform to solve big data problems. As compared to Hadoop, the platform offers analysis of big data using less code and less nodes for greater efficiencies and offers a single programming language, a single platform and a single architecture for efficient processing. HPCC Systems is a technology division of LexisNexis Risk Solutions.

Getting Started

Release + Support Policy

In general, a new version of the HPCC Platform is released every 3 months. These releases can be either Major (with breaking changes) or Minor (with new features). Maintenance and security releases (point releases) are typically made weekly, and may occasionally include technical previews.

Maintenance releases are supported for the current and previous release, while security releases are supported for the current and previous two releases:

mermaid
---
+displayMode: compact
+---
+gantt
+    title Release Schedule
+    axisFormat %Y-Q%q
+    tickInterval 3month
+    dateFormat YYYY-MM-DD
+    section v8.12.x
+        Active:          active, 2023-02-07, 5M
+        Critical:        3M
+        Security:        6M
+    section v9.0.x
+        Active:          active, 2023-04-03, 6M
+        Critical:        3M
+        Security:        6M
+    section v9.2.x
+        Active:          active, 2023-07-04, 9M
+        Critical:        3M
+        Security:        3M
+    section v9.4.x
+        Active:          active, 2023-10-04, 9M
+        Critical:        3M
+        Security:        3M
+    section v9.6.x
+        Active:          active, 2024-04-04, 6M
+        Critical:        3M
+        Security:        3M
+    section v9.8.x
+        Active:          active, 2024-07-02, 6M
+        Critical:        3M
+        Security:        3M
+    section v9.10.x
+        Active:          active, 2024-10-01, 6M
+        Critical:        3M
+        Security:        3M

Architecture

The HPCC Systems architecture incorporates the Thor and Roxie clusters as well as common middleware components, an external communications layer, client interfaces which provide both end-user services and system management tools, and auxiliary components to support monitoring and to facilitate loading and storing of filesystem data from external sources. An HPCC environment can include only Thor clusters, or both Thor and Roxie clusters. Each of these cluster types is described in more detail in the following sections below the architecture diagram.

Thor

Thor (the Data Refinery Cluster) is responsible for consuming vast amounts of data, transforming, linking and indexing that data. It functions as a distributed file system with parallel processing power spread across the nodes. A cluster can scale from a single node to thousands of nodes.

  • Single-threaded
  • Distributed parallel processing
  • Distributed file system
  • Powerful parallel processing programming language (ECL)
  • Optimized for Extraction, Transformation, Loading, Sorting, Indexing and Linking
  • Scales from 1-1000s of nodes

Roxie

Roxie (the Query Cluster) provides separate high-performance online query processing and data warehouse capabilities. Roxie (Rapid Online XML Inquiry Engine) is the data delivery engine used in HPCC to serve data quickly and can support many thousands of requests per node per second.

  • Multi-threaded
  • Distributed parallel processing
  • Distributed file system
  • Powerful parallel processing programming language (ECL)
  • Optimized for concurrent query processing
  • Scales from 1-1000s of nodes

ECL

ECL (Enterprise Control Language) is the powerful programming language that is ideally suited for the manipulation of Big Data.

  • Transparent and implicitly parallel programming language
  • Non-procedural and dataflow oriented
  • Modular, reusable, extensible syntax
  • Combines data representation and algorithm implementation
  • Easily extend using C++ libraries
  • ECL is compiled into optimized C++

ECL IDE

ECL IDE is a modern IDE used to code, debug and monitor ECL programs.

  • Access to shared source code repositories
  • Complete development, debugging and testing environment for developing ECL dataflow programs
  • Access to the ECLWatch tool is built-in, allowing developers to watch job graphs as they are executing
  • Access to current and historical job workunits

ESP

ESP (Enterprise Services Platform) provides an easy to use interface to access ECL queries using XML, HTTP, SOAP and REST.

  • Standards-based interface to access ECL functions

Developer documentation

The following links describe the structure of the system and detail some of the key components:

Regression test

sh
cd /opt/HPCCSystems/testing/regress
+./ecl-test query --target thor nlppp.ecl

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/assets/BUILD_ME.md.VZvnVhVL.js b/assets/BUILD_ME.md.VZvnVhVL.js new file mode 100644 index 00000000000..26086976334 --- /dev/null +++ b/assets/BUILD_ME.md.VZvnVhVL.js @@ -0,0 +1,75 @@ +import{_ as e,c as a,o as s,V as i}from"./chunks/framework.gBlNPWt_.js";const g=JSON.parse('{"title":"Build Instructions","description":"","frontmatter":{},"headers":[],"relativePath":"BUILD_ME.md","filePath":"BUILD_ME.md","lastUpdated":1721825996000}'),n={name:"BUILD_ME.md"},l=i(`
HPCC SYSTEMS software Copyright (C) 2019 HPCC Systems.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+https://hpccsystems.com

Build Instructions

Prerequisites

Ubuntu

Ubuntu 19.10/19.04/18.04

sudo apt-get install cmake bison flex build-essential binutils-dev libldap2-dev libcppunit-dev libicu-dev libxslt1-dev \\
+zlib1g-dev libboost-regex-dev libarchive-dev python-dev libv8-dev default-jdk libapr1-dev libaprutil1-dev libiberty-dev \\
+libhiredis-dev libtbb-dev libxalan-c-dev libnuma-dev nodejs libevent-dev libatlas-base-dev libblas-dev python3-dev \\
+default-libmysqlclient-dev libsqlite3-dev r-base-dev r-cran-rcpp r-cran-rinside r-cran-inline libmemcached-dev \\
+libcurl4-openssl-dev pkg-config libtool autotools-dev automake libssl-dev
+

Ubuntu 16.04

sudo apt-get install cmake bison flex build-essential binutils-dev libldap2-dev libcppunit-dev libicu-dev libxslt1-dev \\
+zlib1g-dev libboost-regex-dev libssl-dev libarchive-dev python-dev libv8-dev default-jdk libapr1-dev libaprutil1-dev \\
+libiberty-dev libhiredis-dev libtbb-dev libxalan-c-dev libnuma-dev libevent-dev libatlas-base-dev libblas-dev \\
+libatlas-dev python3-dev libcurl4-openssl-dev libtool autotools-dev automake
+
Additional information for building versions prior to 7.0 on Ubuntu 18.04

Get openssl-1.0.2o.tar.gz from https://www.openssl.org/source/

unpack, build, and install:

./config -fPIC shared
+make
+make install
+

This installs to /usr/local/ssl by default.

Build the platform with the following additional CMake options:

-DOPENSSL_LIBRARIES=/usr/local/ssl/lib/libssl.so -DOPENSSL_SSL_LIBRARY=/usr/local/ssl/lib/libssl.so
+

CentOS

Regardless of which version of CentOS you will be building on, it is suggested that you enable the EPEL repository

bash
sudo yum install -y epel-release

CentOS 7:

sudo yum install gcc-c++ gcc make bison flex binutils-devel openldap-devel libicu-devel libxslt-devel libarchive-devel \\
+boost-devel openssl-devel apr-devel apr-util-devel hiredis-devel numactl-devel mariadb-devel libevent-devel tbb-devel \\
+atlas-devel python34 libmemcached-devel sqlite-devel v8-devel python-devel python34-devel java-1.8.0-openjdk-devel \\
+R-core-devel R-Rcpp-devel R-inline R-RInside-devel nodejs cmake3 rpm-build libcurl-devel
+

CentOS 6.4:

sudo yum install gcc-c++ gcc make bison flex binutils-devel openldap-devel libicu-devel libxslt-devel libarchive-devel \\
+boost-devel openssl-devel apr-devel apr-util-devel hiredis-devel numactl-devel libmysqlclient-dev libevent-devel \\
+tbb-devel atlas-devel python34 R-core-devel R-Rcpp-devel R-inline R-RInside-devel nodejs libcurl-devel
+

Other Platforms

Fedora 19:

sudo yum install gcc-c++ gcc make fedora-packager cmake bison flex binutils-devel openldap-devel libicu-devel  \\
+xerces-c-devel xalan-c-devel libarchive-devel boost-devel openssl-devel apr-devel apr-util-devel
+

Fedora 23:

sudo dnf install gcc-c++ gcc make fedora-packager cmake bison flex binutils-devel openldap-devel libicu-devel \\
+xerces-c-devel xalan-c-devel libarchive-devel boost-devel openssl-devel apr-devel apr-util-devel numactl-devel \\
+tbb-devel libxslt-devel nodejs
+

Mac (Snow Leopard):

sudo port install bison flex binutils openldap icu xalanc zlib boost openssl libarchive
+

You can disable some functionality in order to reduce the list of required components, if necessary. Optional components, such as the plugins for interfacing to external languages, will be disabled automatically if the required libraries and headers are not found at build time.

Optional dependencies:

To build with support for all the plugins for third party embedded code, additional dependencies may be required. If not found, the system will default to skipping those components.

Windows

From 7.4 the build system has been changed to make it easier to build on windows. It is dependent on two other projects chocolatey and vcpkg for installing the dependencies:

Install chocolatey:

https://chocolatey.org/install

Use that to install bison/flex:

choco install winflexbison3

Install vcpkg:

git clone https://github.com/Microsoft/vcpkg
+cd vcpkg
+bootstrap-vcpkg

Use vcpkg to install various packages:

vcpkg install zlib
+vcpkg install boost
+vcpkg install icu
+vcpkg install libxslt
+vcpkg install tbb
+vcpkg install cppunit
+vcpkg install libarchive
+vcpkg install apr
+vcpkg install apr-util

You may need to force vcpkg to build the correct version, e.g. for 64bit:

vcpkg install zlib zlib:x64-windows

Other required third-party packages

Nodejs

NodeJS (version 8.x.x LTS recommended) is used to package ECL Watch and related web pages.

To install nodeJs on Linux based systems, try:

   curl -sL https://deb.nodesource.com/setup_8.x | sudo bash -
+   sudo apt-get install -y nodejs

If these instructions do not work on your system, refer to the detailed instructions available here

Building with R Support:

First insure that the R language is installed on your system. For Ubuntu use sudo apt-get install r-base-dev. For centos distributions use sudo yum install -y R-core-devel.

To install the prerequisites for building R support, use the following for all distros:

wget https://cran.r-project.org/src/contrib/00Archive/Rcpp/Rcpp_0.12.1.tar.gz
+wget https://cran.r-project.org/src/contrib/00Archive/RInside/RInside_0.2.12.tar.gz
+wget http://cran.r-project.org/src/contrib/inline_0.3.14.tar.gz
+sudo R CMD INSTALL Rcpp_0.12.1.tar.gz RInside_0.2.12.tar.gz inline_0.3.14.tar.gz

Get Latest HPCC Systems Sources

Visit Git-step-by-step for full instructions.

To get started quickly, simply:

bash
git clone [-b <branch name>] --recurse-submodules https://github.com/hpcc-systems/HPCC-Platform.git

Where [ ] denotes an optional argument.

CMake

The minimum version of CMake required to build the HPCC Platform is 3.3.2 on Linux. You may need to download a recent version here at cmake.org.

Now you need to run CMake to populate your build directory with Makefiles and special configuration to build HPCC, packages, tests, etc.

A separate directory is required for the build files. In the examples below, the source directory is contained in ~/hpcc and the build directory is ~/hpcc/build. mkdir ~/hpcc/build

All cmake commands would normally need to be executed within the build directory: cd ~/hpcc/build

For release builds, do: cmake ../src

To enable a specific plugin in the build:

bash
    cmake –D<Plugin Name>=ON ../src 
+    make –j6 package

These are the current supported plugins:

If testing during development and you may want to include plugins (except R) in the package: cmake -DTEST_PLUGINS=ON ../src

To produce a debug build: cmake -DCMAKE_BUILD_TYPE:STRING=Debug ../src

To build the client tools only: cmake -DCLIENTTOOLS_ONLY=1 ../src

To enable signing of the ecl standard library, ensure you have a gpg private key loaded into your gpg keychain and do: # Add -DSIGN_MODULES_KEYID and -DSIGN_MODULES_PASSPHRASE if applicable cmake -DSIGN_MODULES=ON ../src

In some cases, users have found that when packaging for Ubuntu, the dpkg-shlibdeps portion of the packaging adds an exceptional amount of time to the build process. To turn this off (and to create a package without dynamic dependency generation) do: cmake -DUSE_SHLIBDEPS=OFF ../src

CMake will check for necessary dependencies like binutils, boost regex, cppunit, pthreads, etc. If everything is correct, it'll create the necessary Makefiles and you're ready to build HPCC.

NOTE:

We default to using libxslt in place of Xalan for xslt support. Should you prefer to use libxalan, you can specify -DUSE_LIBXALAN on the cmake command line.

Building

You may build by either using make:

# Using -j option here to specify 6 compilation threads (suitable for quad core cpu)
+make -j6
+

Or, alternatively you can call a build system agnostic variant (works with make, ninja, XCode, Visual Studio etc.):

cmake --build .
+

This will make all binaries, libraries and scripts necessary to create the package.

Creating a package

The recommended method to install HPCC Systems on your machine (even for testing) is to use distro packages. CMake has already detected your system, so it know whether to generate TGZ files, DEB or RPM packages.

Just type:

make package
+

Alternatively you can use the build system agnostic variant:

cmake --build . --target package
+

and it will create the appropriate package for you to install. The package file will be created inside the build directory.

Installing the package

Install the package:

sudo dpkg -i hpccsystems-platform-community_6.0.0-trunk0trusty_amd64.deb
+

(note that the name of the package you have just built will depend on the branch you checked out, the distro, and other options).

Hint: missing dependencies may be fixed with:

sudo apt-get -f install
+

(see here for Ubuntu based installation).

To build client tools for MacOS:

Note: These instructions may not be up to date

   git submodule update --init --recursive
   brew install icu4c
+   brew install boost
+   brew install libarchive
+   brew install bison
+   brew install openldap

** Also make sure that bison is ahead of the system bison on your path. bison --version (The result should be > 2.4.1 )

** OS X has LDAP installed, but when compiling against it (/System/Library/Frameworks/LDAP.framework/Headers/ldap.h) you will get a #include nested too deeply, which is why you should install openldap.

   export CC=/usr/bin/clang
+   export CXX=/usr/bin/clang++
+   cmake ../ -DICU_LIBRARIES=/usr/local/opt/icu4c/lib/libicuuc.dylib -DICU_INCLUDE_DIR=/usr/local/opt/icu4c/include \\
+   -DLIBARCHIVE_INCLUDE_DIR=/usr/local/opt/libarchive/include \\
+   -DLIBARCHIVE_LIBRARIES=/usr/local/opt/libarchive/lib/libarchive.dylib \\
+   -DBOOST_REGEX_LIBRARIES=/usr/local/opt/boost/lib -DBOOST_REGEX_INCLUDE_DIR=/usr/local/opt/boost/include \\
+   -DCLIENTTOOLS_ONLY=true \\
+   -DUSE_OPENLDAP=true -DOPENLDAP_INCLUDE_DIR=/usr/local/opt/openldap/include \\
+   -DOPENLDAP_LIBRARIES=/usr/local/opt/openldap/lib/libldap_r.dylib
`,110),t=[l];function o(p,r,d,c,h,u){return s(),a("div",null,t)}const v=e(n,[["render",o]]);export{g as __pageData,v as default}; diff --git a/assets/BUILD_ME.md.VZvnVhVL.lean.js b/assets/BUILD_ME.md.VZvnVhVL.lean.js new file mode 100644 index 00000000000..89a26a7c3fb --- /dev/null +++ b/assets/BUILD_ME.md.VZvnVhVL.lean.js @@ -0,0 +1 @@ +import{_ as e,c as a,o as s,V as i}from"./chunks/framework.gBlNPWt_.js";const g=JSON.parse('{"title":"Build Instructions","description":"","frontmatter":{},"headers":[],"relativePath":"BUILD_ME.md","filePath":"BUILD_ME.md","lastUpdated":1721825996000}'),n={name:"BUILD_ME.md"},l=i("",110),t=[l];function o(p,r,d,c,h,u){return s(),a("div",null,t)}const v=e(n,[["render",o]]);export{g as __pageData,v as default}; diff --git a/assets/ECLIDE-AppProperties.syqSIm3l.png b/assets/ECLIDE-AppProperties.syqSIm3l.png new file mode 100644 index 0000000000000000000000000000000000000000..77eb9635760ad723b9ec3e9a43231b7d5e7f8346 GIT binary patch literal 33835 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 G;Qs@AaM^?a literal 0 HcmV?d00001 diff --git a/assets/README.md.7Qva37_C.js b/assets/README.md.7Qva37_C.js new file mode 100644 index 00000000000..fa4d6cc8a06 --- /dev/null +++ b/assets/README.md.7Qva37_C.js @@ -0,0 +1,37 @@ +import{_ as e,c as s,o as a,V as i}from"./chunks/framework.gBlNPWt_.js";const g=JSON.parse('{"title":"Description / Rationale","description":"","frontmatter":{},"headers":[],"relativePath":"README.md","filePath":"README.md","lastUpdated":1721825996000}'),t={name:"README.md"},n=i(`

Description / Rationale

HPCC Systems offers an enterprise ready, open source supercomputing platform to solve big data problems. As compared to Hadoop, the platform offers analysis of big data using less code and less nodes for greater efficiencies and offers a single programming language, a single platform and a single architecture for efficient processing. HPCC Systems is a technology division of LexisNexis Risk Solutions.

Getting Started

Release + Support Policy

In general, a new version of the HPCC Platform is released every 3 months. These releases can be either Major (with breaking changes) or Minor (with new features). Maintenance and security releases (point releases) are typically made weekly, and may occasionally include technical previews.

Maintenance releases are supported for the current and previous release, while security releases are supported for the current and previous two releases:

mermaid
---
+displayMode: compact
+---
+gantt
+    title Release Schedule
+    axisFormat %Y-Q%q
+    tickInterval 3month
+    dateFormat YYYY-MM-DD
+    section v8.12.x
+        Active:          active, 2023-02-07, 5M
+        Critical:        3M
+        Security:        6M
+    section v9.0.x
+        Active:          active, 2023-04-03, 6M
+        Critical:        3M
+        Security:        6M
+    section v9.2.x
+        Active:          active, 2023-07-04, 9M
+        Critical:        3M
+        Security:        3M
+    section v9.4.x
+        Active:          active, 2023-10-04, 9M
+        Critical:        3M
+        Security:        3M
+    section v9.6.x
+        Active:          active, 2024-04-04, 6M
+        Critical:        3M
+        Security:        3M
+    section v9.8.x
+        Active:          active, 2024-07-02, 6M
+        Critical:        3M
+        Security:        3M
+    section v9.10.x
+        Active:          active, 2024-10-01, 6M
+        Critical:        3M
+        Security:        3M

Architecture

The HPCC Systems architecture incorporates the Thor and Roxie clusters as well as common middleware components, an external communications layer, client interfaces which provide both end-user services and system management tools, and auxiliary components to support monitoring and to facilitate loading and storing of filesystem data from external sources. An HPCC environment can include only Thor clusters, or both Thor and Roxie clusters. Each of these cluster types is described in more detail in the following sections below the architecture diagram.

Thor

Thor (the Data Refinery Cluster) is responsible for consuming vast amounts of data, transforming, linking and indexing that data. It functions as a distributed file system with parallel processing power spread across the nodes. A cluster can scale from a single node to thousands of nodes.

  • Single-threaded
  • Distributed parallel processing
  • Distributed file system
  • Powerful parallel processing programming language (ECL)
  • Optimized for Extraction, Transformation, Loading, Sorting, Indexing and Linking
  • Scales from 1-1000s of nodes

Roxie

Roxie (the Query Cluster) provides separate high-performance online query processing and data warehouse capabilities. Roxie (Rapid Online XML Inquiry Engine) is the data delivery engine used in HPCC to serve data quickly and can support many thousands of requests per node per second.

  • Multi-threaded
  • Distributed parallel processing
  • Distributed file system
  • Powerful parallel processing programming language (ECL)
  • Optimized for concurrent query processing
  • Scales from 1-1000s of nodes

ECL

ECL (Enterprise Control Language) is the powerful programming language that is ideally suited for the manipulation of Big Data.

  • Transparent and implicitly parallel programming language
  • Non-procedural and dataflow oriented
  • Modular, reusable, extensible syntax
  • Combines data representation and algorithm implementation
  • Easily extend using C++ libraries
  • ECL is compiled into optimized C++

ECL IDE

ECL IDE is a modern IDE used to code, debug and monitor ECL programs.

  • Access to shared source code repositories
  • Complete development, debugging and testing environment for developing ECL dataflow programs
  • Access to the ECLWatch tool is built-in, allowing developers to watch job graphs as they are executing
  • Access to current and historical job workunits

ESP

ESP (Enterprise Services Platform) provides an easy to use interface to access ECL queries using XML, HTTP, SOAP and REST.

  • Standards-based interface to access ECL functions

Developer documentation

The following links describe the structure of the system and detail some of the key components:

Regression test

sh
cd /opt/HPCCSystems/testing/regress
+./ecl-test query --target thor nlppp.ecl
`,30),l=[n];function r(o,p,h,c,d,E){return a(),s("div",null,l)}const k=e(t,[["render",r]]);export{g as __pageData,k as default}; diff --git a/assets/README.md.7Qva37_C.lean.js b/assets/README.md.7Qva37_C.lean.js new file mode 100644 index 00000000000..48e44d7c678 --- /dev/null +++ b/assets/README.md.7Qva37_C.lean.js @@ -0,0 +1 @@ +import{_ as e,c as s,o as a,V as i}from"./chunks/framework.gBlNPWt_.js";const g=JSON.parse('{"title":"Description / Rationale","description":"","frontmatter":{},"headers":[],"relativePath":"README.md","filePath":"README.md","lastUpdated":1721825996000}'),t={name:"README.md"},n=i("",30),l=[n];function r(o,p,h,c,d,E){return a(),s("div",null,l)}const k=e(t,[["render",r]]);export{g as __pageData,k as default}; diff --git a/assets/actions-secrets-and-variables.XwHB-cZ0.png b/assets/actions-secrets-and-variables.XwHB-cZ0.png new file mode 100644 index 0000000000000000000000000000000000000000..1c267910fc55d498c9864968361a746176f0fe23 GIT binary patch literal 173164 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 bIdzYrhCg}|>lg=v0}e@1S&{Nj`rrQx859tV literal 0 HcmV?d00001 diff --git a/assets/app.CTpl1ysr.js b/assets/app.CTpl1ysr.js new file mode 100644 index 00000000000..951f00b3ee4 --- /dev/null +++ b/assets/app.CTpl1ysr.js @@ -0,0 +1,7 @@ +import{j as o,a2 as p,a3 as u,a4 as c,a5 as l,a6 as f,a7 as d,a8 as m,a9 as h,aa as A,ab as g,ac as v,d as P,u as w,l as y,z as C,ad as R,ae as _,af as b,ag as E}from"./chunks/framework.gBlNPWt_.js";import{R as D}from"./chunks/theme.LCXbL-fc.js";function i(e){if(e.extends){const a=i(e.extends);return{...a,...e,async enhanceApp(t){a.enhanceApp&&await a.enhanceApp(t),e.enhanceApp&&await e.enhanceApp(t)}}}return e}const s=i(D),T=P({name:"VitePressApp",setup(){const{site:e,lang:a,dir:t}=w();return y(()=>{C(()=>{document.documentElement.lang=a.value,document.documentElement.dir=t.value})}),e.value.router.prefetchLinks&&R(),_(),b(),s.setup&&s.setup(),()=>E(s.Layout)}});async function j(){const e=O(),a=L();a.provide(u,e);const t=c(e.route);return a.provide(l,t),a.component("Content",f),a.component("ClientOnly",d),Object.defineProperties(a.config.globalProperties,{$frontmatter:{get(){return t.frontmatter.value}},$params:{get(){return t.page.value.params}}}),s.enhanceApp&&await s.enhanceApp({app:a,router:e,siteData:m}),{app:a,router:e,data:t}}function L(){return h(T)}function O(){let e=o,a;return A(t=>{let n=g(t),r=null;return n&&(e&&(a=n),(e||a===n)&&(n=n.replace(/\.js$/,".lean.js")),r=v(()=>import(n),__vite__mapDeps([]))),o&&(e=!1),r},s.NotFound)}o&&j().then(({app:e,router:a,data:t})=>{a.go().then(()=>{p(a.route,t.site),e.mount("#app")})});export{j as createApp}; +function __vite__mapDeps(indexes) { + if (!__vite__mapDeps.viteFileDeps) { + __vite__mapDeps.viteFileDeps = [] + } + return indexes.map((i) => __vite__mapDeps.viteFileDeps[i]) +} diff --git a/assets/chunks/framework.gBlNPWt_.js b/assets/chunks/framework.gBlNPWt_.js new file mode 100644 index 00000000000..e5dd86f702a --- /dev/null +++ b/assets/chunks/framework.gBlNPWt_.js @@ -0,0 +1,17 @@ +/** +* @vue/shared v3.4.15 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/function gs(e,t){const n=new Set(e.split(","));return t?s=>n.has(s.toLowerCase()):s=>n.has(s)}const te={},mt=[],xe=()=>{},li=()=>!1,Bt=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),ms=e=>e.startsWith("onUpdate:"),oe=Object.assign,ys=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},ci=Object.prototype.hasOwnProperty,X=(e,t)=>ci.call(e,t),B=Array.isArray,yt=e=>bn(e)==="[object Map]",Nr=e=>bn(e)==="[object Set]",U=e=>typeof e=="function",ne=e=>typeof e=="string",xt=e=>typeof e=="symbol",Z=e=>e!==null&&typeof e=="object",Fr=e=>(Z(e)||U(e))&&U(e.then)&&U(e.catch),$r=Object.prototype.toString,bn=e=>$r.call(e),ai=e=>bn(e).slice(8,-1),Hr=e=>bn(e)==="[object Object]",_s=e=>ne(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,Ot=gs(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),vn=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},ui=/-(\w)/g,Me=vn(e=>e.replace(ui,(t,n)=>n?n.toUpperCase():"")),fi=/\B([A-Z])/g,at=vn(e=>e.replace(fi,"-$1").toLowerCase()),wn=vn(e=>e.charAt(0).toUpperCase()+e.slice(1)),rn=vn(e=>e?`on${wn(e)}`:""),Qe=(e,t)=>!Object.is(e,t),Vn=(e,t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:n})},di=e=>{const t=parseFloat(e);return isNaN(t)?e:t},hi=e=>{const t=ne(e)?Number(e):NaN;return isNaN(t)?e:t};let ks;const jr=()=>ks||(ks=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function bs(e){if(B(e)){const t={};for(let n=0;n{if(n){const s=n.split(gi);s.length>1&&(t[s[0].trim()]=s[1].trim())}}),t}function vs(e){let t="";if(ne(e))t=e;else if(B(e))for(let n=0;nne(e)?e:e==null?"":B(e)||Z(e)&&(e.toString===$r||!U(e.toString))?JSON.stringify(e,Dr,2):String(e),Dr=(e,t)=>t&&t.__v_isRef?Dr(e,t.value):yt(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((n,[s,r],o)=>(n[Dn(s,o)+" =>"]=r,n),{})}:Nr(t)?{[`Set(${t.size})`]:[...t.values()].map(n=>Dn(n))}:xt(t)?Dn(t):Z(t)&&!B(t)&&!Hr(t)?String(t):t,Dn=(e,t="")=>{var n;return xt(e)?`Symbol(${(n=e.description)!=null?n:t})`:e};/** +* @vue/reactivity v3.4.15 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/let be;class vi{constructor(t=!1){this.detached=t,this._active=!0,this.effects=[],this.cleanups=[],this.parent=be,!t&&be&&(this.index=(be.scopes||(be.scopes=[])).push(this)-1)}get active(){return this._active}run(t){if(this._active){const n=be;try{return be=this,t()}finally{be=n}}}on(){be=this}off(){be=this.parent}stop(t){if(this._active){let n,s;for(n=0,s=this.effects.length;n=2))break}this._dirtyLevel<2&&(this._dirtyLevel=0),ft()}return this._dirtyLevel>=2}set dirty(t){this._dirtyLevel=t?2:0}run(){if(this._dirtyLevel=0,!this.active)return this.fn();let t=Ge,n=it;try{return Ge=!0,it=this,this._runnings++,Us(this),this.fn()}finally{Ks(this),this._runnings--,it=n,Ge=t}}stop(){var t;this.active&&(Us(this),Ks(this),(t=this.onStop)==null||t.call(this),this.active=!1)}}function Ci(e){return e.value}function Us(e){e._trackId++,e._depsLength=0}function Ks(e){if(e.deps&&e.deps.length>e._depsLength){for(let t=e._depsLength;t{const n=new Map;return n.cleanup=e,n.computed=t,n},an=new WeakMap,lt=Symbol(""),ss=Symbol("");function ye(e,t,n){if(Ge&&it){let s=an.get(e);s||an.set(e,s=new Map);let r=s.get(n);r||s.set(n,r=Gr(()=>s.delete(n))),Kr(it,r)}}function $e(e,t,n,s,r,o){const i=an.get(e);if(!i)return;let l=[];if(t==="clear")l=[...i.values()];else if(n==="length"&&B(e)){const c=Number(s);i.forEach((u,d)=>{(d==="length"||!xt(d)&&d>=c)&&l.push(u)})}else switch(n!==void 0&&l.push(i.get(n)),t){case"add":B(e)?_s(n)&&l.push(i.get("length")):(l.push(i.get(lt)),yt(e)&&l.push(i.get(ss)));break;case"delete":B(e)||(l.push(i.get(lt)),yt(e)&&l.push(i.get(ss)));break;case"set":yt(e)&&l.push(i.get(lt));break}Es();for(const c of l)c&&Wr(c,2);Cs()}function xi(e,t){var n;return(n=an.get(e))==null?void 0:n.get(t)}const Si=gs("__proto__,__v_isRef,__isVue"),zr=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(xt)),Ws=Ti();function Ti(){const e={};return["includes","indexOf","lastIndexOf"].forEach(t=>{e[t]=function(...n){const s=Y(this);for(let o=0,i=this.length;o{e[t]=function(...n){ut(),Es();const s=Y(this)[t].apply(this,n);return Cs(),ft(),s}}),e}function Ai(e){const t=Y(this);return ye(t,"has",e),t.hasOwnProperty(e)}class Xr{constructor(t=!1,n=!1){this._isReadonly=t,this._shallow=n}get(t,n,s){const r=this._isReadonly,o=this._shallow;if(n==="__v_isReactive")return!r;if(n==="__v_isReadonly")return r;if(n==="__v_isShallow")return o;if(n==="__v_raw")return s===(r?o?Di:Zr:o?Qr:Jr).get(t)||Object.getPrototypeOf(t)===Object.getPrototypeOf(s)?t:void 0;const i=B(t);if(!r){if(i&&X(Ws,n))return Reflect.get(Ws,n,s);if(n==="hasOwnProperty")return Ai}const l=Reflect.get(t,n,s);return(xt(n)?zr.has(n):Si(n))||(r||ye(t,"get",n),o)?l:de(l)?i&&_s(n)?l:l.value:Z(l)?r?xn(l):Cn(l):l}}class Yr extends Xr{constructor(t=!1){super(!1,t)}set(t,n,s,r){let o=t[n];if(!this._shallow){const c=Et(o);if(!un(s)&&!Et(s)&&(o=Y(o),s=Y(s)),!B(t)&&de(o)&&!de(s))return c?!1:(o.value=s,!0)}const i=B(t)&&_s(n)?Number(n)e,En=e=>Reflect.getPrototypeOf(e);function Wt(e,t,n=!1,s=!1){e=e.__v_raw;const r=Y(e),o=Y(t);n||(Qe(t,o)&&ye(r,"get",t),ye(r,"get",o));const{has:i}=En(r),l=s?xs:n?As:$t;if(i.call(r,t))return l(e.get(t));if(i.call(r,o))return l(e.get(o));e!==r&&e.get(t)}function qt(e,t=!1){const n=this.__v_raw,s=Y(n),r=Y(e);return t||(Qe(e,r)&&ye(s,"has",e),ye(s,"has",r)),e===r?n.has(e):n.has(e)||n.has(r)}function Gt(e,t=!1){return e=e.__v_raw,!t&&ye(Y(e),"iterate",lt),Reflect.get(e,"size",e)}function qs(e){e=Y(e);const t=Y(this);return En(t).has.call(t,e)||(t.add(e),$e(t,"add",e,e)),this}function Gs(e,t){t=Y(t);const n=Y(this),{has:s,get:r}=En(n);let o=s.call(n,e);o||(e=Y(e),o=s.call(n,e));const i=r.call(n,e);return n.set(e,t),o?Qe(t,i)&&$e(n,"set",e,t):$e(n,"add",e,t),this}function zs(e){const t=Y(this),{has:n,get:s}=En(t);let r=n.call(t,e);r||(e=Y(e),r=n.call(t,e)),s&&s.call(t,e);const o=t.delete(e);return r&&$e(t,"delete",e,void 0),o}function Xs(){const e=Y(this),t=e.size!==0,n=e.clear();return t&&$e(e,"clear",void 0,void 0),n}function zt(e,t){return function(s,r){const o=this,i=o.__v_raw,l=Y(i),c=t?xs:e?As:$t;return!e&&ye(l,"iterate",lt),i.forEach((u,d)=>s.call(r,c(u),c(d),o))}}function Xt(e,t,n){return function(...s){const r=this.__v_raw,o=Y(r),i=yt(o),l=e==="entries"||e===Symbol.iterator&&i,c=e==="keys"&&i,u=r[e](...s),d=n?xs:t?As:$t;return!t&&ye(o,"iterate",c?ss:lt),{next(){const{value:h,done:m}=u.next();return m?{value:h,done:m}:{value:l?[d(h[0]),d(h[1])]:d(h),done:m}},[Symbol.iterator](){return this}}}}function Ve(e){return function(...t){return e==="delete"?!1:e==="clear"?void 0:this}}function Ii(){const e={get(o){return Wt(this,o)},get size(){return Gt(this)},has:qt,add:qs,set:Gs,delete:zs,clear:Xs,forEach:zt(!1,!1)},t={get(o){return Wt(this,o,!1,!0)},get size(){return Gt(this)},has:qt,add:qs,set:Gs,delete:zs,clear:Xs,forEach:zt(!1,!0)},n={get(o){return Wt(this,o,!0)},get size(){return Gt(this,!0)},has(o){return qt.call(this,o,!0)},add:Ve("add"),set:Ve("set"),delete:Ve("delete"),clear:Ve("clear"),forEach:zt(!0,!1)},s={get(o){return Wt(this,o,!0,!0)},get size(){return Gt(this,!0)},has(o){return qt.call(this,o,!0)},add:Ve("add"),set:Ve("set"),delete:Ve("delete"),clear:Ve("clear"),forEach:zt(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach(o=>{e[o]=Xt(o,!1,!1),n[o]=Xt(o,!0,!1),t[o]=Xt(o,!1,!0),s[o]=Xt(o,!0,!0)}),[e,n,t,s]}const[Mi,Ni,Fi,$i]=Ii();function Ss(e,t){const n=t?e?$i:Fi:e?Ni:Mi;return(s,r,o)=>r==="__v_isReactive"?!e:r==="__v_isReadonly"?e:r==="__v_raw"?s:Reflect.get(X(n,r)&&r in s?n:s,r,o)}const Hi={get:Ss(!1,!1)},ji={get:Ss(!1,!0)},Vi={get:Ss(!0,!1)},Jr=new WeakMap,Qr=new WeakMap,Zr=new WeakMap,Di=new WeakMap;function Bi(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function ki(e){return e.__v_skip||!Object.isExtensible(e)?0:Bi(ai(e))}function Cn(e){return Et(e)?e:Ts(e,!1,Oi,Hi,Jr)}function Ui(e){return Ts(e,!1,Pi,ji,Qr)}function xn(e){return Ts(e,!0,Li,Vi,Zr)}function Ts(e,t,n,s,r){if(!Z(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const o=r.get(e);if(o)return o;const i=ki(e);if(i===0)return e;const l=new Proxy(e,i===2?s:n);return r.set(e,l),l}function _t(e){return Et(e)?_t(e.__v_raw):!!(e&&e.__v_isReactive)}function Et(e){return!!(e&&e.__v_isReadonly)}function un(e){return!!(e&&e.__v_isShallow)}function eo(e){return _t(e)||Et(e)}function Y(e){const t=e&&e.__v_raw;return t?Y(t):e}function Lt(e){return cn(e,"__v_skip",!0),e}const $t=e=>Z(e)?Cn(e):e,As=e=>Z(e)?xn(e):e;class to{constructor(t,n,s,r){this._setter=n,this.dep=void 0,this.__v_isRef=!0,this.__v_isReadonly=!1,this.effect=new ws(()=>t(this._value),()=>Pt(this,1),()=>this.dep&&qr(this.dep)),this.effect.computed=this,this.effect.active=this._cacheable=!r,this.__v_isReadonly=s}get value(){const t=Y(this);return(!t._cacheable||t.effect.dirty)&&Qe(t._value,t._value=t.effect.run())&&Pt(t,2),Rs(t),t.effect._dirtyLevel>=1&&Pt(t,1),t._value}set value(t){this._setter(t)}get _dirty(){return this.effect.dirty}set _dirty(t){this.effect.dirty=t}}function Ki(e,t,n=!1){let s,r;const o=U(e);return o?(s=e,r=xe):(s=e.get,r=e.set),new to(s,r,o||!r,n)}function Rs(e){Ge&&it&&(e=Y(e),Kr(it,e.dep||(e.dep=Gr(()=>e.dep=void 0,e instanceof to?e:void 0))))}function Pt(e,t=2,n){e=Y(e);const s=e.dep;s&&Wr(s,t)}function de(e){return!!(e&&e.__v_isRef===!0)}function me(e){return so(e,!1)}function no(e){return so(e,!0)}function so(e,t){return de(e)?e:new Wi(e,t)}class Wi{constructor(t,n){this.__v_isShallow=n,this.dep=void 0,this.__v_isRef=!0,this._rawValue=n?t:Y(t),this._value=n?t:$t(t)}get value(){return Rs(this),this._value}set value(t){const n=this.__v_isShallow||un(t)||Et(t);t=n?t:Y(t),Qe(t,this._rawValue)&&(this._rawValue=t,this._value=n?t:$t(t),Pt(this,2))}}function ro(e){return de(e)?e.value:e}const qi={get:(e,t,n)=>ro(Reflect.get(e,t,n)),set:(e,t,n,s)=>{const r=e[t];return de(r)&&!de(n)?(r.value=n,!0):Reflect.set(e,t,n,s)}};function oo(e){return _t(e)?e:new Proxy(e,qi)}class Gi{constructor(t){this.dep=void 0,this.__v_isRef=!0;const{get:n,set:s}=t(()=>Rs(this),()=>Pt(this));this._get=n,this._set=s}get value(){return this._get()}set value(t){this._set(t)}}function zi(e){return new Gi(e)}class Xi{constructor(t,n,s){this._object=t,this._key=n,this._defaultValue=s,this.__v_isRef=!0}get value(){const t=this._object[this._key];return t===void 0?this._defaultValue:t}set value(t){this._object[this._key]=t}get dep(){return xi(Y(this._object),this._key)}}class Yi{constructor(t){this._getter=t,this.__v_isRef=!0,this.__v_isReadonly=!0}get value(){return this._getter()}}function Ji(e,t,n){return de(e)?e:U(e)?new Yi(e):Z(e)&&arguments.length>1?Qi(e,t,n):me(e)}function Qi(e,t,n){const s=e[t];return de(s)?s:new Xi(e,t,n)}/** +* @vue/runtime-core v3.4.15 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/function ze(e,t,n,s){let r;try{r=s?e(...s):e()}catch(o){Sn(o,t,n)}return r}function Se(e,t,n,s){if(U(e)){const o=ze(e,t,n,s);return o&&Fr(o)&&o.catch(i=>{Sn(i,t,n)}),o}const r=[];for(let o=0;o>>1,r=ue[s],o=jt(r);oIe&&ue.splice(t,1)}function nl(e){B(e)?bt.push(...e):(!Ue||!Ue.includes(e,e.allowRecurse?rt+1:rt))&&bt.push(e),lo()}function Ys(e,t,n=Ht?Ie+1:0){for(;njt(n)-jt(s));if(bt.length=0,Ue){Ue.push(...t);return}for(Ue=t,rt=0;rte.id==null?1/0:e.id,sl=(e,t)=>{const n=jt(e)-jt(t);if(n===0){if(e.pre&&!t.pre)return-1;if(t.pre&&!e.pre)return 1}return n};function co(e){rs=!1,Ht=!0,ue.sort(sl);try{for(Ie=0;Iene(w)?w.trim():w)),h&&(r=n.map(di))}let l,c=s[l=rn(t)]||s[l=rn(Me(t))];!c&&o&&(c=s[l=rn(at(t))]),c&&Se(c,e,6,r);const u=s[l+"Once"];if(u){if(!e.emitted)e.emitted={};else if(e.emitted[l])return;e.emitted[l]=!0,Se(u,e,6,r)}}function ao(e,t,n=!1){const s=t.emitsCache,r=s.get(e);if(r!==void 0)return r;const o=e.emits;let i={},l=!1;if(!U(e)){const c=u=>{const d=ao(u,t,!0);d&&(l=!0,oe(i,d))};!n&&t.mixins.length&&t.mixins.forEach(c),e.extends&&c(e.extends),e.mixins&&e.mixins.forEach(c)}return!o&&!l?(Z(e)&&s.set(e,null),null):(B(o)?o.forEach(c=>i[c]=null):oe(i,o),Z(e)&&s.set(e,i),i)}function An(e,t){return!e||!Bt(t)?!1:(t=t.slice(2).replace(/Once$/,""),X(e,t[0].toLowerCase()+t.slice(1))||X(e,at(t))||X(e,t))}let fe=null,Rn=null;function dn(e){const t=fe;return fe=e,Rn=e&&e.type.__scopeId||null,t}function $a(e){Rn=e}function Ha(){Rn=null}function ol(e,t=fe,n){if(!t||e._n)return e;const s=(...r)=>{s._d&&cr(-1);const o=dn(t);let i;try{i=e(...r)}finally{dn(o),s._d&&cr(1)}return i};return s._n=!0,s._c=!0,s._d=!0,s}function Bn(e){const{type:t,vnode:n,proxy:s,withProxy:r,props:o,propsOptions:[i],slots:l,attrs:c,emit:u,render:d,renderCache:h,data:m,setupState:w,ctx:L,inheritAttrs:M}=e;let F,W;const J=dn(e);try{if(n.shapeFlag&4){const y=r||s,N=y;F=Ae(d.call(N,y,h,o,w,m,L)),W=c}else{const y=t;F=Ae(y.length>1?y(o,{attrs:c,slots:l,emit:u}):y(o,null)),W=t.props?c:il(c)}}catch(y){Nt.length=0,Sn(y,e,1),F=ae(ve)}let p=F;if(W&&M!==!1){const y=Object.keys(W),{shapeFlag:N}=p;y.length&&N&7&&(i&&y.some(ms)&&(W=ll(W,i)),p=Ze(p,W))}return n.dirs&&(p=Ze(p),p.dirs=p.dirs?p.dirs.concat(n.dirs):n.dirs),n.transition&&(p.transition=n.transition),F=p,dn(J),F}const il=e=>{let t;for(const n in e)(n==="class"||n==="style"||Bt(n))&&((t||(t={}))[n]=e[n]);return t},ll=(e,t)=>{const n={};for(const s in e)(!ms(s)||!(s.slice(9)in t))&&(n[s]=e[s]);return n};function cl(e,t,n){const{props:s,children:r,component:o}=e,{props:i,children:l,patchFlag:c}=t,u=o.emitsOptions;if(t.dirs||t.transition)return!0;if(n&&c>=0){if(c&1024)return!0;if(c&16)return s?Js(s,i,u):!!i;if(c&8){const d=t.dynamicProps;for(let h=0;he.__isSuspense;function ho(e,t){t&&t.pendingBranch?B(e)?t.effects.push(...e):t.effects.push(e):nl(e)}const fl=Symbol.for("v-scx"),dl=()=>wt(fl);function po(e,t){return On(e,null,t)}function Da(e,t){return On(e,null,{flush:"post"})}const Yt={};function Xe(e,t,n){return On(e,t,n)}function On(e,t,{immediate:n,deep:s,flush:r,once:o,onTrack:i,onTrigger:l}=te){if(t&&o){const P=t;t=(...D)=>{P(...D),N()}}const c=ce,u=P=>s===!0?P:pt(P,s===!1?1:void 0);let d,h=!1,m=!1;if(de(e)?(d=()=>e.value,h=un(e)):_t(e)?(d=()=>u(e),h=!0):B(e)?(m=!0,h=e.some(P=>_t(P)||un(P)),d=()=>e.map(P=>{if(de(P))return P.value;if(_t(P))return u(P);if(U(P))return ze(P,c,2)})):U(e)?t?d=()=>ze(e,c,2):d=()=>(w&&w(),Se(e,c,3,[L])):d=xe,t&&s){const P=d;d=()=>pt(P())}let w,L=P=>{w=p.onStop=()=>{ze(P,c,4),w=p.onStop=void 0}},M;if(Fn)if(L=xe,t?n&&Se(t,c,3,[d(),m?[]:void 0,L]):d(),r==="sync"){const P=dl();M=P.__watcherHandles||(P.__watcherHandles=[])}else return xe;let F=m?new Array(e.length).fill(Yt):Yt;const W=()=>{if(!(!p.active||!p.dirty))if(t){const P=p.run();(s||h||(m?P.some((D,R)=>Qe(D,F[R])):Qe(P,F)))&&(w&&w(),Se(t,c,3,[P,F===Yt?void 0:m&&F[0]===Yt?[]:F,L]),F=P)}else p.run()};W.allowRecurse=!!t;let J;r==="sync"?J=W:r==="post"?J=()=>pe(W,c&&c.suspense):(W.pre=!0,c&&(W.id=c.uid),J=()=>Ls(W));const p=new ws(d,xe,J),y=Br(),N=()=>{p.stop(),y&&ys(y.effects,p)};return t?n?W():F=p.run():r==="post"?pe(p.run.bind(p),c&&c.suspense):p.run(),M&&M.push(N),N}function hl(e,t,n){const s=this.proxy,r=ne(e)?e.includes(".")?go(s,e):()=>s[e]:e.bind(s,s);let o;U(t)?o=t:(o=t.handler,n=t);const i=kt(this),l=On(r,o.bind(s),n);return i(),l}function go(e,t){const n=t.split(".");return()=>{let s=e;for(let r=0;r0){if(n>=t)return e;n++}if(s=s||new Set,s.has(e))return e;if(s.add(e),de(e))pt(e.value,t,n,s);else if(B(e))for(let r=0;r{pt(r,t,n,s)});else if(Hr(e))for(const r in e)pt(e[r],t,n,s);return e}function Pe(e,t,n,s){const r=e.dirs,o=t&&t.dirs;for(let i=0;i{e.isMounted=!0}),wo(()=>{e.isUnmounting=!0}),e}const we=[Function,Array],mo={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:we,onEnter:we,onAfterEnter:we,onEnterCancelled:we,onBeforeLeave:we,onLeave:we,onAfterLeave:we,onLeaveCancelled:we,onBeforeAppear:we,onAppear:we,onAfterAppear:we,onAppearCancelled:we},gl={name:"BaseTransition",props:mo,setup(e,{slots:t}){const n=Nn(),s=pl();let r;return()=>{const o=t.default&&_o(t.default(),!0);if(!o||!o.length)return;let i=o[0];if(o.length>1){for(const M of o)if(M.type!==ve){i=M;break}}const l=Y(e),{mode:c}=l;if(s.isLeaving)return kn(i);const u=Zs(i);if(!u)return kn(i);const d=os(u,l,s,n);is(u,d);const h=n.subTree,m=h&&Zs(h);let w=!1;const{getTransitionKey:L}=u.type;if(L){const M=L();r===void 0?r=M:M!==r&&(r=M,w=!0)}if(m&&m.type!==ve&&(!ot(u,m)||w)){const M=os(m,l,s,n);if(is(m,M),c==="out-in")return s.isLeaving=!0,M.afterLeave=()=>{s.isLeaving=!1,n.update.active!==!1&&(n.effect.dirty=!0,n.update())},kn(i);c==="in-out"&&u.type!==ve&&(M.delayLeave=(F,W,J)=>{const p=yo(s,m);p[String(m.key)]=m,F[Ke]=()=>{W(),F[Ke]=void 0,delete d.delayedLeave},d.delayedLeave=J})}return i}}},ml=gl;function yo(e,t){const{leavingVNodes:n}=e;let s=n.get(t.type);return s||(s=Object.create(null),n.set(t.type,s)),s}function os(e,t,n,s){const{appear:r,mode:o,persisted:i=!1,onBeforeEnter:l,onEnter:c,onAfterEnter:u,onEnterCancelled:d,onBeforeLeave:h,onLeave:m,onAfterLeave:w,onLeaveCancelled:L,onBeforeAppear:M,onAppear:F,onAfterAppear:W,onAppearCancelled:J}=t,p=String(e.key),y=yo(n,e),N=(R,T)=>{R&&Se(R,s,9,T)},P=(R,T)=>{const S=T[1];N(R,T),B(R)?R.every(K=>K.length<=1)&&S():R.length<=1&&S()},D={mode:o,persisted:i,beforeEnter(R){let T=l;if(!n.isMounted)if(r)T=M||l;else return;R[Ke]&&R[Ke](!0);const S=y[p];S&&ot(e,S)&&S.el[Ke]&&S.el[Ke](),N(T,[R])},enter(R){let T=c,S=u,K=d;if(!n.isMounted)if(r)T=F||c,S=W||u,K=J||d;else return;let O=!1;const q=R[Jt]=re=>{O||(O=!0,re?N(K,[R]):N(S,[R]),D.delayedLeave&&D.delayedLeave(),R[Jt]=void 0)};T?P(T,[R,q]):q()},leave(R,T){const S=String(e.key);if(R[Jt]&&R[Jt](!0),n.isUnmounting)return T();N(h,[R]);let K=!1;const O=R[Ke]=q=>{K||(K=!0,T(),q?N(L,[R]):N(w,[R]),R[Ke]=void 0,y[S]===e&&delete y[S])};y[S]=e,m?P(m,[R,O]):O()},clone(R){return os(R,t,n,s)}};return D}function kn(e){if(Ln(e))return e=Ze(e),e.children=null,e}function Zs(e){return Ln(e)?e.children?e.children[0]:void 0:e}function is(e,t){e.shapeFlag&6&&e.component?is(e.component.subTree,t):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function _o(e,t=!1,n){let s=[],r=0;for(let o=0;o1)for(let o=0;o!!e.type.__asyncLoader,Ln=e=>e.type.__isKeepAlive;function yl(e,t){vo(e,"a",t)}function _l(e,t){vo(e,"da",t)}function vo(e,t,n=ce){const s=e.__wdc||(e.__wdc=()=>{let r=n;for(;r;){if(r.isDeactivated)return;r=r.parent}return e()});if(Pn(t,s,n),n){let r=n.parent;for(;r&&r.parent;)Ln(r.parent.vnode)&&bl(s,t,n,r),r=r.parent}}function bl(e,t,n,s){const r=Pn(t,e,s,!0);In(()=>{ys(s[t],r)},n)}function Pn(e,t,n=ce,s=!1){if(n){const r=n[e]||(n[e]=[]),o=t.__weh||(t.__weh=(...i)=>{if(n.isUnmounted)return;ut();const l=kt(n),c=Se(t,n,e,i);return l(),ft(),c});return s?r.unshift(o):r.push(o),o}}const je=e=>(t,n=ce)=>(!Fn||e==="sp")&&Pn(e,(...s)=>t(...s),n),vl=je("bm"),St=je("m"),wl=je("bu"),El=je("u"),wo=je("bum"),In=je("um"),Cl=je("sp"),xl=je("rtg"),Sl=je("rtc");function Tl(e,t=ce){Pn("ec",e,t)}function Ba(e,t,n,s){let r;const o=n&&n[s];if(B(e)||ne(e)){r=new Array(e.length);for(let i=0,l=e.length;it(i,l,void 0,o&&o[l]));else{const i=Object.keys(e);r=new Array(i.length);for(let l=0,c=i.length;lmn(t)?!(t.type===ve||t.type===ge&&!Eo(t.children)):!0)?e:null}function Ua(e,t){const n={};for(const s in e)n[t&&/[A-Z]/.test(s)?`on:${s}`:rn(s)]=e[s];return n}const ls=e=>e?Vo(e)?Fs(e)||e.proxy:ls(e.parent):null,It=oe(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>ls(e.parent),$root:e=>ls(e.root),$emit:e=>e.emit,$options:e=>Is(e),$forceUpdate:e=>e.f||(e.f=()=>{e.effect.dirty=!0,Ls(e.update)}),$nextTick:e=>e.n||(e.n=Tn.bind(e.proxy)),$watch:e=>hl.bind(e)}),Un=(e,t)=>e!==te&&!e.__isScriptSetup&&X(e,t),Al={get({_:e},t){const{ctx:n,setupState:s,data:r,props:o,accessCache:i,type:l,appContext:c}=e;let u;if(t[0]!=="$"){const w=i[t];if(w!==void 0)switch(w){case 1:return s[t];case 2:return r[t];case 4:return n[t];case 3:return o[t]}else{if(Un(s,t))return i[t]=1,s[t];if(r!==te&&X(r,t))return i[t]=2,r[t];if((u=e.propsOptions[0])&&X(u,t))return i[t]=3,o[t];if(n!==te&&X(n,t))return i[t]=4,n[t];cs&&(i[t]=0)}}const d=It[t];let h,m;if(d)return t==="$attrs"&&ye(e,"get",t),d(e);if((h=l.__cssModules)&&(h=h[t]))return h;if(n!==te&&X(n,t))return i[t]=4,n[t];if(m=c.config.globalProperties,X(m,t))return m[t]},set({_:e},t,n){const{data:s,setupState:r,ctx:o}=e;return Un(r,t)?(r[t]=n,!0):s!==te&&X(s,t)?(s[t]=n,!0):X(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(o[t]=n,!0)},has({_:{data:e,setupState:t,accessCache:n,ctx:s,appContext:r,propsOptions:o}},i){let l;return!!n[i]||e!==te&&X(e,i)||Un(t,i)||(l=o[0])&&X(l,i)||X(s,i)||X(It,i)||X(r.config.globalProperties,i)},defineProperty(e,t,n){return n.get!=null?e._.accessCache[t]=0:X(n,"value")&&this.set(e,t,n.value,null),Reflect.defineProperty(e,t,n)}};function Ka(){return Rl().slots}function Rl(){const e=Nn();return e.setupContext||(e.setupContext=Bo(e))}function er(e){return B(e)?e.reduce((t,n)=>(t[n]=null,t),{}):e}let cs=!0;function Ol(e){const t=Is(e),n=e.proxy,s=e.ctx;cs=!1,t.beforeCreate&&tr(t.beforeCreate,e,"bc");const{data:r,computed:o,methods:i,watch:l,provide:c,inject:u,created:d,beforeMount:h,mounted:m,beforeUpdate:w,updated:L,activated:M,deactivated:F,beforeDestroy:W,beforeUnmount:J,destroyed:p,unmounted:y,render:N,renderTracked:P,renderTriggered:D,errorCaptured:R,serverPrefetch:T,expose:S,inheritAttrs:K,components:O,directives:q,filters:re}=t;if(u&&Ll(u,s,null),i)for(const z in i){const H=i[z];U(H)&&(s[z]=H.bind(n))}if(r){const z=r.call(n,n);Z(z)&&(e.data=Cn(z))}if(cs=!0,o)for(const z in o){const H=o[z],Ne=U(H)?H.bind(n,n):U(H.get)?H.get.bind(n,n):xe,Ut=!U(H)&&U(H.set)?H.set.bind(n):xe,et=se({get:Ne,set:Ut});Object.defineProperty(s,z,{enumerable:!0,configurable:!0,get:()=>et.value,set:Oe=>et.value=Oe})}if(l)for(const z in l)Co(l[z],s,n,z);if(c){const z=U(c)?c.call(n):c;Reflect.ownKeys(z).forEach(H=>{$l(H,z[H])})}d&&tr(d,e,"c");function j(z,H){B(H)?H.forEach(Ne=>z(Ne.bind(n))):H&&z(H.bind(n))}if(j(vl,h),j(St,m),j(wl,w),j(El,L),j(yl,M),j(_l,F),j(Tl,R),j(Sl,P),j(xl,D),j(wo,J),j(In,y),j(Cl,T),B(S))if(S.length){const z=e.exposed||(e.exposed={});S.forEach(H=>{Object.defineProperty(z,H,{get:()=>n[H],set:Ne=>n[H]=Ne})})}else e.exposed||(e.exposed={});N&&e.render===xe&&(e.render=N),K!=null&&(e.inheritAttrs=K),O&&(e.components=O),q&&(e.directives=q)}function Ll(e,t,n=xe){B(e)&&(e=as(e));for(const s in e){const r=e[s];let o;Z(r)?"default"in r?o=wt(r.from||s,r.default,!0):o=wt(r.from||s):o=wt(r),de(o)?Object.defineProperty(t,s,{enumerable:!0,configurable:!0,get:()=>o.value,set:i=>o.value=i}):t[s]=o}}function tr(e,t,n){Se(B(e)?e.map(s=>s.bind(t.proxy)):e.bind(t.proxy),t,n)}function Co(e,t,n,s){const r=s.includes(".")?go(n,s):()=>n[s];if(ne(e)){const o=t[e];U(o)&&Xe(r,o)}else if(U(e))Xe(r,e.bind(n));else if(Z(e))if(B(e))e.forEach(o=>Co(o,t,n,s));else{const o=U(e.handler)?e.handler.bind(n):t[e.handler];U(o)&&Xe(r,o,e)}}function Is(e){const t=e.type,{mixins:n,extends:s}=t,{mixins:r,optionsCache:o,config:{optionMergeStrategies:i}}=e.appContext,l=o.get(t);let c;return l?c=l:!r.length&&!n&&!s?c=t:(c={},r.length&&r.forEach(u=>hn(c,u,i,!0)),hn(c,t,i)),Z(t)&&o.set(t,c),c}function hn(e,t,n,s=!1){const{mixins:r,extends:o}=t;o&&hn(e,o,n,!0),r&&r.forEach(i=>hn(e,i,n,!0));for(const i in t)if(!(s&&i==="expose")){const l=Pl[i]||n&&n[i];e[i]=l?l(e[i],t[i]):t[i]}return e}const Pl={data:nr,props:sr,emits:sr,methods:Rt,computed:Rt,beforeCreate:he,created:he,beforeMount:he,mounted:he,beforeUpdate:he,updated:he,beforeDestroy:he,beforeUnmount:he,destroyed:he,unmounted:he,activated:he,deactivated:he,errorCaptured:he,serverPrefetch:he,components:Rt,directives:Rt,watch:Ml,provide:nr,inject:Il};function nr(e,t){return t?e?function(){return oe(U(e)?e.call(this,this):e,U(t)?t.call(this,this):t)}:t:e}function Il(e,t){return Rt(as(e),as(t))}function as(e){if(B(e)){const t={};for(let n=0;n1)return n&&U(t)?t.call(s&&s.proxy):t}}function Hl(e,t,n,s=!1){const r={},o={};cn(o,Mn,1),e.propsDefaults=Object.create(null),So(e,t,r,o);for(const i in e.propsOptions[0])i in r||(r[i]=void 0);n?e.props=s?r:Ui(r):e.type.props?e.props=r:e.props=o,e.attrs=o}function jl(e,t,n,s){const{props:r,attrs:o,vnode:{patchFlag:i}}=e,l=Y(r),[c]=e.propsOptions;let u=!1;if((s||i>0)&&!(i&16)){if(i&8){const d=e.vnode.dynamicProps;for(let h=0;h{c=!0;const[m,w]=To(h,t,!0);oe(i,m),w&&l.push(...w)};!n&&t.mixins.length&&t.mixins.forEach(d),e.extends&&d(e.extends),e.mixins&&e.mixins.forEach(d)}if(!o&&!c)return Z(e)&&s.set(e,mt),mt;if(B(o))for(let d=0;d-1,w[1]=M<0||L-1||X(w,"default"))&&l.push(h)}}}const u=[i,l];return Z(e)&&s.set(e,u),u}function rr(e){return e[0]!=="$"}function or(e){const t=e&&e.toString().match(/^\s*(function|class) (\w+)/);return t?t[2]:e===null?"null":""}function ir(e,t){return or(e)===or(t)}function lr(e,t){return B(t)?t.findIndex(n=>ir(n,e)):U(t)&&ir(t,e)?0:-1}const Ao=e=>e[0]==="_"||e==="$stable",Ms=e=>B(e)?e.map(Ae):[Ae(e)],Vl=(e,t,n)=>{if(t._n)return t;const s=ol((...r)=>Ms(t(...r)),n);return s._c=!1,s},Ro=(e,t,n)=>{const s=e._ctx;for(const r in e){if(Ao(r))continue;const o=e[r];if(U(o))t[r]=Vl(r,o,s);else if(o!=null){const i=Ms(o);t[r]=()=>i}}},Oo=(e,t)=>{const n=Ms(t);e.slots.default=()=>n},Dl=(e,t)=>{if(e.vnode.shapeFlag&32){const n=t._;n?(e.slots=Y(t),cn(t,"_",n)):Ro(t,e.slots={})}else e.slots={},t&&Oo(e,t);cn(e.slots,Mn,1)},Bl=(e,t,n)=>{const{vnode:s,slots:r}=e;let o=!0,i=te;if(s.shapeFlag&32){const l=t._;l?n&&l===1?o=!1:(oe(r,t),!n&&l===1&&delete r._):(o=!t.$stable,Ro(t,r)),i=t}else t&&(Oo(e,t),i={default:1});if(o)for(const l in r)!Ao(l)&&i[l]==null&&delete r[l]};function gn(e,t,n,s,r=!1){if(B(e)){e.forEach((m,w)=>gn(m,t&&(B(t)?t[w]:t),n,s,r));return}if(vt(s)&&!r)return;const o=s.shapeFlag&4?Fs(s.component)||s.component.proxy:s.el,i=r?null:o,{i:l,r:c}=e,u=t&&t.r,d=l.refs===te?l.refs={}:l.refs,h=l.setupState;if(u!=null&&u!==c&&(ne(u)?(d[u]=null,X(h,u)&&(h[u]=null)):de(u)&&(u.value=null)),U(c))ze(c,l,12,[i,d]);else{const m=ne(c),w=de(c),L=e.f;if(m||w){const M=()=>{if(L){const F=m?X(h,c)?h[c]:d[c]:c.value;r?B(F)&&ys(F,o):B(F)?F.includes(o)||F.push(o):m?(d[c]=[o],X(h,c)&&(h[c]=d[c])):(c.value=[o],e.k&&(d[e.k]=c.value))}else m?(d[c]=i,X(h,c)&&(h[c]=i)):w&&(c.value=i,e.k&&(d[e.k]=i))};r||L?M():(M.id=-1,pe(M,n))}}}let De=!1;const kl=e=>e.namespaceURI.includes("svg")&&e.tagName!=="foreignObject",Ul=e=>e.namespaceURI.includes("MathML"),Qt=e=>{if(kl(e))return"svg";if(Ul(e))return"mathml"},Zt=e=>e.nodeType===8;function Kl(e){const{mt:t,p:n,o:{patchProp:s,createText:r,nextSibling:o,parentNode:i,remove:l,insert:c,createComment:u}}=e,d=(p,y)=>{if(!y.hasChildNodes()){n(null,p,y),fn(),y._vnode=p;return}De=!1,h(y.firstChild,p,null,null,null),fn(),y._vnode=p,De&&console.error("Hydration completed but contains mismatches.")},h=(p,y,N,P,D,R=!1)=>{const T=Zt(p)&&p.data==="[",S=()=>M(p,y,N,P,D,T),{type:K,ref:O,shapeFlag:q,patchFlag:re}=y;let le=p.nodeType;y.el=p,re===-2&&(R=!1,y.dynamicChildren=null);let j=null;switch(K){case Ct:le!==3?y.children===""?(c(y.el=r(""),i(p),p),j=p):j=S():(p.data!==y.children&&(De=!0,p.data=y.children),j=o(p));break;case ve:J(p)?(j=o(p),W(y.el=p.content.firstChild,p,N)):le!==8||T?j=S():j=o(p);break;case Mt:if(T&&(p=o(p),le=p.nodeType),le===1||le===3){j=p;const z=!y.children.length;for(let H=0;H{R=R||!!y.dynamicChildren;const{type:T,props:S,patchFlag:K,shapeFlag:O,dirs:q,transition:re}=y,le=T==="input"||T==="option";if(le||K!==-1){q&&Pe(y,null,N,"created");let j=!1;if(J(p)){j=Lo(P,re)&&N&&N.vnode.props&&N.vnode.props.appear;const H=p.content.firstChild;j&&re.beforeEnter(H),W(H,p,N),y.el=p=H}if(O&16&&!(S&&(S.innerHTML||S.textContent))){let H=w(p.firstChild,y,p,N,P,D,R);for(;H;){De=!0;const Ne=H;H=H.nextSibling,l(Ne)}}else O&8&&p.textContent!==y.children&&(De=!0,p.textContent=y.children);if(S)if(le||!R||K&48)for(const H in S)(le&&(H.endsWith("value")||H==="indeterminate")||Bt(H)&&!Ot(H)||H[0]===".")&&s(p,H,null,S[H],void 0,void 0,N);else S.onClick&&s(p,"onClick",null,S.onClick,void 0,void 0,N);let z;(z=S&&S.onVnodeBeforeMount)&&Ee(z,N,y),q&&Pe(y,null,N,"beforeMount"),((z=S&&S.onVnodeMounted)||q||j)&&ho(()=>{z&&Ee(z,N,y),j&&re.enter(p),q&&Pe(y,null,N,"mounted")},P)}return p.nextSibling},w=(p,y,N,P,D,R,T)=>{T=T||!!y.dynamicChildren;const S=y.children,K=S.length;for(let O=0;O{const{slotScopeIds:T}=y;T&&(D=D?D.concat(T):T);const S=i(p),K=w(o(p),y,S,N,P,D,R);return K&&Zt(K)&&K.data==="]"?o(y.anchor=K):(De=!0,c(y.anchor=u("]"),S,K),K)},M=(p,y,N,P,D,R)=>{if(De=!0,y.el=null,R){const K=F(p);for(;;){const O=o(p);if(O&&O!==K)l(O);else break}}const T=o(p),S=i(p);return l(p),n(null,y,S,T,N,P,Qt(S),D),T},F=(p,y="[",N="]")=>{let P=0;for(;p;)if(p=o(p),p&&Zt(p)&&(p.data===y&&P++,p.data===N)){if(P===0)return o(p);P--}return p},W=(p,y,N)=>{const P=y.parentNode;P&&P.replaceChild(p,y);let D=N;for(;D;)D.vnode.el===y&&(D.vnode.el=D.subTree.el=p),D=D.parent},J=p=>p.nodeType===1&&p.tagName.toLowerCase()==="template";return[d,h]}const pe=ho;function Wl(e){return ql(e,Kl)}function ql(e,t){const n=jr();n.__VUE__=!0;const{insert:s,remove:r,patchProp:o,createElement:i,createText:l,createComment:c,setText:u,setElementText:d,parentNode:h,nextSibling:m,setScopeId:w=xe,insertStaticContent:L}=e,M=(a,f,g,_=null,b=null,C=null,A=void 0,E=null,x=!!f.dynamicChildren)=>{if(a===f)return;a&&!ot(a,f)&&(_=Kt(a),Oe(a,b,C,!0),a=null),f.patchFlag===-2&&(x=!1,f.dynamicChildren=null);const{type:v,ref:I,shapeFlag:V}=f;switch(v){case Ct:F(a,f,g,_);break;case ve:W(a,f,g,_);break;case Mt:a==null&&J(f,g,_,A);break;case ge:O(a,f,g,_,b,C,A,E,x);break;default:V&1?N(a,f,g,_,b,C,A,E,x):V&6?q(a,f,g,_,b,C,A,E,x):(V&64||V&128)&&v.process(a,f,g,_,b,C,A,E,x,dt)}I!=null&&b&&gn(I,a&&a.ref,C,f||a,!f)},F=(a,f,g,_)=>{if(a==null)s(f.el=l(f.children),g,_);else{const b=f.el=a.el;f.children!==a.children&&u(b,f.children)}},W=(a,f,g,_)=>{a==null?s(f.el=c(f.children||""),g,_):f.el=a.el},J=(a,f,g,_)=>{[a.el,a.anchor]=L(a.children,f,g,_,a.el,a.anchor)},p=({el:a,anchor:f},g,_)=>{let b;for(;a&&a!==f;)b=m(a),s(a,g,_),a=b;s(f,g,_)},y=({el:a,anchor:f})=>{let g;for(;a&&a!==f;)g=m(a),r(a),a=g;r(f)},N=(a,f,g,_,b,C,A,E,x)=>{f.type==="svg"?A="svg":f.type==="math"&&(A="mathml"),a==null?P(f,g,_,b,C,A,E,x):T(a,f,b,C,A,E,x)},P=(a,f,g,_,b,C,A,E)=>{let x,v;const{props:I,shapeFlag:V,transition:$,dirs:k}=a;if(x=a.el=i(a.type,C,I&&I.is,I),V&8?d(x,a.children):V&16&&R(a.children,x,null,_,b,Kn(a,C),A,E),k&&Pe(a,null,_,"created"),D(x,a,a.scopeId,A,_),I){for(const Q in I)Q!=="value"&&!Ot(Q)&&o(x,Q,null,I[Q],C,a.children,_,b,Fe);"value"in I&&o(x,"value",null,I.value,C),(v=I.onVnodeBeforeMount)&&Ee(v,_,a)}k&&Pe(a,null,_,"beforeMount");const G=Lo(b,$);G&&$.beforeEnter(x),s(x,f,g),((v=I&&I.onVnodeMounted)||G||k)&&pe(()=>{v&&Ee(v,_,a),G&&$.enter(x),k&&Pe(a,null,_,"mounted")},b)},D=(a,f,g,_,b)=>{if(g&&w(a,g),_)for(let C=0;C<_.length;C++)w(a,_[C]);if(b){let C=b.subTree;if(f===C){const A=b.vnode;D(a,A,A.scopeId,A.slotScopeIds,b.parent)}}},R=(a,f,g,_,b,C,A,E,x=0)=>{for(let v=x;v{const E=f.el=a.el;let{patchFlag:x,dynamicChildren:v,dirs:I}=f;x|=a.patchFlag&16;const V=a.props||te,$=f.props||te;let k;if(g&&tt(g,!1),(k=$.onVnodeBeforeUpdate)&&Ee(k,g,f,a),I&&Pe(f,a,g,"beforeUpdate"),g&&tt(g,!0),v?S(a.dynamicChildren,v,E,g,_,Kn(f,b),C):A||H(a,f,E,null,g,_,Kn(f,b),C,!1),x>0){if(x&16)K(E,f,V,$,g,_,b);else if(x&2&&V.class!==$.class&&o(E,"class",null,$.class,b),x&4&&o(E,"style",V.style,$.style,b),x&8){const G=f.dynamicProps;for(let Q=0;Q{k&&Ee(k,g,f,a),I&&Pe(f,a,g,"updated")},_)},S=(a,f,g,_,b,C,A)=>{for(let E=0;E{if(g!==_){if(g!==te)for(const E in g)!Ot(E)&&!(E in _)&&o(a,E,g[E],null,A,f.children,b,C,Fe);for(const E in _){if(Ot(E))continue;const x=_[E],v=g[E];x!==v&&E!=="value"&&o(a,E,v,x,A,f.children,b,C,Fe)}"value"in _&&o(a,"value",g.value,_.value,A)}},O=(a,f,g,_,b,C,A,E,x)=>{const v=f.el=a?a.el:l(""),I=f.anchor=a?a.anchor:l("");let{patchFlag:V,dynamicChildren:$,slotScopeIds:k}=f;k&&(E=E?E.concat(k):k),a==null?(s(v,g,_),s(I,g,_),R(f.children||[],g,I,b,C,A,E,x)):V>0&&V&64&&$&&a.dynamicChildren?(S(a.dynamicChildren,$,g,b,C,A,E),(f.key!=null||b&&f===b.subTree)&&Po(a,f,!0)):H(a,f,g,I,b,C,A,E,x)},q=(a,f,g,_,b,C,A,E,x)=>{f.slotScopeIds=E,a==null?f.shapeFlag&512?b.ctx.activate(f,g,_,A,x):re(f,g,_,b,C,A,x):le(a,f,x)},re=(a,f,g,_,b,C,A)=>{const E=a.component=tc(a,_,b);if(Ln(a)&&(E.ctx.renderer=dt),nc(E),E.asyncDep){if(b&&b.registerDep(E,j),!a.el){const x=E.subTree=ae(ve);W(null,x,f,g)}}else j(E,a,f,g,b,C,A)},le=(a,f,g)=>{const _=f.component=a.component;if(cl(a,f,g))if(_.asyncDep&&!_.asyncResolved){z(_,f,g);return}else _.next=f,tl(_.update),_.effect.dirty=!0,_.update();else f.el=a.el,_.vnode=f},j=(a,f,g,_,b,C,A)=>{const E=()=>{if(a.isMounted){let{next:I,bu:V,u:$,parent:k,vnode:G}=a;{const ht=Io(a);if(ht){I&&(I.el=G.el,z(a,I,A)),ht.asyncDep.then(()=>{a.isUnmounted||E()});return}}let Q=I,ee;tt(a,!1),I?(I.el=G.el,z(a,I,A)):I=G,V&&Vn(V),(ee=I.props&&I.props.onVnodeBeforeUpdate)&&Ee(ee,k,I,G),tt(a,!0);const ie=Bn(a),Te=a.subTree;a.subTree=ie,M(Te,ie,h(Te.el),Kt(Te),a,b,C),I.el=ie.el,Q===null&&al(a,ie.el),$&&pe($,b),(ee=I.props&&I.props.onVnodeUpdated)&&pe(()=>Ee(ee,k,I,G),b)}else{let I;const{el:V,props:$}=f,{bm:k,m:G,parent:Q}=a,ee=vt(f);if(tt(a,!1),k&&Vn(k),!ee&&(I=$&&$.onVnodeBeforeMount)&&Ee(I,Q,f),tt(a,!0),V&&jn){const ie=()=>{a.subTree=Bn(a),jn(V,a.subTree,a,b,null)};ee?f.type.__asyncLoader().then(()=>!a.isUnmounted&&ie()):ie()}else{const ie=a.subTree=Bn(a);M(null,ie,g,_,a,b,C),f.el=ie.el}if(G&&pe(G,b),!ee&&(I=$&&$.onVnodeMounted)){const ie=f;pe(()=>Ee(I,Q,ie),b)}(f.shapeFlag&256||Q&&vt(Q.vnode)&&Q.vnode.shapeFlag&256)&&a.a&&pe(a.a,b),a.isMounted=!0,f=g=_=null}},x=a.effect=new ws(E,xe,()=>Ls(v),a.scope),v=a.update=()=>{x.dirty&&x.run()};v.id=a.uid,tt(a,!0),v()},z=(a,f,g)=>{f.component=a;const _=a.vnode.props;a.vnode=f,a.next=null,jl(a,f.props,_,g),Bl(a,f.children,g),ut(),Ys(a),ft()},H=(a,f,g,_,b,C,A,E,x=!1)=>{const v=a&&a.children,I=a?a.shapeFlag:0,V=f.children,{patchFlag:$,shapeFlag:k}=f;if($>0){if($&128){Ut(v,V,g,_,b,C,A,E,x);return}else if($&256){Ne(v,V,g,_,b,C,A,E,x);return}}k&8?(I&16&&Fe(v,b,C),V!==v&&d(g,V)):I&16?k&16?Ut(v,V,g,_,b,C,A,E,x):Fe(v,b,C,!0):(I&8&&d(g,""),k&16&&R(V,g,_,b,C,A,E,x))},Ne=(a,f,g,_,b,C,A,E,x)=>{a=a||mt,f=f||mt;const v=a.length,I=f.length,V=Math.min(v,I);let $;for($=0;$I?Fe(a,b,C,!0,!1,V):R(f,g,_,b,C,A,E,x,V)},Ut=(a,f,g,_,b,C,A,E,x)=>{let v=0;const I=f.length;let V=a.length-1,$=I-1;for(;v<=V&&v<=$;){const k=a[v],G=f[v]=x?We(f[v]):Ae(f[v]);if(ot(k,G))M(k,G,g,null,b,C,A,E,x);else break;v++}for(;v<=V&&v<=$;){const k=a[V],G=f[$]=x?We(f[$]):Ae(f[$]);if(ot(k,G))M(k,G,g,null,b,C,A,E,x);else break;V--,$--}if(v>V){if(v<=$){const k=$+1,G=k$)for(;v<=V;)Oe(a[v],b,C,!0),v++;else{const k=v,G=v,Q=new Map;for(v=G;v<=$;v++){const _e=f[v]=x?We(f[v]):Ae(f[v]);_e.key!=null&&Q.set(_e.key,v)}let ee,ie=0;const Te=$-G+1;let ht=!1,Vs=0;const Tt=new Array(Te);for(v=0;v=Te){Oe(_e,b,C,!0);continue}let Le;if(_e.key!=null)Le=Q.get(_e.key);else for(ee=G;ee<=$;ee++)if(Tt[ee-G]===0&&ot(_e,f[ee])){Le=ee;break}Le===void 0?Oe(_e,b,C,!0):(Tt[Le-G]=v+1,Le>=Vs?Vs=Le:ht=!0,M(_e,f[Le],g,null,b,C,A,E,x),ie++)}const Ds=ht?Gl(Tt):mt;for(ee=Ds.length-1,v=Te-1;v>=0;v--){const _e=G+v,Le=f[_e],Bs=_e+1{const{el:C,type:A,transition:E,children:x,shapeFlag:v}=a;if(v&6){et(a.component.subTree,f,g,_);return}if(v&128){a.suspense.move(f,g,_);return}if(v&64){A.move(a,f,g,dt);return}if(A===ge){s(C,f,g);for(let V=0;VE.enter(C),b);else{const{leave:V,delayLeave:$,afterLeave:k}=E,G=()=>s(C,f,g),Q=()=>{V(C,()=>{G(),k&&k()})};$?$(C,G,Q):Q()}else s(C,f,g)},Oe=(a,f,g,_=!1,b=!1)=>{const{type:C,props:A,ref:E,children:x,dynamicChildren:v,shapeFlag:I,patchFlag:V,dirs:$}=a;if(E!=null&&gn(E,null,g,a,!0),I&256){f.ctx.deactivate(a);return}const k=I&1&&$,G=!vt(a);let Q;if(G&&(Q=A&&A.onVnodeBeforeUnmount)&&Ee(Q,f,a),I&6)ii(a.component,g,_);else{if(I&128){a.suspense.unmount(g,_);return}k&&Pe(a,null,f,"beforeUnmount"),I&64?a.type.remove(a,f,g,b,dt,_):v&&(C!==ge||V>0&&V&64)?Fe(v,f,g,!1,!0):(C===ge&&V&384||!b&&I&16)&&Fe(x,f,g),_&&Hs(a)}(G&&(Q=A&&A.onVnodeUnmounted)||k)&&pe(()=>{Q&&Ee(Q,f,a),k&&Pe(a,null,f,"unmounted")},g)},Hs=a=>{const{type:f,el:g,anchor:_,transition:b}=a;if(f===ge){oi(g,_);return}if(f===Mt){y(a);return}const C=()=>{r(g),b&&!b.persisted&&b.afterLeave&&b.afterLeave()};if(a.shapeFlag&1&&b&&!b.persisted){const{leave:A,delayLeave:E}=b,x=()=>A(g,C);E?E(a.el,C,x):x()}else C()},oi=(a,f)=>{let g;for(;a!==f;)g=m(a),r(a),a=g;r(f)},ii=(a,f,g)=>{const{bum:_,scope:b,update:C,subTree:A,um:E}=a;_&&Vn(_),b.stop(),C&&(C.active=!1,Oe(A,a,f,g)),E&&pe(E,f),pe(()=>{a.isUnmounted=!0},f),f&&f.pendingBranch&&!f.isUnmounted&&a.asyncDep&&!a.asyncResolved&&a.suspenseId===f.pendingId&&(f.deps--,f.deps===0&&f.resolve())},Fe=(a,f,g,_=!1,b=!1,C=0)=>{for(let A=C;Aa.shapeFlag&6?Kt(a.component.subTree):a.shapeFlag&128?a.suspense.next():m(a.anchor||a.el);let $n=!1;const js=(a,f,g)=>{a==null?f._vnode&&Oe(f._vnode,null,null,!0):M(f._vnode||null,a,f,null,null,null,g),$n||($n=!0,Ys(),fn(),$n=!1),f._vnode=a},dt={p:M,um:Oe,m:et,r:Hs,mt:re,mc:R,pc:H,pbc:S,n:Kt,o:e};let Hn,jn;return t&&([Hn,jn]=t(dt)),{render:js,hydrate:Hn,createApp:Fl(js,Hn)}}function Kn({type:e,props:t},n){return n==="svg"&&e==="foreignObject"||n==="mathml"&&e==="annotation-xml"&&t&&t.encoding&&t.encoding.includes("html")?void 0:n}function tt({effect:e,update:t},n){e.allowRecurse=t.allowRecurse=n}function Lo(e,t){return(!e||e&&!e.pendingBranch)&&t&&!t.persisted}function Po(e,t,n=!1){const s=e.children,r=t.children;if(B(s)&&B(r))for(let o=0;o>1,e[n[l]]0&&(t[s]=n[o-1]),n[o]=s)}}for(o=n.length,i=n[o-1];o-- >0;)n[o]=i,i=t[i];return n}function Io(e){const t=e.subTree.component;if(t)return t.asyncDep&&!t.asyncResolved?t:Io(t)}const zl=e=>e.__isTeleport,ge=Symbol.for("v-fgt"),Ct=Symbol.for("v-txt"),ve=Symbol.for("v-cmt"),Mt=Symbol.for("v-stc"),Nt=[];let Re=null;function Mo(e=!1){Nt.push(Re=e?null:[])}function Xl(){Nt.pop(),Re=Nt[Nt.length-1]||null}let Vt=1;function cr(e){Vt+=e}function No(e){return e.dynamicChildren=Vt>0?Re||mt:null,Xl(),Vt>0&&Re&&Re.push(e),e}function Wa(e,t,n,s,r,o){return No(Ho(e,t,n,s,r,o,!0))}function Fo(e,t,n,s,r){return No(ae(e,t,n,s,r,!0))}function mn(e){return e?e.__v_isVNode===!0:!1}function ot(e,t){return e.type===t.type&&e.key===t.key}const Mn="__vInternal",$o=({key:e})=>e??null,on=({ref:e,ref_key:t,ref_for:n})=>(typeof e=="number"&&(e=""+e),e!=null?ne(e)||de(e)||U(e)?{i:fe,r:e,k:t,f:!!n}:e:null);function Ho(e,t=null,n=null,s=0,r=null,o=e===ge?0:1,i=!1,l=!1){const c={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&$o(t),ref:t&&on(t),scopeId:Rn,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:o,patchFlag:s,dynamicProps:r,dynamicChildren:null,appContext:null,ctx:fe};return l?(Ns(c,n),o&128&&e.normalize(c)):n&&(c.shapeFlag|=ne(n)?8:16),Vt>0&&!i&&Re&&(c.patchFlag>0||o&6)&&c.patchFlag!==32&&Re.push(c),c}const ae=Yl;function Yl(e,t=null,n=null,s=0,r=null,o=!1){if((!e||e===uo)&&(e=ve),mn(e)){const l=Ze(e,t,!0);return n&&Ns(l,n),Vt>0&&!o&&Re&&(l.shapeFlag&6?Re[Re.indexOf(e)]=l:Re.push(l)),l.patchFlag|=-2,l}if(ic(e)&&(e=e.__vccOpts),t){t=Jl(t);let{class:l,style:c}=t;l&&!ne(l)&&(t.class=vs(l)),Z(c)&&(eo(c)&&!B(c)&&(c=oe({},c)),t.style=bs(c))}const i=ne(e)?1:ul(e)?128:zl(e)?64:Z(e)?4:U(e)?2:0;return Ho(e,t,n,s,r,i,o,!0)}function Jl(e){return e?eo(e)||Mn in e?oe({},e):e:null}function Ze(e,t,n=!1){const{props:s,ref:r,patchFlag:o,children:i}=e,l=t?Ql(s||{},t):s;return{__v_isVNode:!0,__v_skip:!0,type:e.type,props:l,key:l&&$o(l),ref:t&&t.ref?n&&r?B(r)?r.concat(on(t)):[r,on(t)]:on(t):r,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:i,target:e.target,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==ge?o===-1?16:o|16:o,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:e.transition,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&Ze(e.ssContent),ssFallback:e.ssFallback&&Ze(e.ssFallback),el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce}}function jo(e=" ",t=0){return ae(Ct,null,e,t)}function qa(e,t){const n=ae(Mt,null,e);return n.staticCount=t,n}function Ga(e="",t=!1){return t?(Mo(),Fo(ve,null,e)):ae(ve,null,e)}function Ae(e){return e==null||typeof e=="boolean"?ae(ve):B(e)?ae(ge,null,e.slice()):typeof e=="object"?We(e):ae(Ct,null,String(e))}function We(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:Ze(e)}function Ns(e,t){let n=0;const{shapeFlag:s}=e;if(t==null)t=null;else if(B(t))n=16;else if(typeof t=="object")if(s&65){const r=t.default;r&&(r._c&&(r._d=!1),Ns(e,r()),r._c&&(r._d=!0));return}else{n=32;const r=t._;!r&&!(Mn in t)?t._ctx=fe:r===3&&fe&&(fe.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else U(t)?(t={default:t,_ctx:fe},n=32):(t=String(t),s&64?(n=16,t=[jo(t)]):n=8);e.children=t,e.shapeFlag|=n}function Ql(...e){const t={};for(let n=0;nce||fe;let yn,fs;{const e=jr(),t=(n,s)=>{let r;return(r=e[n])||(r=e[n]=[]),r.push(s),o=>{r.length>1?r.forEach(i=>i(o)):r[0](o)}};yn=t("__VUE_INSTANCE_SETTERS__",n=>ce=n),fs=t("__VUE_SSR_SETTERS__",n=>Fn=n)}const kt=e=>{const t=ce;return yn(e),e.scope.on(),()=>{e.scope.off(),yn(t)}},ar=()=>{ce&&ce.scope.off(),yn(null)};function Vo(e){return e.vnode.shapeFlag&4}let Fn=!1;function nc(e,t=!1){t&&fs(t);const{props:n,children:s}=e.vnode,r=Vo(e);Hl(e,n,r,t),Dl(e,s);const o=r?sc(e,t):void 0;return t&&fs(!1),o}function sc(e,t){const n=e.type;e.accessCache=Object.create(null),e.proxy=Lt(new Proxy(e.ctx,Al));const{setup:s}=n;if(s){const r=e.setupContext=s.length>1?Bo(e):null,o=kt(e);ut();const i=ze(s,e,0,[e.props,r]);if(ft(),o(),Fr(i)){if(i.then(ar,ar),t)return i.then(l=>{ur(e,l,t)}).catch(l=>{Sn(l,e,0)});e.asyncDep=i}else ur(e,i,t)}else Do(e,t)}function ur(e,t,n){U(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:Z(t)&&(e.setupState=oo(t)),Do(e,n)}let fr;function Do(e,t,n){const s=e.type;if(!e.render){if(!t&&fr&&!s.render){const r=s.template||Is(e).template;if(r){const{isCustomElement:o,compilerOptions:i}=e.appContext.config,{delimiters:l,compilerOptions:c}=s,u=oe(oe({isCustomElement:o,delimiters:l},i),c);s.render=fr(r,u)}}e.render=s.render||xe}{const r=kt(e);ut();try{Ol(e)}finally{ft(),r()}}}function rc(e){return e.attrsProxy||(e.attrsProxy=new Proxy(e.attrs,{get(t,n){return ye(e,"get","$attrs"),t[n]}}))}function Bo(e){const t=n=>{e.exposed=n||{}};return{get attrs(){return rc(e)},slots:e.slots,emit:e.emit,expose:t}}function Fs(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(oo(Lt(e.exposed)),{get(t,n){if(n in t)return t[n];if(n in It)return It[n](e)},has(t,n){return n in t||n in It}}))}function oc(e,t=!0){return U(e)?e.displayName||e.name:e.name||t&&e.__name}function ic(e){return U(e)&&"__vccOpts"in e}const se=(e,t)=>Ki(e,t,Fn);function ds(e,t,n){const s=arguments.length;return s===2?Z(t)&&!B(t)?mn(t)?ae(e,null,[t]):ae(e,t):ae(e,null,t):(s>3?n=Array.prototype.slice.call(arguments,2):s===3&&mn(n)&&(n=[n]),ae(e,t,n))}const lc="3.4.15";/** +* @vue/runtime-dom v3.4.15 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/const cc="http://www.w3.org/2000/svg",ac="http://www.w3.org/1998/Math/MathML",qe=typeof document<"u"?document:null,dr=qe&&qe.createElement("template"),uc={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,s)=>{const r=t==="svg"?qe.createElementNS(cc,e):t==="mathml"?qe.createElementNS(ac,e):qe.createElement(e,n?{is:n}:void 0);return e==="select"&&s&&s.multiple!=null&&r.setAttribute("multiple",s.multiple),r},createText:e=>qe.createTextNode(e),createComment:e=>qe.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>qe.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,n,s,r,o){const i=n?n.previousSibling:t.lastChild;if(r&&(r===o||r.nextSibling))for(;t.insertBefore(r.cloneNode(!0),n),!(r===o||!(r=r.nextSibling)););else{dr.innerHTML=s==="svg"?`${e}`:s==="mathml"?`${e}`:e;const l=dr.content;if(s==="svg"||s==="mathml"){const c=l.firstChild;for(;c.firstChild;)l.appendChild(c.firstChild);l.removeChild(c)}t.insertBefore(l,n)}return[i?i.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}},Be="transition",At="animation",Dt=Symbol("_vtc"),ko=(e,{slots:t})=>ds(ml,fc(e),t);ko.displayName="Transition";const Uo={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String};ko.props=oe({},mo,Uo);const nt=(e,t=[])=>{B(e)?e.forEach(n=>n(...t)):e&&e(...t)},hr=e=>e?B(e)?e.some(t=>t.length>1):e.length>1:!1;function fc(e){const t={};for(const O in e)O in Uo||(t[O]=e[O]);if(e.css===!1)return t;const{name:n="v",type:s,duration:r,enterFromClass:o=`${n}-enter-from`,enterActiveClass:i=`${n}-enter-active`,enterToClass:l=`${n}-enter-to`,appearFromClass:c=o,appearActiveClass:u=i,appearToClass:d=l,leaveFromClass:h=`${n}-leave-from`,leaveActiveClass:m=`${n}-leave-active`,leaveToClass:w=`${n}-leave-to`}=e,L=dc(r),M=L&&L[0],F=L&&L[1],{onBeforeEnter:W,onEnter:J,onEnterCancelled:p,onLeave:y,onLeaveCancelled:N,onBeforeAppear:P=W,onAppear:D=J,onAppearCancelled:R=p}=t,T=(O,q,re)=>{st(O,q?d:l),st(O,q?u:i),re&&re()},S=(O,q)=>{O._isLeaving=!1,st(O,h),st(O,w),st(O,m),q&&q()},K=O=>(q,re)=>{const le=O?D:J,j=()=>T(q,O,re);nt(le,[q,j]),pr(()=>{st(q,O?c:o),ke(q,O?d:l),hr(le)||gr(q,s,M,j)})};return oe(t,{onBeforeEnter(O){nt(W,[O]),ke(O,o),ke(O,i)},onBeforeAppear(O){nt(P,[O]),ke(O,c),ke(O,u)},onEnter:K(!1),onAppear:K(!0),onLeave(O,q){O._isLeaving=!0;const re=()=>S(O,q);ke(O,h),gc(),ke(O,m),pr(()=>{O._isLeaving&&(st(O,h),ke(O,w),hr(y)||gr(O,s,F,re))}),nt(y,[O,re])},onEnterCancelled(O){T(O,!1),nt(p,[O])},onAppearCancelled(O){T(O,!0),nt(R,[O])},onLeaveCancelled(O){S(O),nt(N,[O])}})}function dc(e){if(e==null)return null;if(Z(e))return[Wn(e.enter),Wn(e.leave)];{const t=Wn(e);return[t,t]}}function Wn(e){return hi(e)}function ke(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.add(n)),(e[Dt]||(e[Dt]=new Set)).add(t)}function st(e,t){t.split(/\s+/).forEach(s=>s&&e.classList.remove(s));const n=e[Dt];n&&(n.delete(t),n.size||(e[Dt]=void 0))}function pr(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let hc=0;function gr(e,t,n,s){const r=e._endId=++hc,o=()=>{r===e._endId&&s()};if(n)return setTimeout(o,n);const{type:i,timeout:l,propCount:c}=pc(e,t);if(!i)return s();const u=i+"end";let d=0;const h=()=>{e.removeEventListener(u,m),o()},m=w=>{w.target===e&&++d>=c&&h()};setTimeout(()=>{d(n[L]||"").split(", "),r=s(`${Be}Delay`),o=s(`${Be}Duration`),i=mr(r,o),l=s(`${At}Delay`),c=s(`${At}Duration`),u=mr(l,c);let d=null,h=0,m=0;t===Be?i>0&&(d=Be,h=i,m=o.length):t===At?u>0&&(d=At,h=u,m=c.length):(h=Math.max(i,u),d=h>0?i>u?Be:At:null,m=d?d===Be?o.length:c.length:0);const w=d===Be&&/\b(transform|all)(,|$)/.test(s(`${Be}Property`).toString());return{type:d,timeout:h,propCount:m,hasTransform:w}}function mr(e,t){for(;e.lengthyr(n)+yr(e[s])))}function yr(e){return e==="auto"?0:Number(e.slice(0,-1).replace(",","."))*1e3}function gc(){return document.body.offsetHeight}function mc(e,t,n){const s=e[Dt];s&&(t=(t?[t,...s]:[...s]).join(" ")),t==null?e.removeAttribute("class"):n?e.setAttribute("class",t):e.className=t}const yc=Symbol("_vod"),_c=Symbol("");function bc(e,t,n){const s=e.style,r=s.display,o=ne(n);if(n&&!o){if(t&&!ne(t))for(const i in t)n[i]==null&&hs(s,i,"");for(const i in n)hs(s,i,n[i])}else if(o){if(t!==n){const i=s[_c];i&&(n+=";"+i),s.cssText=n}}else t&&e.removeAttribute("style");yc in e&&(s.display=r)}const _r=/\s*!important$/;function hs(e,t,n){if(B(n))n.forEach(s=>hs(e,t,s));else if(n==null&&(n=""),t.startsWith("--"))e.setProperty(t,n);else{const s=vc(e,t);_r.test(n)?e.setProperty(at(s),n.replace(_r,""),"important"):e[s]=n}}const br=["Webkit","Moz","ms"],qn={};function vc(e,t){const n=qn[t];if(n)return n;let s=Me(t);if(s!=="filter"&&s in e)return qn[t]=s;s=wn(s);for(let r=0;rGn||(Ac.then(()=>Gn=0),Gn=Date.now());function Oc(e,t){const n=s=>{if(!s._vts)s._vts=Date.now();else if(s._vts<=n.attached)return;Se(Lc(s,n.value),t,5,[s])};return n.value=e,n.attached=Rc(),n}function Lc(e,t){if(B(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map(s=>r=>!r._stopped&&s&&s(r))}else return t}const Cr=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,Pc=(e,t,n,s,r,o,i,l,c)=>{const u=r==="svg";t==="class"?mc(e,s,u):t==="style"?bc(e,n,s):Bt(t)?ms(t)||Sc(e,t,n,s,i):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):Ic(e,t,s,u))?Ec(e,t,s,o,i,l,c):(t==="true-value"?e._trueValue=s:t==="false-value"&&(e._falseValue=s),wc(e,t,s,u))};function Ic(e,t,n,s){if(s)return!!(t==="innerHTML"||t==="textContent"||t in e&&Cr(t)&&U(n));if(t==="spellcheck"||t==="draggable"||t==="translate"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA")return!1;if(t==="width"||t==="height"){const r=e.tagName;if(r==="IMG"||r==="VIDEO"||r==="CANVAS"||r==="SOURCE")return!1}return Cr(t)&&ne(n)?!1:t in e}const Mc=["ctrl","shift","alt","meta"],Nc={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>"button"in e&&e.button!==0,middle:e=>"button"in e&&e.button!==1,right:e=>"button"in e&&e.button!==2,exact:(e,t)=>Mc.some(n=>e[`${n}Key`]&&!t.includes(n))},za=(e,t)=>{const n=e._withMods||(e._withMods={}),s=t.join(".");return n[s]||(n[s]=(r,...o)=>{for(let i=0;i{const n=e._withKeys||(e._withKeys={}),s=t.join(".");return n[s]||(n[s]=r=>{if(!("key"in r))return;const o=at(r.key);if(t.some(i=>i===o||Fc[i]===o))return e(r)})},$c=oe({patchProp:Pc},uc);let zn,xr=!1;function Hc(){return zn=xr?zn:Wl($c),xr=!0,zn}const Ya=(...e)=>{const t=Hc().createApp(...e),{mount:n}=t;return t.mount=s=>{const r=Vc(s);if(r)return n(r,!0,jc(r))},t};function jc(e){if(e instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&e instanceof MathMLElement)return"mathml"}function Vc(e){return ne(e)?document.querySelector(e):e}const Ja=(e,t)=>{const n=e.__vccOpts||e;for(const[s,r]of t)n[s]=r;return n},Dc="modulepreload",Bc=function(e){return"/HPCC-Platform/"+e},Sr={},Qa=function(t,n,s){let r=Promise.resolve();if(n&&n.length>0){const o=document.getElementsByTagName("link");r=Promise.all(n.map(i=>{if(i=Bc(i),i in Sr)return;Sr[i]=!0;const l=i.endsWith(".css"),c=l?'[rel="stylesheet"]':"";if(!!s)for(let h=o.length-1;h>=0;h--){const m=o[h];if(m.href===i&&(!l||m.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${i}"]${c}`))return;const d=document.createElement("link");if(d.rel=l?"stylesheet":Dc,l||(d.as="script",d.crossOrigin=""),d.href=i,document.head.appendChild(d),l)return new Promise((h,m)=>{d.addEventListener("load",h),d.addEventListener("error",()=>m(new Error(`Unable to preload CSS for ${i}`)))})}))}return r.then(()=>t()).catch(o=>{const i=new Event("vite:preloadError",{cancelable:!0});if(i.payload=o,window.dispatchEvent(i),!i.defaultPrevented)throw o})},kc=window.__VP_SITE_DATA__;function $s(e){return Br()?(Ei(e),!0):!1}function Ye(e){return typeof e=="function"?e():ro(e)}const Ko=typeof window<"u"&&typeof document<"u";typeof WorkerGlobalScope<"u"&&globalThis instanceof WorkerGlobalScope;const Uc=Object.prototype.toString,Kc=e=>Uc.call(e)==="[object Object]",Ft=()=>{},ps=Wc();function Wc(){var e,t;return Ko&&((e=window==null?void 0:window.navigator)==null?void 0:e.userAgent)&&(/iP(ad|hone|od)/.test(window.navigator.userAgent)||((t=window==null?void 0:window.navigator)==null?void 0:t.maxTouchPoints)>2&&/iPad|Macintosh/.test(window==null?void 0:window.navigator.userAgent))}function qc(e,t){function n(...s){return new Promise((r,o)=>{Promise.resolve(e(()=>t.apply(this,s),{fn:t,thisArg:this,args:s})).then(r).catch(o)})}return n}const Wo=e=>e();function Gc(e=Wo){const t=me(!0);function n(){t.value=!1}function s(){t.value=!0}const r=(...o)=>{t.value&&e(...o)};return{isActive:xn(t),pause:n,resume:s,eventFilter:r}}function zc(e){return e||Nn()}function qo(...e){if(e.length!==1)return Ji(...e);const t=e[0];return typeof t=="function"?xn(zi(()=>({get:t,set:Ft}))):me(t)}function Xc(e,t,n={}){const{eventFilter:s=Wo,...r}=n;return Xe(e,qc(s,t),r)}function Yc(e,t,n={}){const{eventFilter:s,...r}=n,{eventFilter:o,pause:i,resume:l,isActive:c}=Gc(s);return{stop:Xc(e,t,{...r,eventFilter:o}),pause:i,resume:l,isActive:c}}function Go(e,t=!0,n){zc()?St(e,n):t?e():Tn(e)}function gt(e){var t;const n=Ye(e);return(t=n==null?void 0:n.$el)!=null?t:n}const He=Ko?window:void 0;function Je(...e){let t,n,s,r;if(typeof e[0]=="string"||Array.isArray(e[0])?([n,s,r]=e,t=He):[t,n,s,r]=e,!t)return Ft;Array.isArray(n)||(n=[n]),Array.isArray(s)||(s=[s]);const o=[],i=()=>{o.forEach(d=>d()),o.length=0},l=(d,h,m,w)=>(d.addEventListener(h,m,w),()=>d.removeEventListener(h,m,w)),c=Xe(()=>[gt(t),Ye(r)],([d,h])=>{if(i(),!d)return;const m=Kc(h)?{...h}:h;o.push(...n.flatMap(w=>s.map(L=>l(d,w,L,m))))},{immediate:!0,flush:"post"}),u=()=>{c(),i()};return $s(u),u}let Tr=!1;function Za(e,t,n={}){const{window:s=He,ignore:r=[],capture:o=!0,detectIframe:i=!1}=n;if(!s)return Ft;ps&&!Tr&&(Tr=!0,Array.from(s.document.body.children).forEach(m=>m.addEventListener("click",Ft)),s.document.documentElement.addEventListener("click",Ft));let l=!0;const c=m=>r.some(w=>{if(typeof w=="string")return Array.from(s.document.querySelectorAll(w)).some(L=>L===m.target||m.composedPath().includes(L));{const L=gt(w);return L&&(m.target===L||m.composedPath().includes(L))}}),d=[Je(s,"click",m=>{const w=gt(e);if(!(!w||w===m.target||m.composedPath().includes(w))){if(m.detail===0&&(l=!c(m)),!l){l=!0;return}t(m)}},{passive:!0,capture:o}),Je(s,"pointerdown",m=>{const w=gt(e);l=!c(m)&&!!(w&&!m.composedPath().includes(w))},{passive:!0}),i&&Je(s,"blur",m=>{setTimeout(()=>{var w;const L=gt(e);((w=s.document.activeElement)==null?void 0:w.tagName)==="IFRAME"&&!(L!=null&&L.contains(s.document.activeElement))&&t(m)},0)})].filter(Boolean);return()=>d.forEach(m=>m())}function Jc(e){return typeof e=="function"?e:typeof e=="string"?t=>t.key===e:Array.isArray(e)?t=>e.includes(t.key):()=>!0}function eu(...e){let t,n,s={};e.length===3?(t=e[0],n=e[1],s=e[2]):e.length===2?typeof e[1]=="object"?(t=!0,n=e[0],s=e[1]):(t=e[0],n=e[1]):(t=!0,n=e[0]);const{target:r=He,eventName:o="keydown",passive:i=!1,dedupe:l=!1}=s,c=Jc(t);return Je(r,o,d=>{d.repeat&&Ye(l)||c(d)&&n(d)},i)}function Qc(){const e=me(!1);return Nn()&&St(()=>{e.value=!0}),e}function Zc(e){const t=Qc();return se(()=>(t.value,!!e()))}function ea(e,t={}){const{window:n=He}=t,s=Zc(()=>n&&"matchMedia"in n&&typeof n.matchMedia=="function");let r;const o=me(!1),i=u=>{o.value=u.matches},l=()=>{r&&("removeEventListener"in r?r.removeEventListener("change",i):r.removeListener(i))},c=po(()=>{s.value&&(l(),r=n.matchMedia(Ye(e)),"addEventListener"in r?r.addEventListener("change",i):r.addListener(i),o.value=r.matches)});return $s(()=>{c(),l(),r=void 0}),o}const en=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{},tn="__vueuse_ssr_handlers__",ta=na();function na(){return tn in en||(en[tn]=en[tn]||{}),en[tn]}function zo(e,t){return ta[e]||t}function sa(e){return e==null?"any":e instanceof Set?"set":e instanceof Map?"map":e instanceof Date?"date":typeof e=="boolean"?"boolean":typeof e=="string"?"string":typeof e=="object"?"object":Number.isNaN(e)?"any":"number"}const ra={boolean:{read:e=>e==="true",write:e=>String(e)},object:{read:e=>JSON.parse(e),write:e=>JSON.stringify(e)},number:{read:e=>Number.parseFloat(e),write:e=>String(e)},any:{read:e=>e,write:e=>String(e)},string:{read:e=>e,write:e=>String(e)},map:{read:e=>new Map(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e.entries()))},set:{read:e=>new Set(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e))},date:{read:e=>new Date(e),write:e=>e.toISOString()}},Ar="vueuse-storage";function oa(e,t,n,s={}){var r;const{flush:o="pre",deep:i=!0,listenToStorageChanges:l=!0,writeDefaults:c=!0,mergeDefaults:u=!1,shallow:d,window:h=He,eventFilter:m,onError:w=T=>{console.error(T)},initOnMounted:L}=s,M=(d?no:me)(typeof t=="function"?t():t);if(!n)try{n=zo("getDefaultStorage",()=>{var T;return(T=He)==null?void 0:T.localStorage})()}catch(T){w(T)}if(!n)return M;const F=Ye(t),W=sa(F),J=(r=s.serializer)!=null?r:ra[W],{pause:p,resume:y}=Yc(M,()=>N(M.value),{flush:o,deep:i,eventFilter:m});return h&&l&&Go(()=>{Je(h,"storage",R),Je(h,Ar,D),L&&R()}),L||R(),M;function N(T){try{if(T==null)n.removeItem(e);else{const S=J.write(T),K=n.getItem(e);K!==S&&(n.setItem(e,S),h&&h.dispatchEvent(new CustomEvent(Ar,{detail:{key:e,oldValue:K,newValue:S,storageArea:n}})))}}catch(S){w(S)}}function P(T){const S=T?T.newValue:n.getItem(e);if(S==null)return c&&F!=null&&n.setItem(e,J.write(F)),F;if(!T&&u){const K=J.read(S);return typeof u=="function"?u(K,F):W==="object"&&!Array.isArray(K)?{...F,...K}:K}else return typeof S!="string"?S:J.read(S)}function D(T){R(T.detail)}function R(T){if(!(T&&T.storageArea!==n)){if(T&&T.key==null){M.value=F;return}if(!(T&&T.key!==e)){p();try{(T==null?void 0:T.newValue)!==J.write(M.value)&&(M.value=P(T))}catch(S){w(S)}finally{T?Tn(y):y()}}}}}function Xo(e){return ea("(prefers-color-scheme: dark)",e)}function ia(e={}){const{selector:t="html",attribute:n="class",initialValue:s="auto",window:r=He,storage:o,storageKey:i="vueuse-color-scheme",listenToStorageChanges:l=!0,storageRef:c,emitAuto:u,disableTransition:d=!0}=e,h={auto:"",light:"light",dark:"dark",...e.modes||{}},m=Xo({window:r}),w=se(()=>m.value?"dark":"light"),L=c||(i==null?qo(s):oa(i,s,o,{window:r,listenToStorageChanges:l})),M=se(()=>L.value==="auto"?w.value:L.value),F=zo("updateHTMLAttrs",(y,N,P)=>{const D=typeof y=="string"?r==null?void 0:r.document.querySelector(y):gt(y);if(!D)return;let R;if(d&&(R=r.document.createElement("style"),R.appendChild(document.createTextNode("*,*::before,*::after{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}")),r.document.head.appendChild(R)),N==="class"){const T=P.split(/\s/g);Object.values(h).flatMap(S=>(S||"").split(/\s/g)).filter(Boolean).forEach(S=>{T.includes(S)?D.classList.add(S):D.classList.remove(S)})}else D.setAttribute(N,P);d&&(r.getComputedStyle(R).opacity,document.head.removeChild(R))});function W(y){var N;F(t,n,(N=h[y])!=null?N:y)}function J(y){e.onChanged?e.onChanged(y,W):W(y)}Xe(M,J,{flush:"post",immediate:!0}),Go(()=>J(M.value));const p=se({get(){return u?L.value:M.value},set(y){L.value=y}});try{return Object.assign(p,{store:L,system:w,state:M})}catch{return p}}function la(e={}){const{valueDark:t="dark",valueLight:n="",window:s=He}=e,r=ia({...e,onChanged:(l,c)=>{var u;e.onChanged?(u=e.onChanged)==null||u.call(e,l==="dark",c,l):c(l)},modes:{dark:t,light:n}}),o=se(()=>r.system?r.system.value:Xo({window:s}).value?"dark":"light");return se({get(){return r.value==="dark"},set(l){const c=l?"dark":"light";o.value===c?r.value="auto":r.value=c}})}function Xn(e){return typeof Window<"u"&&e instanceof Window?e.document.documentElement:typeof Document<"u"&&e instanceof Document?e.documentElement:e}function Yo(e){const t=window.getComputedStyle(e);if(t.overflowX==="scroll"||t.overflowY==="scroll"||t.overflowX==="auto"&&e.clientWidth1?!0:(t.preventDefault&&t.preventDefault(),!1)}const nn=new WeakMap;function tu(e,t=!1){const n=me(t);let s=null,r;Xe(qo(e),l=>{const c=Xn(Ye(l));if(c){const u=c;nn.get(u)||nn.set(u,r),n.value&&(u.style.overflow="hidden")}},{immediate:!0});const o=()=>{const l=Xn(Ye(e));!l||n.value||(ps&&(s=Je(l,"touchmove",c=>{ca(c)},{passive:!1})),l.style.overflow="hidden",n.value=!0)},i=()=>{var l;const c=Xn(Ye(e));!c||!n.value||(ps&&(s==null||s()),c.style.overflow=(l=nn.get(c))!=null?l:"",nn.delete(c),n.value=!1)};return $s(i),se({get(){return n.value},set(l){l?o():i()}})}function nu(e={}){const{window:t=He,behavior:n="auto"}=e;if(!t)return{x:me(0),y:me(0)};const s=me(t.scrollX),r=me(t.scrollY),o=se({get(){return s.value},set(l){scrollTo({left:l,behavior:n})}}),i=se({get(){return r.value},set(l){scrollTo({top:l,behavior:n})}});return Je(t,"scroll",()=>{s.value=t.scrollX,r.value=t.scrollY},{capture:!1,passive:!0}),{x:o,y:i}}var Yn={BASE_URL:"/HPCC-Platform/",MODE:"production",DEV:!1,PROD:!0,SSR:!1},aa={};const Jo=/^(?:[a-z]+:|\/\/)/i,ua="vitepress-theme-appearance",Qo=/#.*$/,fa=/(index)?\.(md|html)$/,Ce=typeof document<"u",Zo={relativePath:"",filePath:"",title:"404",description:"Not Found",headers:[],frontmatter:{sidebar:!1,layout:"page"},lastUpdated:0,isNotFound:!0};function da(e,t,n=!1){if(t===void 0)return!1;if(e=Rr(`/${e}`),n)return new RegExp(t).test(e);if(Rr(t)!==e)return!1;const s=t.match(Qo);return s?(Ce?location.hash:"")===s[0]:!0}function Rr(e){return decodeURI(e).replace(Qo,"").replace(fa,"")}function ha(e){return Jo.test(e)}function pa(e,t){var s,r,o,i,l,c,u;const n=Object.keys(e.locales).find(d=>d!=="root"&&!ha(d)&&da(t,`/${d}/`,!0))||"root";return Object.assign({},e,{localeIndex:n,lang:((s=e.locales[n])==null?void 0:s.lang)??e.lang,dir:((r=e.locales[n])==null?void 0:r.dir)??e.dir,title:((o=e.locales[n])==null?void 0:o.title)??e.title,titleTemplate:((i=e.locales[n])==null?void 0:i.titleTemplate)??e.titleTemplate,description:((l=e.locales[n])==null?void 0:l.description)??e.description,head:ti(e.head,((c=e.locales[n])==null?void 0:c.head)??[]),themeConfig:{...e.themeConfig,...(u=e.locales[n])==null?void 0:u.themeConfig}})}function ei(e,t){const n=t.title||e.title,s=t.titleTemplate??e.titleTemplate;if(typeof s=="string"&&s.includes(":title"))return s.replace(/:title/g,n);const r=ga(e.title,s);return n===r.slice(3)?n:`${n}${r}`}function ga(e,t){return t===!1?"":t===!0||t===void 0?` | ${e}`:e===t?"":` | ${t}`}function ma(e,t){const[n,s]=t;if(n!=="meta")return!1;const r=Object.entries(s)[0];return r==null?!1:e.some(([o,i])=>o===n&&i[r[0]]===r[1])}function ti(e,t){return[...e.filter(n=>!ma(t,n)),...t]}const ya=/[\u0000-\u001F"#$&*+,:;<=>?[\]^`{|}\u007F]/g,_a=/^[a-z]:/i;function Or(e){const t=_a.exec(e),n=t?t[0]:"";return n+e.slice(n.length).replace(ya,"_").replace(/(^|\/)_+(?=[^/]*$)/,"$1")}const Jn=typeof process=="object"&&aa.VITE_EXTRA_EXTENSIONS||(Yn==null?void 0:Yn.VITE_EXTRA_EXTENSIONS)||"",ba=new Set(("3g2,3gp,aac,ai,apng,au,avif,bin,bmp,cer,class,conf,crl,css,csv,dll,doc,eps,epub,exe,gif,gz,ics,ief,jar,jpe,jpeg,jpg,js,json,jsonld,m4a,man,mid,midi,mjs,mov,mp2,mp3,mp4,mpe,mpeg,mpg,mpp,oga,ogg,ogv,ogx,opus,otf,p10,p7c,p7m,p7s,pdf,png,ps,qt,roff,rtf,rtx,ser,svg,t,tif,tiff,tr,ts,tsv,ttf,txt,vtt,wav,weba,webm,webp,woff,woff2,xhtml,xml,yaml,yml,zip"+(Jn&&typeof Jn=="string"?","+Jn:"")).split(","));function va(e){const t=e.split(".").pop();return t==null||!ba.has(t.toLowerCase())}const wa=Symbol(),ct=no(kc);function su(e){const t=se(()=>pa(ct.value,e.data.relativePath)),n=t.value.appearance,s=n==="force-dark"?me(!0):n?la({storageKey:ua,initialValue:()=>typeof n=="string"?n:"auto",...typeof n=="object"?n:{}}):me(!1);return{site:t,theme:se(()=>t.value.themeConfig),page:se(()=>e.data),frontmatter:se(()=>e.data.frontmatter),params:se(()=>e.data.params),lang:se(()=>t.value.lang),dir:se(()=>e.data.frontmatter.dir||t.value.dir),localeIndex:se(()=>t.value.localeIndex||"root"),title:se(()=>ei(t.value,e.data)),description:se(()=>e.data.description||t.value.description),isDark:s}}function Ea(){const e=wt(wa);if(!e)throw new Error("vitepress data not properly injected in app");return e}function Ca(e,t){return`${e}${t}`.replace(/\/+/g,"/")}function Lr(e){return Jo.test(e)||!e.startsWith("/")?e:Ca(ct.value.base,e)}function xa(e){let t=e.replace(/\.html$/,"");if(t=decodeURIComponent(t),t=t.replace(/\/$/,"/index"),Ce){const n="/HPCC-Platform/";t=Or(t.slice(n.length).replace(/\//g,"_")||"index")+".md";let s=__VP_HASH_MAP__[t.toLowerCase()];if(s||(t=t.endsWith("_index.md")?t.slice(0,-9)+".md":t.slice(0,-3)+"_index.md",s=__VP_HASH_MAP__[t.toLowerCase()]),!s)return null;t=`${n}assets/${t}.${s}.js`}else t=`./${Or(t.slice(1).replace(/\//g,"_"))}.md.js`;return t}let ln=[];function ru(e){ln.push(e),In(()=>{ln=ln.filter(t=>t!==e)})}function Sa(){let e=ct.value.scrollOffset,t=0,n=24;if(typeof e=="object"&&"padding"in e&&(n=e.padding,e=e.selector),typeof e=="number")t=e;else if(typeof e=="string")t=Pr(e,n);else if(Array.isArray(e))for(const s of e){const r=Pr(s,n);if(r){t=r;break}}return t}function Pr(e,t){const n=document.querySelector(e);if(!n)return 0;const s=n.getBoundingClientRect().bottom;return s<0?0:s+t}const Ta=Symbol(),ni="http://a.com",Aa=()=>({path:"/",component:null,data:Zo});function ou(e,t){const n=Cn(Aa()),s={route:n,go:r};async function r(l=Ce?location.href:"/"){var c,u;l=_n(l),await((c=s.onBeforeRouteChange)==null?void 0:c.call(s,l))!==!1&&(Mr(l),await i(l),await((u=s.onAfterRouteChanged)==null?void 0:u.call(s,l)))}let o=null;async function i(l,c=0,u=!1){var m;if(await((m=s.onBeforePageLoad)==null?void 0:m.call(s,l))===!1)return;const d=new URL(l,ni),h=o=d.pathname;try{let w=await e(h);if(!w)throw new Error(`Page not found: ${h}`);if(o===h){o=null;const{default:L,__pageData:M}=w;if(!L)throw new Error(`Invalid route component: ${L}`);n.path=Ce?h:Lr(h),n.component=Lt(L),n.data=Lt(M),Ce&&Tn(()=>{let F=ct.value.base+M.relativePath.replace(/(?:(^|\/)index)?\.md$/,"$1");if(!ct.value.cleanUrls&&!F.endsWith("/")&&(F+=".html"),F!==d.pathname&&(d.pathname=F,l=F+d.search+d.hash,history.replaceState(null,"",l)),d.hash&&!c){let W=null;try{W=document.getElementById(decodeURIComponent(d.hash).slice(1))}catch(J){console.warn(J)}if(W){Ir(W,d.hash);return}}window.scrollTo(0,c)})}}catch(w){if(!/fetch|Page not found/.test(w.message)&&!/^\/404(\.html|\/)?$/.test(l)&&console.error(w),!u)try{const L=await fetch(ct.value.base+"hashmap.json");window.__VP_HASH_MAP__=await L.json(),await i(l,c,!0);return}catch{}o===h&&(o=null,n.path=Ce?h:Lr(h),n.component=t?Lt(t):null,n.data=Zo)}}return Ce&&(window.addEventListener("click",l=>{if(l.target.closest("button"))return;const u=l.target.closest("a");if(u&&!u.closest(".vp-raw")&&(u instanceof SVGElement||!u.download)){const{target:d}=u,{href:h,origin:m,pathname:w,hash:L,search:M}=new URL(u.href instanceof SVGAnimatedString?u.href.animVal:u.href,u.baseURI),F=window.location;!l.ctrlKey&&!l.shiftKey&&!l.altKey&&!l.metaKey&&!d&&m===F.origin&&va(w)&&(l.preventDefault(),w===F.pathname&&M===F.search?(L!==F.hash&&(history.pushState(null,"",L),window.dispatchEvent(new Event("hashchange"))),L?Ir(u,L,u.classList.contains("header-anchor")):(Mr(h),window.scrollTo(0,0))):r(h))}},{capture:!0}),window.addEventListener("popstate",async l=>{var c;await i(_n(location.href),l.state&&l.state.scrollPosition||0),(c=s.onAfterRouteChanged)==null||c.call(s,location.href)}),window.addEventListener("hashchange",l=>{l.preventDefault()})),s}function Ra(){const e=wt(Ta);if(!e)throw new Error("useRouter() is called without provider.");return e}function si(){return Ra().route}function Ir(e,t,n=!1){let s=null;try{s=e.classList.contains("header-anchor")?e:document.getElementById(decodeURIComponent(t).slice(1))}catch(r){console.warn(r)}if(s){let r=function(){!n||Math.abs(i-window.scrollY)>window.innerHeight?window.scrollTo(0,i):window.scrollTo({left:0,top:i,behavior:"smooth"})};const o=parseInt(window.getComputedStyle(s).paddingTop,10),i=window.scrollY+s.getBoundingClientRect().top-Sa()+o;requestAnimationFrame(r)}}function Mr(e){Ce&&_n(e)!==_n(location.href)&&(history.replaceState({scrollPosition:window.scrollY},document.title),history.pushState(null,"",e))}function _n(e){const t=new URL(e,ni);return t.pathname=t.pathname.replace(/(^|\/)index(\.html)?$/,"$1"),ct.value.cleanUrls?t.pathname=t.pathname.replace(/\.html$/,""):!t.pathname.endsWith("/")&&!t.pathname.endsWith(".html")&&(t.pathname+=".html"),t.pathname+t.search+t.hash}const Qn=()=>ln.forEach(e=>e()),iu=bo({name:"VitePressContent",props:{as:{type:[Object,String],default:"div"}},setup(e){const t=si(),{site:n}=Ea();return()=>ds(e.as,n.value.contentProps??{style:{position:"relative"}},[t.component?ds(t.component,{onVnodeMounted:Qn,onVnodeUpdated:Qn,onVnodeUnmounted:Qn}):"404 Page Not Found"])}}),lu=bo({setup(e,{slots:t}){const n=me(!1);return St(()=>{n.value=!0}),()=>n.value&&t.default?t.default():null}});function cu(){Ce&&window.addEventListener("click",e=>{var n;const t=e.target;if(t.matches(".vp-code-group input")){const s=(n=t.parentElement)==null?void 0:n.parentElement;if(!s)return;const r=Array.from(s.querySelectorAll("input")).indexOf(t);if(r<0)return;const o=s.querySelector(".blocks");if(!o)return;const i=Array.from(o.children).find(u=>u.classList.contains("active"));if(!i)return;const l=o.children[r];if(!l||i===l)return;i.classList.remove("active"),l.classList.add("active");const c=s==null?void 0:s.querySelector(`label[for="${t.id}"]`);c==null||c.scrollIntoView({block:"nearest"})}})}function au(){if(Ce){const e=new WeakMap;window.addEventListener("click",t=>{var s;const n=t.target;if(n.matches('div[class*="language-"] > button.copy')){const r=n.parentElement,o=(s=n.nextElementSibling)==null?void 0:s.nextElementSibling;if(!r||!o)return;const i=/language-(shellscript|shell|bash|sh|zsh)/.test(r.className),l=[".vp-copy-ignore",".diff.remove"],c=o.cloneNode(!0);c.querySelectorAll(l.join(",")).forEach(d=>d.remove());let u=c.textContent||"";i&&(u=u.replace(/^ *(\$|>) /gm,"").trim()),Oa(u).then(()=>{n.classList.add("copied"),clearTimeout(e.get(n));const d=setTimeout(()=>{n.classList.remove("copied"),n.blur(),e.delete(n)},2e3);e.set(n,d)})}})}}async function Oa(e){try{return navigator.clipboard.writeText(e)}catch{const t=document.createElement("textarea"),n=document.activeElement;t.value=e,t.setAttribute("readonly",""),t.style.contain="strict",t.style.position="absolute",t.style.left="-9999px",t.style.fontSize="12pt";const s=document.getSelection(),r=s?s.rangeCount>0&&s.getRangeAt(0):null;document.body.appendChild(t),t.select(),t.selectionStart=0,t.selectionEnd=e.length,document.execCommand("copy"),document.body.removeChild(t),r&&(s.removeAllRanges(),s.addRange(r)),n&&n.focus()}}function uu(e,t){let n=!0,s=[];const r=o=>{if(n){n=!1,o.forEach(l=>{const c=Zn(l);for(const u of document.head.children)if(u.isEqualNode(c)){s.push(u);return}});return}const i=o.map(Zn);s.forEach((l,c)=>{const u=i.findIndex(d=>d==null?void 0:d.isEqualNode(l??null));u!==-1?delete i[u]:(l==null||l.remove(),delete s[c])}),i.forEach(l=>l&&document.head.appendChild(l)),s=[...s,...i].filter(Boolean)};po(()=>{const o=e.data,i=t.value,l=o&&o.description,c=o&&o.frontmatter.head||[],u=ei(i,o);u!==document.title&&(document.title=u);const d=l||i.description;let h=document.querySelector("meta[name=description]");h?h.getAttribute("content")!==d&&h.setAttribute("content",d):Zn(["meta",{name:"description",content:d}]),r(ti(i.head,Pa(c)))})}function Zn([e,t,n]){const s=document.createElement(e);for(const r in t)s.setAttribute(r,t[r]);return n&&(s.innerHTML=n),e==="script"&&!t.async&&(s.async=!1),s}function La(e){return e[0]==="meta"&&e[1]&&e[1].name==="description"}function Pa(e){return e.filter(t=>!La(t))}const es=new Set,ri=()=>document.createElement("link"),Ia=e=>{const t=ri();t.rel="prefetch",t.href=e,document.head.appendChild(t)},Ma=e=>{const t=new XMLHttpRequest;t.open("GET",e,t.withCredentials=!0),t.send()};let sn;const Na=Ce&&(sn=ri())&&sn.relList&&sn.relList.supports&&sn.relList.supports("prefetch")?Ia:Ma;function fu(){if(!Ce||!window.IntersectionObserver)return;let e;if((e=navigator.connection)&&(e.saveData||/2g/.test(e.effectiveType)))return;const t=window.requestIdleCallback||setTimeout;let n=null;const s=()=>{n&&n.disconnect(),n=new IntersectionObserver(o=>{o.forEach(i=>{if(i.isIntersecting){const l=i.target;n.unobserve(l);const{pathname:c}=l;if(!es.has(c)){es.add(c);const u=xa(c);u&&Na(u)}}})}),t(()=>{document.querySelectorAll("#app a").forEach(o=>{const{hostname:i,pathname:l}=new URL(o.href instanceof SVGAnimatedString?o.href.animVal:o.href,o.baseURI),c=l.match(/\.\w+$/);c&&c[0]!==".html"||o.target!=="_blank"&&i===location.hostname&&(l!==location.pathname?n.observe(o):es.add(l))})})};St(s);const r=si();Xe(()=>r.path,s),In(()=>{n&&n.disconnect()})}export{Ua as $,In as A,Da as B,El as C,Sa as D,ja as E,ge as F,Ba as G,no as H,ru as I,ae as J,Va as K,Jo as L,si as M,Ql as N,wt as O,Za as P,eu as Q,bs as R,Tn as S,ko as T,nu as U,qa as V,xn as W,tu as X,$l as Y,Xa as Z,Ja as _,jo as a,za as a0,Ka as a1,uu as a2,Ta as a3,su as a4,wa as a5,iu as a6,lu as a7,ct as a8,Ya as a9,ou as aa,xa as ab,Qa as ac,fu as ad,au as ae,cu as af,ds as ag,Fo as b,Wa as c,bo as d,Ga as e,va as f,Lr as g,me as h,ha as i,Ce as j,se as k,St as l,Ho as m,vs as n,Mo as o,ro as p,$a as q,ka as r,Ha as s,Fa as t,Ea as u,da as v,ol as w,ea as x,Xe as y,po as z}; diff --git a/assets/chunks/theme.LCXbL-fc.js b/assets/chunks/theme.LCXbL-fc.js new file mode 100644 index 00000000000..56e193ae19d --- /dev/null +++ b/assets/chunks/theme.LCXbL-fc.js @@ -0,0 +1 @@ +import{d as g,o as a,c as l,r as d,n as T,a as H,t as V,b as k,w as p,T as de,e as f,_ as m,u as Oe,i as xe,f as Ue,g as ve,h as L,j as K,k as $,l as G,m as u,p as r,q as E,s as F,v as U,x as ie,y as j,z as X,A as he,B as we,C as Ge,D as je,E as R,F as M,G as A,H as Pe,I as ee,J as _,K as x,L as Ve,M as te,N as J,O as se,P as Re,Q as qe,R as Ke,S as We,U as Le,V as Ye,W as Je,X as Se,Y as Me,Z as Ze,$ as Qe,a0 as Xe,a1 as et}from"./framework.gBlNPWt_.js";const tt=g({__name:"VPBadge",props:{text:{},type:{default:"tip"}},setup(s){return(e,t)=>(a(),l("span",{class:T(["VPBadge",e.type])},[d(e.$slots,"default",{},()=>[H(V(e.text),1)])],2))}}),st={key:0,class:"VPBackdrop"},nt=g({__name:"VPBackdrop",props:{show:{type:Boolean}},setup(s){return(e,t)=>(a(),k(de,{name:"fade"},{default:p(()=>[e.show?(a(),l("div",st)):f("",!0)]),_:1}))}}),ot=m(nt,[["__scopeId","data-v-54a304ca"]]),P=Oe;function at(s,e){let t,n=!1;return()=>{t&&clearTimeout(t),n?t=setTimeout(s,e):(s(),(n=!0)&&setTimeout(()=>n=!1,e))}}function le(s){return/^\//.test(s)?s:`/${s}`}function pe(s){const{pathname:e,search:t,hash:n,protocol:o}=new URL(s,"http://a.com");if(xe(s)||s.startsWith("#")||!o.startsWith("http")||!Ue(e))return s;const{site:i}=P(),c=e.endsWith("/")||e.endsWith(".html")?s:s.replace(/(?:(^\.+)\/)?.*$/,`$1${e.replace(/(\.md)?$/,i.value.cleanUrls?"":".html")}${t}${n}`);return ve(c)}const _e=L(K?location.hash:"");K&&window.addEventListener("hashchange",()=>{_e.value=location.hash});function W({removeCurrent:s=!0,correspondingLink:e=!1}={}){const{site:t,localeIndex:n,page:o,theme:i}=P(),c=$(()=>{var v,b;return{label:(v=t.value.locales[n.value])==null?void 0:v.label,link:((b=t.value.locales[n.value])==null?void 0:b.link)||(n.value==="root"?"/":`/${n.value}/`)}});return{localeLinks:$(()=>Object.entries(t.value.locales).flatMap(([v,b])=>s&&c.value.label===b.label?[]:{text:b.label,link:rt(b.link||(v==="root"?"/":`/${v}/`),i.value.i18nRouting!==!1&&e,o.value.relativePath.slice(c.value.link.length-1),!t.value.cleanUrls)+_e.value})),currentLang:c}}function rt(s,e,t,n){return e?s.replace(/\/$/,"")+le(t.replace(/(^|\/)index\.md$/,"$1").replace(/\.md$/,n?".html":"")):s}const it=s=>(E("data-v-b9c0c15a"),s=s(),F(),s),lt={class:"NotFound"},ct={class:"code"},ut={class:"title"},dt=it(()=>u("div",{class:"divider"},null,-1)),vt={class:"quote"},ht={class:"action"},pt=["href","aria-label"],_t=g({__name:"NotFound",setup(s){const{site:e,theme:t}=P(),{localeLinks:n}=W({removeCurrent:!1}),o=L("/");return G(()=>{var c;const i=window.location.pathname.replace(e.value.base,"").replace(/(^.*?\/).*$/,"/$1");n.value.length&&(o.value=((c=n.value.find(({link:h})=>h.startsWith(i)))==null?void 0:c.link)||n.value[0].link)}),(i,c)=>{var h,v,b,y,w;return a(),l("div",lt,[u("p",ct,V(((h=r(t).notFound)==null?void 0:h.code)??"404"),1),u("h1",ut,V(((v=r(t).notFound)==null?void 0:v.title)??"PAGE NOT FOUND"),1),dt,u("blockquote",vt,V(((b=r(t).notFound)==null?void 0:b.quote)??"But if you don't change your direction, and if you keep looking, you may end up where you are heading."),1),u("div",ht,[u("a",{class:"link",href:r(ve)(o.value),"aria-label":((y=r(t).notFound)==null?void 0:y.linkLabel)??"go to home"},V(((w=r(t).notFound)==null?void 0:w.linkText)??"Take me home"),9,pt)])])}}}),ft=m(_t,[["__scopeId","data-v-b9c0c15a"]]);function Ce(s,e){if(Array.isArray(s))return Z(s);if(s==null)return[];e=le(e);const t=Object.keys(s).sort((o,i)=>i.split("/").length-o.split("/").length).find(o=>e.startsWith(le(o))),n=t?s[t]:[];return Array.isArray(n)?Z(n):Z(n.items,n.base)}function mt(s){const e=[];let t=0;for(const n in s){const o=s[n];if(o.items){t=e.push(o);continue}e[t]||e.push({items:[]}),e[t].items.push(o)}return e}function gt(s){const e=[];function t(n){for(const o of n)o.text&&o.link&&e.push({text:o.text,link:o.link,docFooterText:o.docFooterText}),o.items&&t(o.items)}return t(s),e}function ce(s,e){return Array.isArray(e)?e.some(t=>ce(s,t)):U(s,e.link)?!0:e.items?ce(s,e.items):!1}function Z(s,e){return[...s].map(t=>{const n={...t},o=n.base||e;return o&&n.link&&(n.link=o+n.link),n.items&&(n.items=Z(n.items,o)),n})}function D(){const{frontmatter:s,page:e,theme:t}=P(),n=ie("(min-width: 960px)"),o=L(!1),i=$(()=>{const B=t.value.sidebar,S=e.value.relativePath;return B?Ce(B,S):[]}),c=L(i.value);j(i,(B,S)=>{JSON.stringify(B)!==JSON.stringify(S)&&(c.value=i.value)});const h=$(()=>s.value.sidebar!==!1&&c.value.length>0&&s.value.layout!=="home"),v=$(()=>b?s.value.aside==null?t.value.aside==="left":s.value.aside==="left":!1),b=$(()=>s.value.layout==="home"?!1:s.value.aside!=null?!!s.value.aside:t.value.aside!==!1),y=$(()=>h.value&&n.value),w=$(()=>h.value?mt(c.value):[]);function C(){o.value=!0}function I(){o.value=!1}function N(){o.value?I():C()}return{isOpen:o,sidebar:c,sidebarGroups:w,hasSidebar:h,hasAside:b,leftAside:v,isSidebarEnabled:y,open:C,close:I,toggle:N}}function kt(s,e){let t;X(()=>{t=s.value?document.activeElement:void 0}),G(()=>{window.addEventListener("keyup",n)}),he(()=>{window.removeEventListener("keyup",n)});function n(o){o.key==="Escape"&&s.value&&(e(),t==null||t.focus())}}function $t(s){const{page:e}=P(),t=L(!1),n=$(()=>s.value.collapsed!=null),o=$(()=>!!s.value.link),i=L(!1),c=()=>{i.value=U(e.value.relativePath,s.value.link)};j([e,s,_e],c),G(c);const h=$(()=>i.value?!0:s.value.items?ce(e.value.relativePath,s.value.items):!1),v=$(()=>!!(s.value.items&&s.value.items.length));X(()=>{t.value=!!(n.value&&s.value.collapsed)}),we(()=>{(i.value||h.value)&&(t.value=!1)});function b(){n.value&&(t.value=!t.value)}return{collapsed:t,collapsible:n,isLink:o,isActiveLink:i,hasActiveLink:h,hasChildren:v,toggle:b}}function bt(){const{hasSidebar:s}=D(),e=ie("(min-width: 960px)"),t=ie("(min-width: 1280px)");return{isAsideEnabled:$(()=>!t.value&&!e.value?!1:s.value?t.value:e.value)}}const ue=[];function Ie(s){return typeof s.outline=="object"&&!Array.isArray(s.outline)&&s.outline.label||s.outlineTitle||"On this page"}function fe(s){const e=[...document.querySelectorAll(".VPDoc :where(h1,h2,h3,h4,h5,h6)")].filter(t=>t.id&&t.hasChildNodes()).map(t=>{const n=Number(t.tagName[1]);return{element:t,title:yt(t),link:"#"+t.id,level:n}});return wt(e,s)}function yt(s){let e="";for(const t of s.childNodes)if(t.nodeType===1){if(t.classList.contains("VPBadge")||t.classList.contains("header-anchor")||t.classList.contains("ignore-header"))continue;e+=t.textContent}else t.nodeType===3&&(e+=t.textContent);return e.trim()}function wt(s,e){if(e===!1)return[];const t=(typeof e=="object"&&!Array.isArray(e)?e.level:e)||2,[n,o]=typeof t=="number"?[t,t]:t==="deep"?[2,6]:t;s=s.filter(c=>c.level>=n&&c.level<=o),ue.length=0;for(const{element:c,link:h}of s)ue.push({element:c,link:h});const i=[];e:for(let c=0;c=0;v--){const b=s[v];if(b.level{requestAnimationFrame(i),window.addEventListener("scroll",n)}),Ge(()=>{c(location.hash)}),he(()=>{window.removeEventListener("scroll",n)});function i(){if(!t.value)return;const h=window.scrollY,v=window.innerHeight,b=document.body.offsetHeight,y=Math.abs(h+v-b)<1,w=ue.map(({element:I,link:N})=>({link:N,top:Vt(I)})).filter(({top:I})=>!Number.isNaN(I)).sort((I,N)=>I.top-N.top);if(!w.length){c(null);return}if(h<1){c(null);return}if(y){c(w[w.length-1].link);return}let C=null;for(const{link:I,top:N}of w){if(N>h+je()+4)break;C=I}c(C)}function c(h){o&&o.classList.remove("active"),h==null?o=null:o=s.value.querySelector(`a[href="${decodeURIComponent(h)}"]`);const v=o;v?(v.classList.add("active"),e.value.style.top=v.offsetTop+39+"px",e.value.style.opacity="1"):(e.value.style.top="33px",e.value.style.opacity="0")}}function Vt(s){let e=0;for(;s!==document.body;){if(s===null)return NaN;e+=s.offsetTop,s=s.offsetParent}return e}const Lt=["href","title"],St=g({__name:"VPDocOutlineItem",props:{headers:{},root:{type:Boolean}},setup(s){function e({target:t}){const n=t.href.split("#")[1],o=document.getElementById(decodeURIComponent(n));o==null||o.focus({preventScroll:!0})}return(t,n)=>{const o=R("VPDocOutlineItem",!0);return a(),l("ul",{class:T(["VPDocOutlineItem",t.root?"root":"nested"])},[(a(!0),l(M,null,A(t.headers,({children:i,link:c,title:h})=>(a(),l("li",null,[u("a",{class:"outline-link",href:c,onClick:e,title:h},V(h),9,Lt),i!=null&&i.length?(a(),k(o,{key:0,headers:i},null,8,["headers"])):f("",!0)]))),256))],2)}}}),Te=m(St,[["__scopeId","data-v-53c99d69"]]),Mt=s=>(E("data-v-6b52fe58"),s=s(),F(),s),Ct={class:"content"},It={class:"outline-title",role:"heading","aria-level":"2"},Tt={"aria-labelledby":"doc-outline-aria-label"},Nt=Mt(()=>u("span",{class:"visually-hidden",id:"doc-outline-aria-label"}," Table of Contents for current page ",-1)),Bt=g({__name:"VPDocAsideOutline",setup(s){const{frontmatter:e,theme:t}=P(),n=Pe([]);ee(()=>{n.value=fe(e.value.outline??t.value.outline)});const o=L(),i=L();return Pt(o,i),(c,h)=>(a(),l("div",{class:T(["VPDocAsideOutline",{"has-outline":n.value.length>0}]),ref_key:"container",ref:o,role:"navigation"},[u("div",Ct,[u("div",{class:"outline-marker",ref_key:"marker",ref:i},null,512),u("div",It,V(r(Ie)(r(t))),1),u("nav",Tt,[Nt,_(Te,{headers:n.value,root:!0},null,8,["headers"])])])],2))}}),At=m(Bt,[["__scopeId","data-v-6b52fe58"]]),Ht={class:"VPDocAsideCarbonAds"},zt=g({__name:"VPDocAsideCarbonAds",props:{carbonAds:{}},setup(s){const e=()=>null;return(t,n)=>(a(),l("div",Ht,[_(r(e),{"carbon-ads":t.carbonAds},null,8,["carbon-ads"])]))}}),Et=s=>(E("data-v-cb998dce"),s=s(),F(),s),Ft={class:"VPDocAside"},Dt=Et(()=>u("div",{class:"spacer"},null,-1)),Ot=g({__name:"VPDocAside",setup(s){const{theme:e}=P();return(t,n)=>(a(),l("div",Ft,[d(t.$slots,"aside-top",{},void 0,!0),d(t.$slots,"aside-outline-before",{},void 0,!0),_(At),d(t.$slots,"aside-outline-after",{},void 0,!0),Dt,d(t.$slots,"aside-ads-before",{},void 0,!0),r(e).carbonAds?(a(),k(zt,{key:0,"carbon-ads":r(e).carbonAds},null,8,["carbon-ads"])):f("",!0),d(t.$slots,"aside-ads-after",{},void 0,!0),d(t.$slots,"aside-bottom",{},void 0,!0)]))}}),xt=m(Ot,[["__scopeId","data-v-cb998dce"]]);function Ut(){const{theme:s,page:e}=P();return $(()=>{const{text:t="Edit this page",pattern:n=""}=s.value.editLink||{};let o;return typeof n=="function"?o=n(e.value):o=n.replace(/:path/g,e.value.filePath),{url:o,text:t}})}function Gt(){const{page:s,theme:e,frontmatter:t}=P();return $(()=>{var v,b,y,w,C,I,N,B;const n=Ce(e.value.sidebar,s.value.relativePath),o=gt(n),i=o.findIndex(S=>U(s.value.relativePath,S.link)),c=((v=e.value.docFooter)==null?void 0:v.prev)===!1&&!t.value.prev||t.value.prev===!1,h=((b=e.value.docFooter)==null?void 0:b.next)===!1&&!t.value.next||t.value.next===!1;return{prev:c?void 0:{text:(typeof t.value.prev=="string"?t.value.prev:typeof t.value.prev=="object"?t.value.prev.text:void 0)??((y=o[i-1])==null?void 0:y.docFooterText)??((w=o[i-1])==null?void 0:w.text),link:(typeof t.value.prev=="object"?t.value.prev.link:void 0)??((C=o[i-1])==null?void 0:C.link)},next:h?void 0:{text:(typeof t.value.next=="string"?t.value.next:typeof t.value.next=="object"?t.value.next.text:void 0)??((I=o[i+1])==null?void 0:I.docFooterText)??((N=o[i+1])==null?void 0:N.text),link:(typeof t.value.next=="object"?t.value.next.link:void 0)??((B=o[i+1])==null?void 0:B.link)}}})}const jt={},Rt={xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},qt=u("path",{d:"M18,23H4c-1.7,0-3-1.3-3-3V6c0-1.7,1.3-3,3-3h7c0.6,0,1,0.4,1,1s-0.4,1-1,1H4C3.4,5,3,5.4,3,6v14c0,0.6,0.4,1,1,1h14c0.6,0,1-0.4,1-1v-7c0-0.6,0.4-1,1-1s1,0.4,1,1v7C21,21.7,19.7,23,18,23z"},null,-1),Kt=u("path",{d:"M8,17c-0.3,0-0.5-0.1-0.7-0.3C7,16.5,6.9,16.1,7,15.8l1-4c0-0.2,0.1-0.3,0.3-0.5l9.5-9.5c1.2-1.2,3.2-1.2,4.4,0c1.2,1.2,1.2,3.2,0,4.4l-9.5,9.5c-0.1,0.1-0.3,0.2-0.5,0.3l-4,1C8.2,17,8.1,17,8,17zM9.9,12.5l-0.5,2.1l2.1-0.5l9.3-9.3c0.4-0.4,0.4-1.1,0-1.6c-0.4-0.4-1.2-0.4-1.6,0l0,0L9.9,12.5z M18.5,2.5L18.5,2.5L18.5,2.5z"},null,-1),Wt=[qt,Kt];function Yt(s,e){return a(),l("svg",Rt,Wt)}const Jt=m(jt,[["render",Yt]]),z=g({__name:"VPLink",props:{tag:{},href:{},noIcon:{type:Boolean},target:{},rel:{}},setup(s){const e=s,t=$(()=>e.tag??(e.href?"a":"span")),n=$(()=>e.href&&Ve.test(e.href));return(o,i)=>(a(),k(x(t.value),{class:T(["VPLink",{link:o.href,"vp-external-link-icon":n.value,"no-icon":o.noIcon}]),href:o.href?r(pe)(o.href):void 0,target:o.target??(n.value?"_blank":void 0),rel:o.rel??(n.value?"noreferrer":void 0)},{default:p(()=>[d(o.$slots,"default")]),_:3},8,["class","href","target","rel"]))}}),Zt={class:"VPLastUpdated"},Qt=["datetime"],Xt=g({__name:"VPDocFooterLastUpdated",setup(s){const{theme:e,page:t,frontmatter:n,lang:o}=P(),i=$(()=>new Date(n.value.lastUpdated??t.value.lastUpdated)),c=$(()=>i.value.toISOString()),h=L("");return G(()=>{X(()=>{var v,b,y;h.value=new Intl.DateTimeFormat((b=(v=e.value.lastUpdated)==null?void 0:v.formatOptions)!=null&&b.forceLocale?o.value:void 0,((y=e.value.lastUpdated)==null?void 0:y.formatOptions)??{dateStyle:"short",timeStyle:"short"}).format(i.value)})}),(v,b)=>{var y;return a(),l("p",Zt,[H(V(((y=r(e).lastUpdated)==null?void 0:y.text)||r(e).lastUpdatedText||"Last updated")+": ",1),u("time",{datetime:c.value},V(h.value),9,Qt)])}}}),es=m(Xt,[["__scopeId","data-v-19a7ae4e"]]),ts={key:0,class:"VPDocFooter"},ss={key:0,class:"edit-info"},ns={key:0,class:"edit-link"},os={key:1,class:"last-updated"},as={key:1,class:"prev-next"},rs={class:"pager"},is=["innerHTML"],ls=["innerHTML"],cs={class:"pager"},us=["innerHTML"],ds=["innerHTML"],vs=g({__name:"VPDocFooter",setup(s){const{theme:e,page:t,frontmatter:n}=P(),o=Ut(),i=Gt(),c=$(()=>e.value.editLink&&n.value.editLink!==!1),h=$(()=>t.value.lastUpdated&&n.value.lastUpdated!==!1),v=$(()=>c.value||h.value||i.value.prev||i.value.next);return(b,y)=>{var w,C,I,N;return v.value?(a(),l("footer",ts,[d(b.$slots,"doc-footer-before",{},void 0,!0),c.value||h.value?(a(),l("div",ss,[c.value?(a(),l("div",ns,[_(z,{class:"edit-link-button",href:r(o).url,"no-icon":!0},{default:p(()=>[_(Jt,{class:"edit-link-icon","aria-label":"edit icon"}),H(" "+V(r(o).text),1)]),_:1},8,["href"])])):f("",!0),h.value?(a(),l("div",os,[_(es)])):f("",!0)])):f("",!0),(w=r(i).prev)!=null&&w.link||(C=r(i).next)!=null&&C.link?(a(),l("nav",as,[u("div",rs,[(I=r(i).prev)!=null&&I.link?(a(),k(z,{key:0,class:"pager-link prev",href:r(i).prev.link},{default:p(()=>{var B;return[u("span",{class:"desc",innerHTML:((B=r(e).docFooter)==null?void 0:B.prev)||"Previous page"},null,8,is),u("span",{class:"title",innerHTML:r(i).prev.text},null,8,ls)]}),_:1},8,["href"])):f("",!0)]),u("div",cs,[(N=r(i).next)!=null&&N.link?(a(),k(z,{key:0,class:"pager-link next",href:r(i).next.link},{default:p(()=>{var B;return[u("span",{class:"desc",innerHTML:((B=r(e).docFooter)==null?void 0:B.next)||"Next page"},null,8,us),u("span",{class:"title",innerHTML:r(i).next.text},null,8,ds)]}),_:1},8,["href"])):f("",!0)])])):f("",!0)])):f("",!0)}}}),hs=m(vs,[["__scopeId","data-v-b4b63abf"]]),ps=s=>(E("data-v-e6f2a212"),s=s(),F(),s),_s={class:"container"},fs=ps(()=>u("div",{class:"aside-curtain"},null,-1)),ms={class:"aside-container"},gs={class:"aside-content"},ks={class:"content"},$s={class:"content-container"},bs={class:"main"},ys=g({__name:"VPDoc",setup(s){const{theme:e}=P(),t=te(),{hasSidebar:n,hasAside:o,leftAside:i}=D(),c=$(()=>t.path.replace(/[./]+/g,"_").replace(/_html$/,""));return(h,v)=>{const b=R("Content");return a(),l("div",{class:T(["VPDoc",{"has-sidebar":r(n),"has-aside":r(o)}])},[d(h.$slots,"doc-top",{},void 0,!0),u("div",_s,[r(o)?(a(),l("div",{key:0,class:T(["aside",{"left-aside":r(i)}])},[fs,u("div",ms,[u("div",gs,[_(xt,null,{"aside-top":p(()=>[d(h.$slots,"aside-top",{},void 0,!0)]),"aside-bottom":p(()=>[d(h.$slots,"aside-bottom",{},void 0,!0)]),"aside-outline-before":p(()=>[d(h.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":p(()=>[d(h.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":p(()=>[d(h.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":p(()=>[d(h.$slots,"aside-ads-after",{},void 0,!0)]),_:3})])])],2)):f("",!0),u("div",ks,[u("div",$s,[d(h.$slots,"doc-before",{},void 0,!0),u("main",bs,[_(b,{class:T(["vp-doc",[c.value,r(e).externalLinkIcon&&"external-link-icon-enabled"]])},null,8,["class"])]),_(hs,null,{"doc-footer-before":p(()=>[d(h.$slots,"doc-footer-before",{},void 0,!0)]),_:3}),d(h.$slots,"doc-after",{},void 0,!0)])])]),d(h.$slots,"doc-bottom",{},void 0,!0)],2)}}}),ws=m(ys,[["__scopeId","data-v-e6f2a212"]]),Ps=g({__name:"VPButton",props:{tag:{},size:{default:"medium"},theme:{default:"brand"},text:{},href:{}},setup(s){const e=s,t=$(()=>e.href&&Ve.test(e.href)),n=$(()=>e.tag||e.href?"a":"button");return(o,i)=>(a(),k(x(n.value),{class:T(["VPButton",[o.size,o.theme]]),href:o.href?r(pe)(o.href):void 0,target:t.value?"_blank":void 0,rel:t.value?"noreferrer":void 0},{default:p(()=>[H(V(o.text),1)]),_:1},8,["class","href","target","rel"]))}}),Vs=m(Ps,[["__scopeId","data-v-1e76fe75"]]),Ls=["src","alt"],Ss=g({inheritAttrs:!1,__name:"VPImage",props:{image:{},alt:{}},setup(s){return(e,t)=>{const n=R("VPImage",!0);return e.image?(a(),l(M,{key:0},[typeof e.image=="string"||"src"in e.image?(a(),l("img",J({key:0,class:"VPImage"},typeof e.image=="string"?e.$attrs:{...e.image,...e.$attrs},{src:r(ve)(typeof e.image=="string"?e.image:e.image.src),alt:e.alt??(typeof e.image=="string"?"":e.image.alt||"")}),null,16,Ls)):(a(),l(M,{key:1},[_(n,J({class:"dark",image:e.image.dark,alt:e.image.alt},e.$attrs),null,16,["image","alt"]),_(n,J({class:"light",image:e.image.light,alt:e.image.alt},e.$attrs),null,16,["image","alt"])],64))],64)):f("",!0)}}}),Q=m(Ss,[["__scopeId","data-v-ab19afbb"]]),Ms=s=>(E("data-v-5a3e9999"),s=s(),F(),s),Cs={class:"container"},Is={class:"main"},Ts={key:0,class:"name"},Ns=["innerHTML"],Bs=["innerHTML"],As=["innerHTML"],Hs={key:0,class:"actions"},zs={key:0,class:"image"},Es={class:"image-container"},Fs=Ms(()=>u("div",{class:"image-bg"},null,-1)),Ds=g({__name:"VPHero",props:{name:{},text:{},tagline:{},image:{},actions:{}},setup(s){const e=se("hero-image-slot-exists");return(t,n)=>(a(),l("div",{class:T(["VPHero",{"has-image":t.image||r(e)}])},[u("div",Cs,[u("div",Is,[d(t.$slots,"home-hero-info",{},()=>[t.name?(a(),l("h1",Ts,[u("span",{innerHTML:t.name,class:"clip"},null,8,Ns)])):f("",!0),t.text?(a(),l("p",{key:1,innerHTML:t.text,class:"text"},null,8,Bs)):f("",!0),t.tagline?(a(),l("p",{key:2,innerHTML:t.tagline,class:"tagline"},null,8,As)):f("",!0)],!0),t.actions?(a(),l("div",Hs,[(a(!0),l(M,null,A(t.actions,o=>(a(),l("div",{key:o.link,class:"action"},[_(Vs,{tag:"a",size:"medium",theme:o.theme,text:o.text,href:o.link},null,8,["theme","text","href"])]))),128))])):f("",!0)]),t.image||r(e)?(a(),l("div",zs,[u("div",Es,[Fs,d(t.$slots,"home-hero-image",{},()=>[t.image?(a(),k(Q,{key:0,class:"image-src",image:t.image},null,8,["image"])):f("",!0)],!0)])])):f("",!0)])],2))}}),Os=m(Ds,[["__scopeId","data-v-5a3e9999"]]),xs=g({__name:"VPHomeHero",setup(s){const{frontmatter:e}=P();return(t,n)=>r(e).hero?(a(),k(Os,{key:0,class:"VPHomeHero",name:r(e).hero.name,text:r(e).hero.text,tagline:r(e).hero.tagline,image:r(e).hero.image,actions:r(e).hero.actions},{"home-hero-info":p(()=>[d(t.$slots,"home-hero-info")]),"home-hero-image":p(()=>[d(t.$slots,"home-hero-image")]),_:3},8,["name","text","tagline","image","actions"])):f("",!0)}}),Us={},Gs={xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},js=u("path",{d:"M19.9,12.4c0.1-0.2,0.1-0.5,0-0.8c-0.1-0.1-0.1-0.2-0.2-0.3l-7-7c-0.4-0.4-1-0.4-1.4,0s-0.4,1,0,1.4l5.3,5.3H5c-0.6,0-1,0.4-1,1s0.4,1,1,1h11.6l-5.3,5.3c-0.4,0.4-0.4,1,0,1.4c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3l7-7C19.8,12.6,19.9,12.5,19.9,12.4z"},null,-1),Rs=[js];function qs(s,e){return a(),l("svg",Gs,Rs)}const Ks=m(Us,[["render",qs]]),Ws={class:"box"},Ys={key:0,class:"icon"},Js=["innerHTML"],Zs=["innerHTML"],Qs=["innerHTML"],Xs={key:4,class:"link-text"},en={class:"link-text-value"},tn=g({__name:"VPFeature",props:{icon:{},title:{},details:{},link:{},linkText:{},rel:{},target:{}},setup(s){return(e,t)=>(a(),k(z,{class:"VPFeature",href:e.link,rel:e.rel,target:e.target,"no-icon":!0,tag:e.link?"a":"div"},{default:p(()=>[u("article",Ws,[typeof e.icon=="object"&&e.icon.wrap?(a(),l("div",Ys,[_(Q,{image:e.icon,alt:e.icon.alt,height:e.icon.height||48,width:e.icon.width||48},null,8,["image","alt","height","width"])])):typeof e.icon=="object"?(a(),k(Q,{key:1,image:e.icon,alt:e.icon.alt,height:e.icon.height||48,width:e.icon.width||48},null,8,["image","alt","height","width"])):e.icon?(a(),l("div",{key:2,class:"icon",innerHTML:e.icon},null,8,Js)):f("",!0),u("h2",{class:"title",innerHTML:e.title},null,8,Zs),e.details?(a(),l("p",{key:3,class:"details",innerHTML:e.details},null,8,Qs)):f("",!0),e.linkText?(a(),l("div",Xs,[u("p",en,[H(V(e.linkText)+" ",1),_(Ks,{class:"link-text-icon"})])])):f("",!0)])]),_:1},8,["href","rel","target","tag"]))}}),sn=m(tn,[["__scopeId","data-v-ee984185"]]),nn={key:0,class:"VPFeatures"},on={class:"container"},an={class:"items"},rn=g({__name:"VPFeatures",props:{features:{}},setup(s){const e=s,t=$(()=>{const n=e.features.length;if(n){if(n===2)return"grid-2";if(n===3)return"grid-3";if(n%3===0)return"grid-6";if(n>3)return"grid-4"}else return});return(n,o)=>n.features?(a(),l("div",nn,[u("div",on,[u("div",an,[(a(!0),l(M,null,A(n.features,i=>(a(),l("div",{key:i.title,class:T(["item",[t.value]])},[_(sn,{icon:i.icon,title:i.title,details:i.details,link:i.link,"link-text":i.linkText,rel:i.rel,target:i.target},null,8,["icon","title","details","link","link-text","rel","target"])],2))),128))])])])):f("",!0)}}),ln=m(rn,[["__scopeId","data-v-b1eea84a"]]),cn=g({__name:"VPHomeFeatures",setup(s){const{frontmatter:e}=P();return(t,n)=>r(e).features?(a(),k(ln,{key:0,class:"VPHomeFeatures",features:r(e).features},null,8,["features"])):f("",!0)}}),un={class:"VPHome"},dn=g({__name:"VPHome",setup(s){return(e,t)=>{const n=R("Content");return a(),l("div",un,[d(e.$slots,"home-hero-before",{},void 0,!0),_(xs,null,{"home-hero-info":p(()=>[d(e.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-image":p(()=>[d(e.$slots,"home-hero-image",{},void 0,!0)]),_:3}),d(e.$slots,"home-hero-after",{},void 0,!0),d(e.$slots,"home-features-before",{},void 0,!0),_(cn),d(e.$slots,"home-features-after",{},void 0,!0),_(n)])}}}),vn=m(dn,[["__scopeId","data-v-20eabd3a"]]),hn={},pn={class:"VPPage"};function _n(s,e){const t=R("Content");return a(),l("div",pn,[d(s.$slots,"page-top"),_(t),d(s.$slots,"page-bottom")])}const fn=m(hn,[["render",_n]]),mn=g({__name:"VPContent",setup(s){const{page:e,frontmatter:t}=P(),{hasSidebar:n}=D();return(o,i)=>(a(),l("div",{class:T(["VPContent",{"has-sidebar":r(n),"is-home":r(t).layout==="home"}]),id:"VPContent"},[r(e).isNotFound?d(o.$slots,"not-found",{key:0},()=>[_(ft)],!0):r(t).layout==="page"?(a(),k(fn,{key:1},{"page-top":p(()=>[d(o.$slots,"page-top",{},void 0,!0)]),"page-bottom":p(()=>[d(o.$slots,"page-bottom",{},void 0,!0)]),_:3})):r(t).layout==="home"?(a(),k(vn,{key:2},{"home-hero-before":p(()=>[d(o.$slots,"home-hero-before",{},void 0,!0)]),"home-hero-info":p(()=>[d(o.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-image":p(()=>[d(o.$slots,"home-hero-image",{},void 0,!0)]),"home-hero-after":p(()=>[d(o.$slots,"home-hero-after",{},void 0,!0)]),"home-features-before":p(()=>[d(o.$slots,"home-features-before",{},void 0,!0)]),"home-features-after":p(()=>[d(o.$slots,"home-features-after",{},void 0,!0)]),_:3})):r(t).layout&&r(t).layout!=="doc"?(a(),k(x(r(t).layout),{key:3})):(a(),k(ws,{key:4},{"doc-top":p(()=>[d(o.$slots,"doc-top",{},void 0,!0)]),"doc-bottom":p(()=>[d(o.$slots,"doc-bottom",{},void 0,!0)]),"doc-footer-before":p(()=>[d(o.$slots,"doc-footer-before",{},void 0,!0)]),"doc-before":p(()=>[d(o.$slots,"doc-before",{},void 0,!0)]),"doc-after":p(()=>[d(o.$slots,"doc-after",{},void 0,!0)]),"aside-top":p(()=>[d(o.$slots,"aside-top",{},void 0,!0)]),"aside-outline-before":p(()=>[d(o.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":p(()=>[d(o.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":p(()=>[d(o.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":p(()=>[d(o.$slots,"aside-ads-after",{},void 0,!0)]),"aside-bottom":p(()=>[d(o.$slots,"aside-bottom",{},void 0,!0)]),_:3}))],2))}}),gn=m(mn,[["__scopeId","data-v-3cf691b6"]]),kn={class:"container"},$n=["innerHTML"],bn=["innerHTML"],yn=g({__name:"VPFooter",setup(s){const{theme:e,frontmatter:t}=P(),{hasSidebar:n}=D();return(o,i)=>r(e).footer&&r(t).footer!==!1?(a(),l("footer",{key:0,class:T(["VPFooter",{"has-sidebar":r(n)}])},[u("div",kn,[r(e).footer.message?(a(),l("p",{key:0,class:"message",innerHTML:r(e).footer.message},null,8,$n)):f("",!0),r(e).footer.copyright?(a(),l("p",{key:1,class:"copyright",innerHTML:r(e).footer.copyright},null,8,bn)):f("",!0)])],2)):f("",!0)}}),wn=m(yn,[["__scopeId","data-v-566314d4"]]);function Ne(){const{theme:s,frontmatter:e}=P(),t=Pe([]),n=$(()=>t.value.length>0);return ee(()=>{t.value=fe(e.value.outline??s.value.outline)}),{headers:t,hasLocalNav:n}}const Pn={},Vn={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},Ln=u("path",{d:"M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"},null,-1),Sn=[Ln];function Mn(s,e){return a(),l("svg",Vn,Sn)}const Be=m(Pn,[["render",Mn]]),Cn={class:"header"},In={class:"outline"},Tn=g({__name:"VPLocalNavOutlineDropdown",props:{headers:{},navHeight:{}},setup(s){const e=s,{theme:t}=P(),n=L(!1),o=L(0),i=L(),c=L();Re(i,()=>{n.value=!1}),qe("Escape",()=>{n.value=!1}),ee(()=>{n.value=!1});function h(){n.value=!n.value,o.value=window.innerHeight+Math.min(window.scrollY-e.navHeight,0)}function v(y){y.target.classList.contains("outline-link")&&(c.value&&(c.value.style.transition="none"),We(()=>{n.value=!1}))}function b(){n.value=!1,window.scrollTo({top:0,left:0,behavior:"smooth"})}return(y,w)=>(a(),l("div",{class:"VPLocalNavOutlineDropdown",style:Ke({"--vp-vh":o.value+"px"}),ref_key:"main",ref:i},[y.headers.length>0?(a(),l("button",{key:0,onClick:h,class:T({open:n.value})},[H(V(r(Ie)(r(t)))+" ",1),_(Be,{class:"icon"})],2)):(a(),l("button",{key:1,onClick:b},V(r(t).returnToTopLabel||"Return to top"),1)),_(de,{name:"flyout"},{default:p(()=>[n.value?(a(),l("div",{key:0,ref_key:"items",ref:c,class:"items",onClick:v},[u("div",Cn,[u("a",{class:"top-link",href:"#",onClick:b},V(r(t).returnToTopLabel||"Return to top"),1)]),u("div",In,[_(Te,{headers:y.headers},null,8,["headers"])])],512)):f("",!0)]),_:1})],4))}}),Nn=m(Tn,[["__scopeId","data-v-2744f6e0"]]),Bn={},An={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},Hn=u("path",{d:"M17,11H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,11,17,11z"},null,-1),zn=u("path",{d:"M21,7H3C2.4,7,2,6.6,2,6s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,7,21,7z"},null,-1),En=u("path",{d:"M21,15H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,15,21,15z"},null,-1),Fn=u("path",{d:"M17,19H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,19,17,19z"},null,-1),Dn=[Hn,zn,En,Fn];function On(s,e){return a(),l("svg",An,Dn)}const xn=m(Bn,[["render",On]]),Un={class:"container"},Gn=["aria-expanded"],jn={class:"menu-text"},Rn=g({__name:"VPLocalNav",props:{open:{type:Boolean}},emits:["open-menu"],setup(s){const{theme:e,frontmatter:t}=P(),{hasSidebar:n}=D(),{headers:o}=Ne(),{y:i}=Le(),c=L(0);G(()=>{c.value=parseInt(getComputedStyle(document.documentElement).getPropertyValue("--vp-nav-height"))}),ee(()=>{o.value=fe(t.value.outline??e.value.outline)});const h=$(()=>o.value.length===0),v=$(()=>h.value&&!n.value),b=$(()=>({VPLocalNav:!0,"has-sidebar":n.value,empty:h.value,fixed:v.value}));return(y,w)=>r(t).layout!=="home"&&(!v.value||r(i)>=c.value)?(a(),l("div",{key:0,class:T(b.value)},[u("div",Un,[r(n)?(a(),l("button",{key:0,class:"menu","aria-expanded":y.open,"aria-controls":"VPSidebarNav",onClick:w[0]||(w[0]=C=>y.$emit("open-menu"))},[_(xn,{class:"menu-icon"}),u("span",jn,V(r(e).sidebarMenuLabel||"Menu"),1)],8,Gn)):f("",!0),_(Nn,{headers:r(o),navHeight:c.value},null,8,["headers","navHeight"])])],2)):f("",!0)}}),qn=m(Rn,[["__scopeId","data-v-b979e4d9"]]);function Kn(){const s=L(!1);function e(){s.value=!0,window.addEventListener("resize",o)}function t(){s.value=!1,window.removeEventListener("resize",o)}function n(){s.value?t():e()}function o(){window.outerWidth>=768&&t()}const i=te();return j(()=>i.path,t),{isScreenOpen:s,openScreen:e,closeScreen:t,toggleScreen:n}}const Wn={},Yn={class:"VPSwitch",type:"button",role:"switch"},Jn={class:"check"},Zn={key:0,class:"icon"};function Qn(s,e){return a(),l("button",Yn,[u("span",Jn,[s.$slots.default?(a(),l("span",Zn,[d(s.$slots,"default",{},void 0,!0)])):f("",!0)])])}const Xn=m(Wn,[["render",Qn],["__scopeId","data-v-1c29e291"]]),eo={},to={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},so=u("path",{d:"M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z"},null,-1),no=[so];function oo(s,e){return a(),l("svg",to,no)}const ao=m(eo,[["render",oo]]),ro={},io={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},lo=Ye('',9),co=[lo];function uo(s,e){return a(),l("svg",io,co)}const vo=m(ro,[["render",uo]]),ho=g({__name:"VPSwitchAppearance",setup(s){const{isDark:e,theme:t}=P(),n=se("toggle-appearance",()=>{e.value=!e.value}),o=$(()=>e.value?t.value.lightModeSwitchTitle||"Switch to light theme":t.value.darkModeSwitchTitle||"Switch to dark theme");return(i,c)=>(a(),k(Xn,{title:o.value,class:"VPSwitchAppearance","aria-checked":r(e),onClick:r(n)},{default:p(()=>[_(vo,{class:"sun"}),_(ao,{class:"moon"})]),_:1},8,["title","aria-checked","onClick"]))}}),me=m(ho,[["__scopeId","data-v-d80abb8e"]]),po={key:0,class:"VPNavBarAppearance"},_o=g({__name:"VPNavBarAppearance",setup(s){const{site:e}=P();return(t,n)=>r(e).appearance&&r(e).appearance!=="force-dark"?(a(),l("div",po,[_(me)])):f("",!0)}}),fo=m(_o,[["__scopeId","data-v-283b26e9"]]),ge=L();let Ae=!1,re=0;function mo(s){const e=L(!1);if(K){!Ae&&go(),re++;const t=j(ge,n=>{var o,i,c;n===s.el.value||(o=s.el.value)!=null&&o.contains(n)?(e.value=!0,(i=s.onFocus)==null||i.call(s)):(e.value=!1,(c=s.onBlur)==null||c.call(s))});he(()=>{t(),re--,re||ko()})}return Je(e)}function go(){document.addEventListener("focusin",He),Ae=!0,ge.value=document.activeElement}function ko(){document.removeEventListener("focusin",He)}function He(){ge.value=document.activeElement}const $o={},bo={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},yo=u("path",{d:"M12,16c-0.3,0-0.5-0.1-0.7-0.3l-6-6c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l5.3,5.3l5.3-5.3c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-6,6C12.5,15.9,12.3,16,12,16z"},null,-1),wo=[yo];function Po(s,e){return a(),l("svg",bo,wo)}const ze=m($o,[["render",Po]]),Vo={},Lo={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},So=u("circle",{cx:"12",cy:"12",r:"2"},null,-1),Mo=u("circle",{cx:"19",cy:"12",r:"2"},null,-1),Co=u("circle",{cx:"5",cy:"12",r:"2"},null,-1),Io=[So,Mo,Co];function To(s,e){return a(),l("svg",Lo,Io)}const No=m(Vo,[["render",To]]),Bo={class:"VPMenuLink"},Ao=g({__name:"VPMenuLink",props:{item:{}},setup(s){const{page:e}=P();return(t,n)=>(a(),l("div",Bo,[_(z,{class:T({active:r(U)(r(e).relativePath,t.item.activeMatch||t.item.link,!!t.item.activeMatch)}),href:t.item.link,target:t.item.target,rel:t.item.rel},{default:p(()=>[H(V(t.item.text),1)]),_:1},8,["class","href","target","rel"])]))}}),ne=m(Ao,[["__scopeId","data-v-f51f088d"]]),Ho={class:"VPMenuGroup"},zo={key:0,class:"title"},Eo=g({__name:"VPMenuGroup",props:{text:{},items:{}},setup(s){return(e,t)=>(a(),l("div",Ho,[e.text?(a(),l("p",zo,V(e.text),1)):f("",!0),(a(!0),l(M,null,A(e.items,n=>(a(),l(M,null,["link"in n?(a(),k(ne,{key:0,item:n},null,8,["item"])):f("",!0)],64))),256))]))}}),Fo=m(Eo,[["__scopeId","data-v-a6b0397c"]]),Do={class:"VPMenu"},Oo={key:0,class:"items"},xo=g({__name:"VPMenu",props:{items:{}},setup(s){return(e,t)=>(a(),l("div",Do,[e.items?(a(),l("div",Oo,[(a(!0),l(M,null,A(e.items,n=>(a(),l(M,{key:n.text},["link"in n?(a(),k(ne,{key:0,item:n},null,8,["item"])):(a(),k(Fo,{key:1,text:n.text,items:n.items},null,8,["text","items"]))],64))),128))])):f("",!0),d(e.$slots,"default",{},void 0,!0)]))}}),Uo=m(xo,[["__scopeId","data-v-e42ed9b3"]]),Go=["aria-expanded","aria-label"],jo={key:0,class:"text"},Ro=["innerHTML"],qo={class:"menu"},Ko=g({__name:"VPFlyout",props:{icon:{},button:{},label:{},items:{}},setup(s){const e=L(!1),t=L();mo({el:t,onBlur:n});function n(){e.value=!1}return(o,i)=>(a(),l("div",{class:"VPFlyout",ref_key:"el",ref:t,onMouseenter:i[1]||(i[1]=c=>e.value=!0),onMouseleave:i[2]||(i[2]=c=>e.value=!1)},[u("button",{type:"button",class:"button","aria-haspopup":"true","aria-expanded":e.value,"aria-label":o.label,onClick:i[0]||(i[0]=c=>e.value=!e.value)},[o.button||o.icon?(a(),l("span",jo,[o.icon?(a(),k(x(o.icon),{key:0,class:"option-icon"})):f("",!0),o.button?(a(),l("span",{key:1,innerHTML:o.button},null,8,Ro)):f("",!0),_(ze,{class:"text-icon"})])):(a(),k(No,{key:1,class:"icon"}))],8,Go),u("div",qo,[_(Uo,{items:o.items},{default:p(()=>[d(o.$slots,"default",{},void 0,!0)]),_:3},8,["items"])])],544))}}),ke=m(Ko,[["__scopeId","data-v-aa8de344"]]),Wo={discord:'Discord',facebook:'Facebook',github:'GitHub',instagram:'Instagram',linkedin:'LinkedIn',mastodon:'Mastodon',npm:'npm',slack:'Slack',twitter:'Twitter',x:'X',youtube:'YouTube'},Yo=["href","aria-label","innerHTML"],Jo=g({__name:"VPSocialLink",props:{icon:{},link:{},ariaLabel:{}},setup(s){const e=s,t=$(()=>typeof e.icon=="object"?e.icon.svg:Wo[e.icon]);return(n,o)=>(a(),l("a",{class:"VPSocialLink no-icon",href:n.link,"aria-label":n.ariaLabel??(typeof n.icon=="string"?n.icon:""),target:"_blank",rel:"noopener",innerHTML:t.value},null,8,Yo))}}),Zo=m(Jo,[["__scopeId","data-v-16cf740a"]]),Qo={class:"VPSocialLinks"},Xo=g({__name:"VPSocialLinks",props:{links:{}},setup(s){return(e,t)=>(a(),l("div",Qo,[(a(!0),l(M,null,A(e.links,({link:n,icon:o,ariaLabel:i})=>(a(),k(Zo,{key:n,icon:o,link:n,ariaLabel:i},null,8,["icon","link","ariaLabel"]))),128))]))}}),$e=m(Xo,[["__scopeId","data-v-e71e869c"]]),ea={key:0,class:"group translations"},ta={class:"trans-title"},sa={key:1,class:"group"},na={class:"item appearance"},oa={class:"label"},aa={class:"appearance-action"},ra={key:2,class:"group"},ia={class:"item social-links"},la=g({__name:"VPNavBarExtra",setup(s){const{site:e,theme:t}=P(),{localeLinks:n,currentLang:o}=W({correspondingLink:!0}),i=$(()=>n.value.length&&o.value.label||e.value.appearance||t.value.socialLinks);return(c,h)=>i.value?(a(),k(ke,{key:0,class:"VPNavBarExtra",label:"extra navigation"},{default:p(()=>[r(n).length&&r(o).label?(a(),l("div",ea,[u("p",ta,V(r(o).label),1),(a(!0),l(M,null,A(r(n),v=>(a(),k(ne,{key:v.link,item:v},null,8,["item"]))),128))])):f("",!0),r(e).appearance&&r(e).appearance!=="force-dark"?(a(),l("div",sa,[u("div",na,[u("p",oa,V(r(t).darkModeSwitchLabel||"Appearance"),1),u("div",aa,[_(me)])])])):f("",!0),r(t).socialLinks?(a(),l("div",ra,[u("div",ia,[_($e,{class:"social-links-list",links:r(t).socialLinks},null,8,["links"])])])):f("",!0)]),_:1})):f("",!0)}}),ca=m(la,[["__scopeId","data-v-8e87c032"]]),ua=s=>(E("data-v-6bee1efd"),s=s(),F(),s),da=["aria-expanded"],va=ua(()=>u("span",{class:"container"},[u("span",{class:"top"}),u("span",{class:"middle"}),u("span",{class:"bottom"})],-1)),ha=[va],pa=g({__name:"VPNavBarHamburger",props:{active:{type:Boolean}},emits:["click"],setup(s){return(e,t)=>(a(),l("button",{type:"button",class:T(["VPNavBarHamburger",{active:e.active}]),"aria-label":"mobile navigation","aria-expanded":e.active,"aria-controls":"VPNavScreen",onClick:t[0]||(t[0]=n=>e.$emit("click"))},ha,10,da))}}),_a=m(pa,[["__scopeId","data-v-6bee1efd"]]),fa=["innerHTML"],ma=g({__name:"VPNavBarMenuLink",props:{item:{}},setup(s){const{page:e}=P();return(t,n)=>(a(),k(z,{class:T({VPNavBarMenuLink:!0,active:r(U)(r(e).relativePath,t.item.activeMatch||t.item.link,!!t.item.activeMatch)}),href:t.item.link,target:t.item.target,rel:t.item.rel,tabindex:"0"},{default:p(()=>[u("span",{innerHTML:t.item.text},null,8,fa)]),_:1},8,["class","href","target","rel"]))}}),ga=m(ma,[["__scopeId","data-v-cb318fec"]]),ka=g({__name:"VPNavBarMenuGroup",props:{item:{}},setup(s){const e=s,{page:t}=P(),n=i=>"link"in i?U(t.value.relativePath,i.link,!!e.item.activeMatch):i.items.some(n),o=$(()=>n(e.item));return(i,c)=>(a(),k(ke,{class:T({VPNavBarMenuGroup:!0,active:r(U)(r(t).relativePath,i.item.activeMatch,!!i.item.activeMatch)||o.value}),button:i.item.text,items:i.item.items},null,8,["class","button","items"]))}}),$a=s=>(E("data-v-f732b5d0"),s=s(),F(),s),ba={key:0,"aria-labelledby":"main-nav-aria-label",class:"VPNavBarMenu"},ya=$a(()=>u("span",{id:"main-nav-aria-label",class:"visually-hidden"},"Main Navigation",-1)),wa=g({__name:"VPNavBarMenu",setup(s){const{theme:e}=P();return(t,n)=>r(e).nav?(a(),l("nav",ba,[ya,(a(!0),l(M,null,A(r(e).nav,o=>(a(),l(M,{key:o.text},["link"in o?(a(),k(ga,{key:0,item:o},null,8,["item"])):(a(),k(ka,{key:1,item:o},null,8,["item"]))],64))),128))])):f("",!0)}}),Pa=m(wa,[["__scopeId","data-v-f732b5d0"]]);function Va(s){const{localeIndex:e,theme:t}=P();function n(o){var N,B,S;const i=o.split("."),c=(N=t.value.search)==null?void 0:N.options,h=c&&typeof c=="object",v=h&&((S=(B=c.locales)==null?void 0:B[e.value])==null?void 0:S.translations)||null,b=h&&c.translations||null;let y=v,w=b,C=s;const I=i.pop();for(const Y of i){let O=null;const q=C==null?void 0:C[Y];q&&(O=C=q);const oe=w==null?void 0:w[Y];oe&&(O=w=oe);const ae=y==null?void 0:y[Y];ae&&(O=y=ae),q||(C=O),oe||(w=O),ae||(y=O)}return(y==null?void 0:y[I])??(w==null?void 0:w[I])??(C==null?void 0:C[I])??""}return n}const La=["aria-label"],Sa={class:"DocSearch-Button-Container"},Ma=u("svg",{class:"DocSearch-Search-Icon",width:"20",height:"20",viewBox:"0 0 20 20","aria-label":"search icon"},[u("path",{d:"M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z",stroke:"currentColor",fill:"none","fill-rule":"evenodd","stroke-linecap":"round","stroke-linejoin":"round"})],-1),Ca={class:"DocSearch-Button-Placeholder"},Ia=u("span",{class:"DocSearch-Button-Keys"},[u("kbd",{class:"DocSearch-Button-Key"}),u("kbd",{class:"DocSearch-Button-Key"},"K")],-1),be=g({__name:"VPNavBarSearchButton",setup(s){const t=Va({button:{buttonText:"Search",buttonAriaLabel:"Search"}});return(n,o)=>(a(),l("button",{type:"button",class:"DocSearch DocSearch-Button","aria-label":r(t)("button.buttonAriaLabel")},[u("span",Sa,[Ma,u("span",Ca,V(r(t)("button.buttonText")),1)]),Ia],8,La))}}),Ta={class:"VPNavBarSearch"},Na={id:"local-search"},Ba={key:1,id:"docsearch"},Aa=g({__name:"VPNavBarSearch",setup(s){const e=()=>null,t=()=>null,{theme:n}=P(),o=L(!1),i=L(!1);G(()=>{});function c(){o.value||(o.value=!0,setTimeout(h,16))}function h(){const y=new Event("keydown");y.key="k",y.metaKey=!0,window.dispatchEvent(y),setTimeout(()=>{document.querySelector(".DocSearch-Modal")||h()},16)}const v=L(!1),b="";return(y,w)=>{var C;return a(),l("div",Ta,[r(b)==="local"?(a(),l(M,{key:0},[v.value?(a(),k(r(e),{key:0,onClose:w[0]||(w[0]=I=>v.value=!1)})):f("",!0),u("div",Na,[_(be,{onClick:w[1]||(w[1]=I=>v.value=!0)})])],64)):r(b)==="algolia"?(a(),l(M,{key:1},[o.value?(a(),k(r(t),{key:0,algolia:((C=r(n).search)==null?void 0:C.options)??r(n).algolia,onVnodeBeforeMount:w[2]||(w[2]=I=>i.value=!0)},null,8,["algolia"])):f("",!0),i.value?f("",!0):(a(),l("div",Ba,[_(be,{onClick:c})]))],64)):f("",!0)])}}}),Ha=g({__name:"VPNavBarSocialLinks",setup(s){const{theme:e}=P();return(t,n)=>r(e).socialLinks?(a(),k($e,{key:0,class:"VPNavBarSocialLinks",links:r(e).socialLinks},null,8,["links"])):f("",!0)}}),za=m(Ha,[["__scopeId","data-v-ef6192dc"]]),Ea=["href","rel","target"],Fa=g({__name:"VPNavBarTitle",setup(s){const{site:e,theme:t}=P(),{hasSidebar:n}=D(),{currentLang:o}=W(),i=$(()=>{var v;return typeof t.value.logoLink=="string"?t.value.logoLink:(v=t.value.logoLink)==null?void 0:v.link}),c=$(()=>{var v;return typeof t.value.logoLink=="string"||(v=t.value.logoLink)==null?void 0:v.rel}),h=$(()=>{var v;return typeof t.value.logoLink=="string"||(v=t.value.logoLink)==null?void 0:v.target});return(v,b)=>(a(),l("div",{class:T(["VPNavBarTitle",{"has-sidebar":r(n)}])},[u("a",{class:"title",href:i.value??r(pe)(r(o).link),rel:c.value,target:h.value},[d(v.$slots,"nav-bar-title-before",{},void 0,!0),r(t).logo?(a(),k(Q,{key:0,class:"logo",image:r(t).logo},null,8,["image"])):f("",!0),r(t).siteTitle?(a(),l(M,{key:1},[H(V(r(t).siteTitle),1)],64)):r(t).siteTitle===void 0?(a(),l(M,{key:2},[H(V(r(e).title),1)],64)):f("",!0),d(v.$slots,"nav-bar-title-after",{},void 0,!0)],8,Ea)],2))}}),Da=m(Fa,[["__scopeId","data-v-e4cade88"]]),Oa={},xa={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},Ua=u("path",{d:"M0 0h24v24H0z",fill:"none"},null,-1),Ga=u("path",{d:" M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z ",class:"css-c4d79v"},null,-1),ja=[Ua,Ga];function Ra(s,e){return a(),l("svg",xa,ja)}const Ee=m(Oa,[["render",Ra]]),qa={class:"items"},Ka={class:"title"},Wa=g({__name:"VPNavBarTranslations",setup(s){const{theme:e}=P(),{localeLinks:t,currentLang:n}=W({correspondingLink:!0});return(o,i)=>r(t).length&&r(n).label?(a(),k(ke,{key:0,class:"VPNavBarTranslations",icon:Ee,label:r(e).langMenuLabel||"Change language"},{default:p(()=>[u("div",qa,[u("p",Ka,V(r(n).label),1),(a(!0),l(M,null,A(r(t),c=>(a(),k(ne,{key:c.link,item:c},null,8,["item"]))),128))])]),_:1},8,["label"])):f("",!0)}}),Ya=m(Wa,[["__scopeId","data-v-ff4524ae"]]),Ja=s=>(E("data-v-3efcd581"),s=s(),F(),s),Za={class:"wrapper"},Qa={class:"container"},Xa={class:"title"},er={class:"content"},tr={class:"content-body"},sr=Ja(()=>u("div",{class:"divider"},[u("div",{class:"divider-line"})],-1)),nr=g({__name:"VPNavBar",props:{isScreenOpen:{type:Boolean}},emits:["toggle-screen"],setup(s){const{y:e}=Le(),{hasSidebar:t}=D(),{hasLocalNav:n}=Ne(),{frontmatter:o}=P(),i=L({});return we(()=>{i.value={"has-sidebar":t.value,"has-local-nav":n.value,top:o.value.layout==="home"&&e.value===0}}),(c,h)=>(a(),l("div",{class:T(["VPNavBar",i.value])},[u("div",Za,[u("div",Qa,[u("div",Xa,[_(Da,null,{"nav-bar-title-before":p(()=>[d(c.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":p(()=>[d(c.$slots,"nav-bar-title-after",{},void 0,!0)]),_:3})]),u("div",er,[u("div",tr,[d(c.$slots,"nav-bar-content-before",{},void 0,!0),_(Aa,{class:"search"}),_(Pa,{class:"menu"}),_(Ya,{class:"translations"}),_(fo,{class:"appearance"}),_(za,{class:"social-links"}),_(ca,{class:"extra"}),d(c.$slots,"nav-bar-content-after",{},void 0,!0),_(_a,{class:"hamburger",active:c.isScreenOpen,onClick:h[0]||(h[0]=v=>c.$emit("toggle-screen"))},null,8,["active"])])])])]),sr],2))}}),or=m(nr,[["__scopeId","data-v-3efcd581"]]),ar={key:0,class:"VPNavScreenAppearance"},rr={class:"text"},ir=g({__name:"VPNavScreenAppearance",setup(s){const{site:e,theme:t}=P();return(n,o)=>r(e).appearance&&r(e).appearance!=="force-dark"?(a(),l("div",ar,[u("p",rr,V(r(t).darkModeSwitchLabel||"Appearance"),1),_(me)])):f("",!0)}}),lr=m(ir,[["__scopeId","data-v-338d9b48"]]),cr=g({__name:"VPNavScreenMenuLink",props:{item:{}},setup(s){const e=se("close-screen");return(t,n)=>(a(),k(z,{class:"VPNavScreenMenuLink",href:t.item.link,target:t.item.target,rel:t.item.rel,onClick:r(e)},{default:p(()=>[H(V(t.item.text),1)]),_:1},8,["href","target","rel","onClick"]))}}),ur=m(cr,[["__scopeId","data-v-fe523e3d"]]),dr={},vr={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},hr=u("path",{d:"M18.9,10.9h-6v-6c0-0.6-0.4-1-1-1s-1,0.4-1,1v6h-6c-0.6,0-1,0.4-1,1s0.4,1,1,1h6v6c0,0.6,0.4,1,1,1s1-0.4,1-1v-6h6c0.6,0,1-0.4,1-1S19.5,10.9,18.9,10.9z"},null,-1),pr=[hr];function _r(s,e){return a(),l("svg",vr,pr)}const fr=m(dr,[["render",_r]]),mr=g({__name:"VPNavScreenMenuGroupLink",props:{item:{}},setup(s){const e=se("close-screen");return(t,n)=>(a(),k(z,{class:"VPNavScreenMenuGroupLink",href:t.item.link,target:t.item.target,rel:t.item.rel,onClick:r(e)},{default:p(()=>[H(V(t.item.text),1)]),_:1},8,["href","target","rel","onClick"]))}}),Fe=m(mr,[["__scopeId","data-v-aea78dd1"]]),gr={class:"VPNavScreenMenuGroupSection"},kr={key:0,class:"title"},$r=g({__name:"VPNavScreenMenuGroupSection",props:{text:{},items:{}},setup(s){return(e,t)=>(a(),l("div",gr,[e.text?(a(),l("p",kr,V(e.text),1)):f("",!0),(a(!0),l(M,null,A(e.items,n=>(a(),k(Fe,{key:n.text,item:n},null,8,["item"]))),128))]))}}),br=m($r,[["__scopeId","data-v-f60dbfa7"]]),yr=["aria-controls","aria-expanded"],wr=["innerHTML"],Pr=["id"],Vr={key:1,class:"group"},Lr=g({__name:"VPNavScreenMenuGroup",props:{text:{},items:{}},setup(s){const e=s,t=L(!1),n=$(()=>`NavScreenGroup-${e.text.replace(" ","-").toLowerCase()}`);function o(){t.value=!t.value}return(i,c)=>(a(),l("div",{class:T(["VPNavScreenMenuGroup",{open:t.value}])},[u("button",{class:"button","aria-controls":n.value,"aria-expanded":t.value,onClick:o},[u("span",{class:"button-text",innerHTML:i.text},null,8,wr),_(fr,{class:"button-icon"})],8,yr),u("div",{id:n.value,class:"items"},[(a(!0),l(M,null,A(i.items,h=>(a(),l(M,{key:h.text},["link"in h?(a(),l("div",{key:h.text,class:"item"},[_(Fe,{item:h},null,8,["item"])])):(a(),l("div",Vr,[_(br,{text:h.text,items:h.items},null,8,["text","items"])]))],64))),128))],8,Pr)],2))}}),Sr=m(Lr,[["__scopeId","data-v-32e4a89c"]]),Mr={key:0,class:"VPNavScreenMenu"},Cr=g({__name:"VPNavScreenMenu",setup(s){const{theme:e}=P();return(t,n)=>r(e).nav?(a(),l("nav",Mr,[(a(!0),l(M,null,A(r(e).nav,o=>(a(),l(M,{key:o.text},["link"in o?(a(),k(ur,{key:0,item:o},null,8,["item"])):(a(),k(Sr,{key:1,text:o.text||"",items:o.items},null,8,["text","items"]))],64))),128))])):f("",!0)}}),Ir=g({__name:"VPNavScreenSocialLinks",setup(s){const{theme:e}=P();return(t,n)=>r(e).socialLinks?(a(),k($e,{key:0,class:"VPNavScreenSocialLinks",links:r(e).socialLinks},null,8,["links"])):f("",!0)}}),Tr={class:"list"},Nr=g({__name:"VPNavScreenTranslations",setup(s){const{localeLinks:e,currentLang:t}=W({correspondingLink:!0}),n=L(!1);function o(){n.value=!n.value}return(i,c)=>r(e).length&&r(t).label?(a(),l("div",{key:0,class:T(["VPNavScreenTranslations",{open:n.value}])},[u("button",{class:"title",onClick:o},[_(Ee,{class:"icon lang"}),H(" "+V(r(t).label)+" ",1),_(ze,{class:"icon chevron"})]),u("ul",Tr,[(a(!0),l(M,null,A(r(e),h=>(a(),l("li",{key:h.link,class:"item"},[_(z,{class:"link",href:h.link},{default:p(()=>[H(V(h.text),1)]),_:2},1032,["href"])]))),128))])],2)):f("",!0)}}),Br=m(Nr,[["__scopeId","data-v-41505286"]]),Ar={class:"container"},Hr=g({__name:"VPNavScreen",props:{open:{type:Boolean}},setup(s){const e=L(null),t=Se(K?document.body:null);return(n,o)=>(a(),k(de,{name:"fade",onEnter:o[0]||(o[0]=i=>t.value=!0),onAfterLeave:o[1]||(o[1]=i=>t.value=!1)},{default:p(()=>[n.open?(a(),l("div",{key:0,class:"VPNavScreen",ref_key:"screen",ref:e,id:"VPNavScreen"},[u("div",Ar,[d(n.$slots,"nav-screen-content-before",{},void 0,!0),_(Cr,{class:"menu"}),_(Br,{class:"translations"}),_(lr,{class:"appearance"}),_(Ir,{class:"social-links"}),d(n.$slots,"nav-screen-content-after",{},void 0,!0)])],512)):f("",!0)]),_:3}))}}),zr=m(Hr,[["__scopeId","data-v-57cce842"]]),Er={key:0,class:"VPNav"},Fr=g({__name:"VPNav",setup(s){const{isScreenOpen:e,closeScreen:t,toggleScreen:n}=Kn(),{frontmatter:o}=P(),i=$(()=>o.value.navbar!==!1);return Me("close-screen",t),X(()=>{K&&document.documentElement.classList.toggle("hide-nav",!i.value)}),(c,h)=>i.value?(a(),l("header",Er,[_(or,{"is-screen-open":r(e),onToggleScreen:r(n)},{"nav-bar-title-before":p(()=>[d(c.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":p(()=>[d(c.$slots,"nav-bar-title-after",{},void 0,!0)]),"nav-bar-content-before":p(()=>[d(c.$slots,"nav-bar-content-before",{},void 0,!0)]),"nav-bar-content-after":p(()=>[d(c.$slots,"nav-bar-content-after",{},void 0,!0)]),_:3},8,["is-screen-open","onToggleScreen"]),_(zr,{open:r(e)},{"nav-screen-content-before":p(()=>[d(c.$slots,"nav-screen-content-before",{},void 0,!0)]),"nav-screen-content-after":p(()=>[d(c.$slots,"nav-screen-content-after",{},void 0,!0)]),_:3},8,["open"])])):f("",!0)}}),Dr=m(Fr,[["__scopeId","data-v-7ad780c2"]]),Or=s=>(E("data-v-bd01e0d5"),s=s(),F(),s),xr=["role","tabindex"],Ur=Or(()=>u("div",{class:"indicator"},null,-1)),Gr={key:1,class:"items"},jr=g({__name:"VPSidebarItem",props:{item:{},depth:{}},setup(s){const e=s,{collapsed:t,collapsible:n,isLink:o,isActiveLink:i,hasActiveLink:c,hasChildren:h,toggle:v}=$t($(()=>e.item)),b=$(()=>h.value?"section":"div"),y=$(()=>o.value?"a":"div"),w=$(()=>h.value?e.depth+2===7?"p":`h${e.depth+2}`:"p"),C=$(()=>o.value?void 0:"button"),I=$(()=>[[`level-${e.depth}`],{collapsible:n.value},{collapsed:t.value},{"is-link":o.value},{"is-active":i.value},{"has-active":c.value}]);function N(S){"key"in S&&S.key!=="Enter"||!e.item.link&&v()}function B(){e.item.link&&v()}return(S,Y)=>{const O=R("VPSidebarItem",!0);return a(),k(x(b.value),{class:T(["VPSidebarItem",I.value])},{default:p(()=>[S.item.text?(a(),l("div",J({key:0,class:"item",role:C.value},Qe(S.item.items?{click:N,keydown:N}:{},!0),{tabindex:S.item.items&&0}),[Ur,S.item.link?(a(),k(z,{key:0,tag:y.value,class:"link",href:S.item.link,rel:S.item.rel,target:S.item.target},{default:p(()=>[(a(),k(x(w.value),{class:"text",innerHTML:S.item.text},null,8,["innerHTML"]))]),_:1},8,["tag","href","rel","target"])):(a(),k(x(w.value),{key:1,class:"text",innerHTML:S.item.text},null,8,["innerHTML"])),S.item.collapsed!=null?(a(),l("div",{key:2,class:"caret",role:"button","aria-label":"toggle section",onClick:B,onKeydown:Ze(B,["enter"]),tabindex:"0"},[_(Be,{class:"caret-icon"})],32)):f("",!0)],16,xr)):f("",!0),S.item.items&&S.item.items.length?(a(),l("div",Gr,[S.depth<5?(a(!0),l(M,{key:0},A(S.item.items,q=>(a(),k(O,{key:q.text,item:q,depth:S.depth+1},null,8,["item","depth"]))),128)):f("",!0)])):f("",!0)]),_:1},8,["class"])}}}),Rr=m(jr,[["__scopeId","data-v-bd01e0d5"]]),De=s=>(E("data-v-4871f9f5"),s=s(),F(),s),qr=De(()=>u("div",{class:"curtain"},null,-1)),Kr={class:"nav",id:"VPSidebarNav","aria-labelledby":"sidebar-aria-label",tabindex:"-1"},Wr=De(()=>u("span",{class:"visually-hidden",id:"sidebar-aria-label"}," Sidebar Navigation ",-1)),Yr=g({__name:"VPSidebar",props:{open:{type:Boolean}},setup(s){const{sidebarGroups:e,hasSidebar:t}=D(),n=s,o=L(null),i=Se(K?document.body:null);return j([n,o],()=>{var c;n.open?(i.value=!0,(c=o.value)==null||c.focus()):i.value=!1},{immediate:!0,flush:"post"}),(c,h)=>r(t)?(a(),l("aside",{key:0,class:T(["VPSidebar",{open:c.open}]),ref_key:"navEl",ref:o,onClick:h[0]||(h[0]=Xe(()=>{},["stop"]))},[qr,u("nav",Kr,[Wr,d(c.$slots,"sidebar-nav-before",{},void 0,!0),(a(!0),l(M,null,A(r(e),v=>(a(),l("div",{key:v.text,class:"group"},[_(Rr,{item:v,depth:0},null,8,["item"])]))),128)),d(c.$slots,"sidebar-nav-after",{},void 0,!0)])],2)):f("",!0)}}),Jr=m(Yr,[["__scopeId","data-v-4871f9f5"]]),Zr=g({__name:"VPSkipLink",setup(s){const e=te(),t=L();j(()=>e.path,()=>t.value.focus());function n({target:o}){const i=document.getElementById(decodeURIComponent(o.hash).slice(1));if(i){const c=()=>{i.removeAttribute("tabindex"),i.removeEventListener("blur",c)};i.setAttribute("tabindex","-1"),i.addEventListener("blur",c),i.focus(),window.scrollTo(0,0)}}return(o,i)=>(a(),l(M,null,[u("span",{ref_key:"backToTop",ref:t,tabindex:"-1"},null,512),u("a",{href:"#VPContent",class:"VPSkipLink visually-hidden",onClick:n}," Skip to content ")],64))}}),Qr=m(Zr,[["__scopeId","data-v-c8291ffa"]]),Xr=g({__name:"Layout",setup(s){const{isOpen:e,open:t,close:n}=D(),o=te();j(()=>o.path,n),kt(e,n);const{frontmatter:i}=P(),c=et(),h=$(()=>!!c["home-hero-image"]);return Me("hero-image-slot-exists",h),(v,b)=>{const y=R("Content");return r(i).layout!==!1?(a(),l("div",{key:0,class:T(["Layout",r(i).pageClass])},[d(v.$slots,"layout-top",{},void 0,!0),_(Qr),_(ot,{class:"backdrop",show:r(e),onClick:r(n)},null,8,["show","onClick"]),_(Dr,null,{"nav-bar-title-before":p(()=>[d(v.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":p(()=>[d(v.$slots,"nav-bar-title-after",{},void 0,!0)]),"nav-bar-content-before":p(()=>[d(v.$slots,"nav-bar-content-before",{},void 0,!0)]),"nav-bar-content-after":p(()=>[d(v.$slots,"nav-bar-content-after",{},void 0,!0)]),"nav-screen-content-before":p(()=>[d(v.$slots,"nav-screen-content-before",{},void 0,!0)]),"nav-screen-content-after":p(()=>[d(v.$slots,"nav-screen-content-after",{},void 0,!0)]),_:3}),_(qn,{open:r(e),onOpenMenu:r(t)},null,8,["open","onOpenMenu"]),_(Jr,{open:r(e)},{"sidebar-nav-before":p(()=>[d(v.$slots,"sidebar-nav-before",{},void 0,!0)]),"sidebar-nav-after":p(()=>[d(v.$slots,"sidebar-nav-after",{},void 0,!0)]),_:3},8,["open"]),_(gn,null,{"page-top":p(()=>[d(v.$slots,"page-top",{},void 0,!0)]),"page-bottom":p(()=>[d(v.$slots,"page-bottom",{},void 0,!0)]),"not-found":p(()=>[d(v.$slots,"not-found",{},void 0,!0)]),"home-hero-before":p(()=>[d(v.$slots,"home-hero-before",{},void 0,!0)]),"home-hero-info":p(()=>[d(v.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-image":p(()=>[d(v.$slots,"home-hero-image",{},void 0,!0)]),"home-hero-after":p(()=>[d(v.$slots,"home-hero-after",{},void 0,!0)]),"home-features-before":p(()=>[d(v.$slots,"home-features-before",{},void 0,!0)]),"home-features-after":p(()=>[d(v.$slots,"home-features-after",{},void 0,!0)]),"doc-footer-before":p(()=>[d(v.$slots,"doc-footer-before",{},void 0,!0)]),"doc-before":p(()=>[d(v.$slots,"doc-before",{},void 0,!0)]),"doc-after":p(()=>[d(v.$slots,"doc-after",{},void 0,!0)]),"doc-top":p(()=>[d(v.$slots,"doc-top",{},void 0,!0)]),"doc-bottom":p(()=>[d(v.$slots,"doc-bottom",{},void 0,!0)]),"aside-top":p(()=>[d(v.$slots,"aside-top",{},void 0,!0)]),"aside-bottom":p(()=>[d(v.$slots,"aside-bottom",{},void 0,!0)]),"aside-outline-before":p(()=>[d(v.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":p(()=>[d(v.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":p(()=>[d(v.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":p(()=>[d(v.$slots,"aside-ads-after",{},void 0,!0)]),_:3}),_(wn),d(v.$slots,"layout-bottom",{},void 0,!0)],2)):(a(),k(y,{key:1}))}}}),ei=m(Xr,[["__scopeId","data-v-9d8abc1e"]]),ye={Layout:ei,enhanceApp:({app:s})=>{s.component("Badge",tt)}},si={...ye,enhanceApp(s){ye.enhanceApp(s)}};export{si as R}; diff --git a/assets/cmake_modules_DOCUMENTATION.md.PBiJbB4I.js b/assets/cmake_modules_DOCUMENTATION.md.PBiJbB4I.js new file mode 100644 index 00000000000..878d22c3d27 --- /dev/null +++ b/assets/cmake_modules_DOCUMENTATION.md.PBiJbB4I.js @@ -0,0 +1,52 @@ +import{_ as e,c as a,o as i,V as t}from"./chunks/framework.gBlNPWt_.js";const p=JSON.parse('{"title":"CMake files structure and usage","description":"","frontmatter":{},"headers":[],"relativePath":"cmake_modules/DOCUMENTATION.md","filePath":"cmake_modules/DOCUMENTATION.md","lastUpdated":1721825996000}'),n={name:"cmake_modules/DOCUMENTATION.md"},o=t(`

Directory structure of CMake files

- /

: - CMakeLists.txt - Root CMake file - version.cmake - common cmake file where version variables are set - build-config.h.cmake - cmake generation template for build-config.h

\\- cmake\\_modules/ - Directory storing modules and configurations for CMake
+
+:   -   FindXXXXX.cmake - CMake find files used to locate libraries,
+        headers, and binaries
+    -   commonSetup.cmake - common configuration settings for the
+        entire project (contains configure time options)
+    -   docMacros.cmake - common documentation macros used for
+        generating fop and pdf files
+    -   optionDefaults.cmake - contains common variables for the
+        platform build
+    -   distrocheck.sh - script that determines if the OS uses DEB
+        or RPM
+    -   getpackagerevisionarch.sh - script that returns OS version
+        and arch in format used for packaging
+
+    \\- dependencies/ - Directory storing dependency files used for package dependencies
+
+    :   -   \\<OS\\>.cmake - File containing either DEB or RPM
+            dependencies for the given OS
+
+\\- build-utils/ - Directory for build related utilities
+
+:   -   cleanDeb.sh - script that unpacks a deb file and rebuilds
+        with fakeroot to clean up lintain errors/warnings
+

Common Macros

  • MACRO_ENSURE_OUT_OF_SOURCE_BUILD - prevents building from with in source tree
  • HPCC_ADD_EXECUTABLE - simple wrapper around add_executable
  • HPCC_ADD_LIBRARY - simple wrapper around add_library
  • PARSE_ARGUMENTS - macro that can be used by other macros and functions for arg list parsing
  • HPCC_ADD_SUBDIRECTORY - argument controlled add subdirectory wrapper
  • HPCC_ADD_SUBDIRECTORY(test t1 t2 t3) - Will add the subdirectory test if t1,t2, or t3 are set to any value other then False/OFF/0
  • LOG_PLUGIN - used to log any code based plugin for the platform
  • ADD_PLUGIN - adds a plugin with optional build dependencies to the build if dependencies are found

Documentation Macros

  • RUN_XSLTPROC - Runs xsltproc using given args
  • RUN_FOP - Runs fop using given args
  • CLEAN_REL_BOOK - Uses a custom xsl and xsltproc to clean relative book paths from given xml file
  • CLEAN_REL_SET - Uses a custom xsl and xsltproc to clean relative set paths from given xml file
  • DOCBOOK_TO_PDF - Master macro used to generate pdf, uses above macros

Initfiles macro

  • GENERATE_BASH - used to run processor program on given files to replace ###<REPLACE>### with given variables FindXXXXX.cmake

Some standard techniques used in Cmake project files

Common looping

Use FOREACH:

FOREACH( oITEMS
+  item1
+  item2
+  item3
+  item4
+  item5
+)
+  Actions on each item here.
+ENDFOREACH ( oITEMS )
+

Common installs over just install

  • install ( FILES ... ) - installs item with 664 permissions
  • install ( PROGRAMS ... ) - installs runable item with 755 permissions
  • install ( TARGETS ... ) - installs built target with 755 permissions
  • install ( DIRECTORY ... ) - installs directory with 777 permissions

Common settings for generated source files

  • set_source_files_properties(<file> PROPERTIES GENERATED TRUE) - Must be set on generated source files or dependency generation fails and increases build time.

Using custom commands between multiple cmake files

  • GET_TARGET_PROPERTY(<VAR from other cmake file> <var for this file> LOCATION)
  • GET_TARGET_PROPERTY(ESDL_EXE esdl LOCATION) - will get from the top level cache the ESDL_EXE value and set it in esdl for your current cmake file

USE add_custom_command only when 100% needed.

All directories in a cmake project should have a CMakeLists.txt file and be called from the upper level project with an add_subdirectory or HPCC_ADD_SUBDIRECTORY

When you have a property that will be shared between cmake projects use define_property to set it in the top level cache.

  • define_property(GLOBAL PROPERTY TEST_TARGET BRIEF_DOCS "test doc" FULL_DOCS "Full test doc")
  • mark_as_advanced(TEST_TARGET) - this is required to force the property into the top level cache.CMake Layout:

FindXXXXX.cmake format

All of our Find scripts use the following format:

NOT XXXXX_FOUND
+  Externals set
+    define needed vars for finding external based libraries/headers
+
+    Use Native set
+      use FIND_PATH to locate headers
+      use FIND_LIBRARY to find libs
+
+Include Cmake macros file for package handling
+define package handling args for find return  (This will set XXXXX_FOUND)
+
+XXXXX_FOUND
+  perform any modifications you feel is needed for the find
+
+Mark defined variables used in package handling args as advanced for return
+

Will define when done:

XXXXX_FOUND
+XXXXX_INCLUDE_DIR
+XXXXX_LIBRARIES
+

(more can be defined, but must be at min the previous unless looking for only a binary)

For an example, see FindAPR.cmake

`,32),r=[o];function s(l,c,d,m,u,f){return i(),a("div",null,r)}const _=e(n,[["render",s]]);export{p as __pageData,_ as default}; diff --git a/assets/cmake_modules_DOCUMENTATION.md.PBiJbB4I.lean.js b/assets/cmake_modules_DOCUMENTATION.md.PBiJbB4I.lean.js new file mode 100644 index 00000000000..2ff90b16336 --- /dev/null +++ b/assets/cmake_modules_DOCUMENTATION.md.PBiJbB4I.lean.js @@ -0,0 +1 @@ +import{_ as e,c as a,o as i,V as t}from"./chunks/framework.gBlNPWt_.js";const p=JSON.parse('{"title":"CMake files structure and usage","description":"","frontmatter":{},"headers":[],"relativePath":"cmake_modules/DOCUMENTATION.md","filePath":"cmake_modules/DOCUMENTATION.md","lastUpdated":1721825996000}'),n={name:"cmake_modules/DOCUMENTATION.md"},o=t("",32),r=[o];function s(l,c,d,m,u,f){return i(),a("div",null,r)}const _=e(n,[["render",s]]);export{p as __pageData,_ as default}; diff --git a/assets/devdoc_CodeGenerator.md.Sr_00DEY.js b/assets/devdoc_CodeGenerator.md.Sr_00DEY.js new file mode 100644 index 00000000000..8d70e2a078e --- /dev/null +++ b/assets/devdoc_CodeGenerator.md.Sr_00DEY.js @@ -0,0 +1,13 @@ +import{_ as e,c as t,o as a,V as i}from"./chunks/framework.gBlNPWt_.js";const m=JSON.parse('{"title":"Eclcc/Code generator","description":"","frontmatter":{"title":"Eclcc/Code generator"},"headers":[],"relativePath":"devdoc/CodeGenerator.md","filePath":"devdoc/CodeGenerator.md","lastUpdated":1721825996000}'),o={name:"devdoc/CodeGenerator.md"},r=i(`

Introduction

Purpose

The primary purpose of the code generator is to take an ECL query and convert it into a work unit that is suitable for running by one of the engines.

Aims

The code generator has to do its job accurately. If the code generator does not correctly map the ECL to the workunit it can lead to corrupt data and invalid results. Problems like that can often be very hard and frustrating for the ECL users to track down.

There is also a strong emphasis on generating output that is as good as possible. Eclcc contains many different optimization stages, and is extensible to allow others to be easily added.

Eclcc needs to be able to cope with reasonably large jobs. Queries that contain several megabytes of ECL, and generate tens of thousands of activities, and 10s of Mb of C++ are routine. These queries need to be processed relatively quickly.

Key ideas

Nearly all the processing of ECL is done using an expression graph. The representation of the expression graph has some particular characteristics:

  • Once the nodes in the expression graph have been created they are NEVER modified.
  • Nodes in the expression graph are ALWAYS commoned up if they are identical.
  • Each node in the expression graph is link counted (see below) to track its lifetime.
  • If a modified graph is required a new graph is created (sharing nodes from the old one)

The ECL language is a declarative language, and in general is assumed to be pure - i.e. there are no side-effects, expressions can be evaluated lazily and re-evaluating an expression causes no problems. This allows eclcc to transform the graph in lots of interesting ways. (Life is never that simple so there are mechanisms for handling the exceptions.)

From declarative to imperative

One of the main challenges with eclcc is converting the declarative ECL code into imperative C++ code. One key problem is it needs to try to ensure that code is only evaluated when it is required, but that it is also only evaluated once. It isn't always possible to satisfy both constraints - for example a global dataset expression used within a child query. Should it be evaluated once before the activity containing the child query is called, or each time the child query is called? If it is called on demand then it may not be evaluated as efficiently...

This issue complicates many of the optimizations and transformations that are done to the queries. Long term the plan is to allow the engines to support more delayed lazy-evaluation, so that whether something is evaluated is more dynamic rather than static.

Flow of processing

The idealised view of the processing within eclcc follows the following stages:

  • Parse the ECL into an expression graph.
  • Expand out function calls.
  • Normalize the expression graph so it is in a consistent format.
  • Normalize the references to fields within datasets to tie them up with their scopes.
  • Apply various global optimizations.
  • Translate logical operations into the activities that will implement them.
  • Resource and generate the global graph
  • For each activity, resource, optimize and generate its child graphs.

In practice the progression is not so clear cut. There tends to be some overlap between the different stages, and some of them may occur in slightly different orders. However the order broadly holds.

Working on the code generator

The regression suite

Before any change is accepted for the code generator it is always run against several regression suites to ensure that it doesn't introduce any problems, and that the change has the desired effect. There are several different regression suites:

  • testing/regress/ecl - The run time regression suite.
  • ecl/regress - a compiler regression suite. This contains tests that cannot run and error tests.
  • LN private suite - This contains a large selection (>10Gb) of archived queries. The contain proprietary code so unfortunately cannot be released as open source.

The ecl/regress directory contains a script 'regress.sh' that is used for running the regression tests. It should be executed in the directory containing the ecl files. The script generates the c++ code (and workunits) for each of the source files to a target directory, and then executes a comparison program to compare the new results with a previous "golden" reference set.

Before making any changes to the compiler, a reference set should be created by running the regression script and copying the generated files to the reference directory.

Here is a sample command line

~/dev/hpcc/ecl/regress/regress.sh -t /regress/hpcc -e /home/<user>/buildr/Release/bin/eclcc -I /home/<user>/dev/hpcc/ecl/regress/modules -I /home/<user>/dev/hpcc/plugins/javaembed -I /home/<user>/dev/hpcc/plugins/v8embed -c /regress/hpcc.master -d bcompare

(A version of this command resides in a shell script in each of my regression suite directories, with the -t and -c options adapted for each suite.)

For a full list of options execute the script with no parameters, or take a look at the script itself. A couple of useful options are:

  • The script can be run on a single file by using the -q option.
  • The (-e) option selects the path of the eclcc. This is particularly useful when running from the build directory (see below), or using multiple build directories to compare behaviour between different versions.

We strongly recommend using a comparison program which allows rules to be defined to ignore certain differences (e.g., beyond compare).

Running directly from the build directory

It is much quicker to run eclcc directly from the build directory, rather than deploying a system and running eclcc from there. To do this you need to configure some options that eclcc requires, e.g. where the include files are found. The options can be set by either setting environment variables or by specifiying options in an eclcc.ini file. The following are the names of the different options:


Environment flag Ini file option


CL_PATH compilerPath

ECLCC_LIBRARY_PATH libraryPath

ECLCC_INCLUDE_PATH includePath

ECLCC_PLUGIN_PATH plugins

HPCC_FILEHOOKS_PATH filehooks

ECLCC_TPL_PATH templatePath

ECLCC_ECLLIBRARY_PATH eclLibrariesPath

ECLCC_ECLBUNDLE_PATH eclBundlesPath

The eclcc.ini can either be a file in the local directory, or specified on the eclcc command line with -specs. Including the settings in a local eclcc.ini file also it easy to debug eclcc directly from the build directory within the eclipse environment.

Hints and tips

  • Logging

    There is an option for eclcc to output a logging file, and another to specify the level of detail in that logging file. If the detail level is above 500 then the expresssion tree for the query is output to the logging file after each of the code transformations. The tracing is very useful for tracking down at which stage inconsistencies are introduced in the expression graph, and also for learning how each transformation affects the query.

    The output format defaults to ECL - which is regenerated from the expression tree. (This ECL cannot generally be compiled without editing - partly because it contains extra annoations.) Use either of the following:

    eclcc myfile.ecl --logfile myfile.log --logdetail 999

    regress.sh -q myfile.ecl -l myfile.log

  • -ftraceIR

    There is a debug option (-ftraceIR) that generates an intermediate representation of the expression graph rather than regenerating ECL. The output tends to be less compact and harder to read quickly, but has the advantage of being better structured, and contains more details of the internal representation. ecl/hql/hqlir.cpp contains more details of the format.

  • Adding extra logging into the source code

    If you want to add tracing of expressions at any point in the code generation then adding either of the following calls will include the expression details in the log file:

    dbglogExpr(expr); // regenerate the ecl for an expression. See other functions in ecl/hql/hqlthql.hpp

    EclIR::dbglogIR(expr); // regenerate the IR for an expression. See other functions in ecl/hql/hqlir.hpp

  • Logging while debugging

    If you are debugging inside gdb it is often useful to be able to dump out details of an expression. Calling EclIR:dump_ir(expr); will generate the IR to stdout.

    p EclIR::dump_ir(expr)

    The function can also be used with multiple parameters. Each expression will be dumped out, but common child nodes will only be generated once. This can be very useful when trying to determine the difference between two expressions. The quickest way is to call EclIR::dump_ir(expr1, expr2). The first difference between the expressions will be the expression that follows the first "return".

  • Expression sequence ids.

    Sometimes it can be hard to determine where a particular IHqlExpression node was created. If that is the case, then defining DEBUG_TRACK_INSTANCEID (in ecl/hql/hqlexpr.ipp) will add a unique sequence number to each IHqlExpression that is created. There is also a function checkSeqId() at the start of ecl/hql/hqlexpr.cpp which is called whenever an expression is created, linked, released etc.. Setting a breakpoint in that function can allow you to trace back exactly when and why a particular node was created.

Expressions

Expression Graph representation

The key data structure within eclcc is the graph representation. The design has some key elements.

  • Once a node is created it is never modified.

    Some derived information (e.g., sort order, number of records, unique hash, ...) might be calculated and stored in the class after it has been created, but that doesn't change what the node represents in any way. Some nodes are created in stages - e.g., records, modules. These nodes are marked as fully completed when closeExpr() is called, after which they cannot be modified.

  • Nodes are always commoned up.

    If the same operator has the same arguments and type then there will be a unique IHqlExpression to represent it. This helps ensure that graphs stay as graphs and don't get converted to trees. It also helps with optimizations, and allows code duplicated in two different contexts to be brought together.

  • The nodes are link counted.

    Link counts are used to control the lifetime of the expression objects. Whenever a reference to an expression node is held, its link count is increased, and decreased when no longer required. The node is freed when there are no more references. (This generally works well, but does give us problems with forward references.)

  • The access to the graph is through interfaces.

    The main interfaces are IHqlExpression, IHqlDataset and IHqlScope. They are all defined in hqlexpr.hpp. The aim of the interfaces is to hide the implementation of the expression nodes so they can be restructured and changed without affecting any other code.

  • The expression classes use interfaces and a type field rather than polymorphism. This could be argued to be bad object design...but.

    There are more than 500 different possible operators. If a class was created for each of them the system would quickly become unwieldy. Instead there are several different classes which model the different types of expression (dataset/expression/scope).

    The interfaces contain everything needed to create and interrogate an expression tree, but they do not contain functionality for directly processing the graph.

    To avoid some of the shortcomings of type fields there are various mechanisms for accessing derived attributes which avoid interrogating the type field.

  • Memory consumption is critical.

It is not unusual to have 10M or even 100M nodes in memory as a query is being processed. At that scale the memory consumption of each node matters - so great care should be taken when considering increasing the size of the objects. The node classes contain a class hierarchy which is there purely to reduce the memory consumption - not to reflect the functionality. With no memory constraints they wouldn't be there, but removing a single pointer per node can save 1Gb of memory usage for very complex queries.

IHqlExpression

This is the interface that is used to walk and interrogate the expression graph once it has been created. Some of the main functions are: getOperator() What does this node represent? It returns a member of the node_operator enumerated type. numChildren() How many arguments does node have? queryChild(unsigned n) What is the nth child? If the argument is out of range it returns NULL. queryType() The type of this node. queryBody() Used to skip annotations (see below) queryProperty() Does this node have a child which is an attribute that matches a given name. (see below for more about attributes). queryValue() For a no_constant return the value of the constant. It returns NULL otherwise.

The nodes in the expression graph are created through factory functions. Some of the expression types have specialised functions - e.g., createDataset, createRow, createDictionary, but scalar expressions and actions are normally created with createValue().

Note: Generally ownership of the arguments to the createX() functions are assumed to be taken over by the newly created node.

The values of the enumeration constants in node_operator are used to calculate "crcs" which are used to check if the ECL for a query matches, and if disk and index record formats match. It contains quite a few legacy entries no_unusedXXX which can be used for new operators (otherwise new operators must be added to the end).

IHqlSimpleScope

This interface is implemented by records, and is used to map names to the fields within the records. If a record contains IFBLOCKs then each of the fields in the ifblock is defined in the IHqlSimpleScope for the containing record.

IHqlScope

Normally obtained by calling IHqlExpression::queryScope(). It is primarily used in the parser to resolve fields from within modules.

The ECL is parsed on demand so as the symbol is looked up it may cause a cascade of ECL to be compiled. The lookup context (HqlLookupContext ) is passed to IHqlScope::lookupSymbol() for several reasons:

  • It contains information about the active repository - the source of the ECL which will be dynamically parsed.
  • It contains caches of expanded functions - to avoid repeating expansion transforms.
  • Some members are used for tracking definitions that are read to build dependency graphs, or archives of submitted queries.

The interface IHqlScope currently has some members that are used for creation; this should be refactored and placed in a different interface.

IHqlDataset

This is normally obtained by calling IHqlExpression::queryDataset(). It has shrunk in size over time, and could quite possibly be folded into IHqlExpression with little pain.

There is a distinction in the code generator between "tables" and "datasets". A table (IHqlDataset::queryTable()) is a dataset operation that defines a new output record. Any operation that has a transform or record that defines an output record (e.g., PROJECT,TABLE) is a table, whilst those that don't (e.g., a filter, dedup) are not. There are a few apparent exceptions -e.g., IF (This is controlled by definesColumnList() which returns true the operator is a table.)

Properties and attributes

There are two related by slightly different concepts. An attribute refers to the explicit flags that are added to operators (e.g., , LOCAL, KEEP(n) etc. specified in the ECL or some internal attributes added by the code generator). There are a couple of different functions for creating attributes. createExtraAttribute() should be used by default. createAttribute() is reserved for an attribute that never has any arguments, or in unusual situations where it is important that the arguments are never transformed. They are tested using queryAttribute()/hasAttribute() and represented by nodes of kind no_attr/no_expr_attr.

The term "property" refers to computed information (e.g., record counts) that can be derived from the operator, its arguments and attributes. They are covered in more detail below.

Field references

Fields can be selected from active rows of a dataset in three main ways:

  • Some operators define LEFT/RIGHT to represent an input or processed dataset. Fields from these active rows are referenced with LEFT.<field-name>. Here LEFT or RIGHT is the "selector".

  • Other operators use the input dataset as the selector. E.g., myFile(myFile.id != 0). Here the input dataset is the "selector".

  • Often when the input dataset is used as the selector it can be omitted. E.g., myFile(id != 0). This is implicitly expanded by the PARSER to the second form. A reference to a field is always represented in the expression graph as a node of kind no_select (with createSelectExpr). The first child is the selector, and the second is the field. Needless to say there are some complications...

  • LEFT/RIGHT.

    The problem is that the different uses of LEFT/RIGHT need to be disambiguated since there may be several different uses of LEFT in a query. This is especially true when operations are executed in child queries. LEFT is represented by a node no_left(record, selSeq). Often the record is sufficient to disambiguate the uses, but there are situations where it isn't enough. So in addition no_left has a child which is a selSeq (selector sequence) which is added as a child attribute of the PROJECT or other operator. At parse time it is a function of the input dataset that is later normalized to a unique id to reduce the transformation work.

  • Active datasets. It is slightly more complicated - because the dataset used as the selector can be any upstream dataset up to the nearest table. So the following ECL code is legal:

    x := DATASET(...)
    +y := x(x.id != 0);
    +z := y(x.id != 100);
    +

Here the reference to x.id in the definition of z is referring to a field in the input dataset.

Because of these semantics the selector in a normalized tree is actually inputDataset->queryNormalizedSelector() rather than inputDatset. This function currently returns the table expression node (but it may change in the future see below).

Attribute "new"

In some situations ECL allows child datasets to be treated as a dataset without an explicit NORMALIZE. E.g., EXISTS(myDataset.ChildDataset);

This is primarily to enable efficient aggregates on disk files to be generated, but it adds some complications with an expression of the form dataset.childdataset.grandchild. E.g.,:

EXISTS(dataset(EXISTS(dataset.childdataset.grandchild))
+

Or:

EXISTS(dataset.childdataset(EXISTS(dataset.childdataset.grandchild))
+

In the first example dataset.childdataset within the dataset.childdataset.grandchild is a reference to a dataset that doesn't have an active cursor and needs to be iterated), whilst in the second it refers to an active cursor.

To differentiate between the two, all references to fields within datasets/rows that don't have active selectors have an additional attribute("new") as a child of the select. So a no_select with a "new" attribute requires the dataset to be created, one without is a member of an active dataset cursor.

If you have a nested row, the new attribute is added to the selection from the dataset, rather than the selection from the nested row. The functions queryDatasetCursor() and querySelectorDataset()) are used to help interpret the meaning.

(An alternative would be to use a different node from no_select - possibly this should be considered - it would be more space efficient.)

The expression graph generated by the ECL parser doesn't contain any new attributes. These are added as one of the first stages of normalizing the expression graph. Any code that works on normalized expressions needs to take care to interpret no_selects correctly.

Transforming selects

When an expression graph is transformed and none of the records are changed, the representation of LEFT/RIGHT remains the same. This means any no_select nodes in the expression tree will also stay the same.

However, if the transform modifies a table (highly likely) it means that the selector for the second form of field selector will also change. Unfortunately this means that transforms often cannot be short-circuited.

It could significantly reduce the extent of the graph that needs traversing, and the number of nodes replaced in a transformed graph if this could be avoided. One possibility is to use a different value for dataset->queryNormalizedSelector() using a unique id associated with the table. I think it would be a good long term change, but it would require unique ids (similar to the selSeq) to be added to all table expressions, and correctly preserved by any optimization.

Annotations

Sometimes it is useful to add information into the expression graph (e.g., symbol names, position information) that doesn't change the meaning, but should be preserved. Annotations allow information to be added in this way.

An annotation's implementation of IHqlExpression generally delegates the majority of the methods through to the annotated expression. This means that most code that interrogates the expression graph can ignore their presence, which simplifies the caller significantly. However transforms need to be careful (see below).

Information about the annotation can be obtained by calling IHqlExpression:: getAnnotationKind() and IHqlExpression:: queryAnnotation().

Associated side-effects

In legacy ECL you will see code like the following::

EXPORT a(x) := FUNCTION
+   Y := F(x);
+   OUTPUT(Y);
+   RETURN G(Y);
+END;
+

The assumption is that whenever a(x) is evaluated the value of Y will be output. However that doesn't particularly fit in with a declarative expression graph. The code generator creates a special node (no_compound) with child(0) as the output action, and child(1) as the value to be evaluated (g(Y)).

If the expression ends up being included in the final query then the action will also be included (via the no_compound). At a later stage the action is migrated to a position in the graph where actions are normally evaluated.

Derived properties

There are many pieces of information that it is useful to know about a node in the expression graph - many of which would be expensive to recomputed each time there were required. Eclcc has several mechanisms for caching derived information so it is available efficiently.

  • Boolean flags - getInfoFlags()/getInfoFlags2().

    There are many Boolean attributes of an expression that are useful to know - e.g., is it constant, does it have side-effects, does it reference any fields from a dataset etc. etc. The bulk of these are calculated and stored in a couple of members of the expression class. They are normally retrieved via accessor functions e.g., containsAssertKeyed(IHqlExpression*).

  • Active datasets - gatherTablesUsed().

    It is very common to want to know which datasets an expression references. This information is calculated and cached on demand and accessed via the IHqlExpression::gatherTablesUsed() functions. There are a couple of other functions IHqlExpression::isIndependentOfScope() and IHqlExpression::usesSelector() which provide efficient functions for common uses.

  • Information stored in the type.

    Currently datasets contain information about sort order, distribution and grouping as part of the expression type. This information should be accessed through the accessor functions applied to the expression (e.g., isGrouped(expr)). At some point in the future it is planned to move this information as a general derived property (see next).

  • Other derived property.

    There is a mechanism (in hqlattr) for calculating and caching an arbitrary derived property of an expression. It is currently used for number of rows, location-independent representation, maximum record size etc. . There are typically accessor functions to access the cached information (rather than calling the underlying IHqlExpression::queryAttribute() function).

  • Helper functions.

    Some information doesn't need to be cached because it isn't expensive to calculate, but rather than duplicating the code, a helper function is provided. E.g., queryOriginalRecord() and hasUnknownTransform(). They are not part of the interface because the number would make the interface unwieldy and they can be completely calculated from the public functions.

    However, it can be very hard to find the function you are looking for, and they would greatly benefit from being grouped e.g., into namespaces.

Transformations

One of the key processes in eclcc is walking and transforming the expression graphs. Both of these are covered by the term transformations. One of the key things to bear in mind is that you need to walk the expression graph as a graph, not as a tree. If you have already examined a node once you shouldn't repeat the work - otherwise the execution time may be exponential with node depth.

Other things to bear in mind

  • If a node isn't modified don't create a new one - return a link to the old one.
  • You generally need to walk the graph and gather some information before creating a modified graph. Sometimes creating a new graph can be short-circuited if no changes will be required.
  • Sometimes you can be tempted to try and short-circuit transforming part of a graph (e.g., the arguments to a dataset activity), but because of the way references to fields within dataset work that often doesn't work.
  • If an expression is moved to another place in the graph, you need to be very careful to check if the original context was conditional and that the new context is not.
  • The meaning of expressions can be context dependent. E.g., References to active datasets can be ambiguous.
  • Never walk the expressions as a tree, always as a graph!
  • Be careful with annotations.

It is essential that an expression that is used in different contexts with different annotations (e.g., two different named symbols) is consistently transformed. Otherwise it is possible for a graph to be converted into a tree. E.g.,:

A := x; B := x; C = A + B;
+

must not be converted to:

A' := x'; B' := X'';  C' := A' + B';
+

For this reason most transformers will check if expr->queryBody() matches expr, and if not will transform the body (the unannotated expression), and then clone any annotations.

Some examples of the work done by transformations are:

  • Constant folding.
  • Expanding function calls.
  • Walking the graph and reporting warnings.
  • Optimizing the order and removing redundant activities.
  • Reducing the fields flowing through the generated graph.
  • Spotting common sub expressions.
  • Calculating the best location to evaluate an expression (e.g., globally instead of in a child query).
  • Many, many others.

Some more details on the individual transforms are given below..

Key Stages

Parsing

The first job of eclcc is to parse the ECL into an expression graph. The source for the ECL can come from various different sources (archive, source files, remote repository). The details are hidden behind the IEclSource/IEclSourceCollection interfaces. The createRepository() function is then used to resolve and parse the various source files on demand.

Several things occur while the ECL is being parsed:

  • Function definitions are expanded inline.

    A slightly unusual behaviour. It means that the expression tree is a fully expanded expression -which is better suited to processing and optimizing.

  • Some limited constant folding occurs.

    When a function is expanded, often it means that some of the test conditions are always true/false. To reduce the transformations the condition may be folded early on.

  • When a symbol is referenced from another module this will recursively cause the ECL for that module (or definition within that module) to be parsed.

  • Currently the semantic checking is done as the ECL is parsed.

    If we are going to fully support template functions and delayed expansion of functions this will probably have to change so that a syntax tree is built first, and then the semantic checking is done later.

Normalizing

There are various problems with the expression graph that comes out of the parser:

  • Records can have values as children (e.g., { myField := infield.value} ), but it causes chaos if record definitions can change while other transformations are going on. So the normalization removes values from fields.

  • Some activities use records to define the values that output records should contain (e.g., TABLE). These are now converted to another form (e.g., no_newusertable).

  • Sometimes expressions have multiple definition names. Symbols and annotations are rationalized and commoned up to aid commoning up other expressions.

  • Some PATTERN definitions are recursive by name. They are resolved to a form that works if all symbols are removed.

  • The CASE/MAP representation for a dataset/action is awkward for the transforms to process. They are converted to nested Ifs.

    (At some point a different representation might be a good idea.)

  • EVALUATE is a weird syntax. Instances are replaced with equivalent code which is much easier to subsequently process.

  • The datasets used in index definitions are primarily there to provide details of the fields. The dataset itself may be very complex and may not actually be used. The dataset input to an index is replaced with a dummy "null" dataset to avoid unnecessary graph transforming, and avoid introducing any additional incorrect dependencies.

Scope checking

Generally if you use LEFT/RIGHT then the input rows are going to be available wherever they are used. However if they are passed into a function, and that function uses them inside a definition marked as global then that is invalid (since by definition global expressions don't have any context).

Similarly if you use syntax <dataset>.<field>, its validity and meaning depends on whether <dataset> is active. The scope transformer ensures that all references to fields are legal, and adds a "new" attribute to any no_selects where it is necessary.

Constant folding: foldHqlExpression

This transform simplifies the expression tree. Its aim is to simplify scalar expressions, and dataset expressions that are valid whether or not the nodes are shared. Some examples are:

  • 1 + 2 => 3 and any other operation on scalar constants.
  • IF(true, x, y) => x
  • COUNT(<empty-dataset>) => 0
  • IF (a = b, 'c', 'd') = 'd' => IF(a=b, false, true) => a != b
  • Simplifying sorts, projects filters on empty datasets

Most of the optimizations are fairly standard, but a few have been added to cover more esoteric examples which have occurred in queries over the years.

This transform also supports the option to percolate constants through the graph. E.g., if a project assigns the value 3 to a field, it can substitute the value 3 wherever that field is used in subsequent activities. This can often lead to further opportunities for constant folding (and removing fields in the implicit project).

Expression optimizer: optimizeHqlExpression

This transformer is used to simplify, combine and reorder dataset expressions. The transformer takes care to count the number of times each expression is used to ensure that none of the transformations cause duplication. E.g., swapping a filter with a sort is a good idea, but if there are two filters of the same sort and they are both swapped you will now be duplicating the sort.

Some examples of the optimizations include:

  • COUNT(SORT(x)) => COUNT(x)
  • Moving filters over projects, joins, sorts.
  • Combining adjacent projects, projects and joins.
  • Removing redundant sorts or distributes
  • Moving filters from JOINs to their inputs.
  • Combining activities e.g., CHOOSEN(SORT(x)) => TOPN(x)
  • Sometimes moving filters into IFs
  • Expanding out a field selected from a single row dataset.
  • Combine filters and projects into compound disk read operations.

Implicit project: insertImplicitProjects

ECL tends to be written as general purpose definitions which can then be combined. This can lead to potential inefficiencies - e.g., one definition may summarise some data in 20 different ways, this is then used by another definition which only uses a subset of those results. The implicit project transformer tracks the data flow at each point through the expression graph, and removes any fields that are not required.

This often works in combination with the other optimizations. For instance the constant percolation can remove the need for fields, and removing fields can sometimes allow a left outer join to be converted to a project.

Workunits

is this the correct term? Should it be a query? This should really be independent of this document...)

The code generator ultimately creates workunits. A workunit completely describes a generated query. It consists of two parts. There is an xml component - this contains the workflow information, the various execution graphs, and information about options. It also describes which inputs can be supplied to the query and what results are generated. The other part is the generated shared object compiled from the generated C++. This contains functions and classes that are used by the engines to execute the queries. Often the xml is compressed and stored as a resource within the shared object -so the shared object contains a complete workunit.

Workflow

The actions in a workunit are divided up into individual workflow items. Details of when each workflow item is executed, what its dependencies are stored in the <Workflow> section of the xml. The generated code also contains a class definition, with a method perform() which is used to execute the actions associated with a particular workflow item. (The class instances are created by calling the exported createProcess() factory function).

The generated code for an individual workflow item will typically call back into the engine at some point to execute a graph.

Graph

The activity graphs are stored in the xml. The graph contains details of which activities are required, how those activities link together, what dependencies there are between the activities. For each activity it might contain the following information:

  • A unique id.
  • The "kind" of the activity (from enum ThorActivityKind in eclhelper.hpp)
  • The ECL that created the activity.
  • Name of the original definition
  • Location (e.g., file, line number) of the original ECL.
  • Information about the record size, number of rows, sort order etc.
  • Hints which control options for a particular activity (e.g,, the number of threads to use while sorting).
  • Record counts and stats once the job has executed.

Each activity in a graph also has a corresponding helper class instance in the generated code. (The name of the class is cAc followed by the activity number, and the exported factory method is fAc followed by the activity number.) These classes implement the interfaces defined in eclhelper.hpp.

The engine uses the information from the xml to produce a graph of activities that need to be executed. It has a general purpose implementation of each activity kind, and it uses the class instance to tailor that general activity to the specific use e.g., what is the filter condition, what fields are set up, what is the sort order?

Inputs and Results

The workunit xml contains details of what inputs can be supplied when that workunit is run. These correspond to STORED definitions in the ECL. The result xml also contains the schema for the results that the workunit will generate.

Once an instance of the workunit has been run, the values of the results may be written back into dali's copy of the workunit so they can be retrieved and displayed.

Generated code

Aims for the generated C++ code:

  • Minimal include dependencies.

    Compile time is an issue - especially for small on-demand queries. To help reduce compile times (and dependencies with the rest of the system) the number of header files included by the generated code is kept to a minimum. In particular references to jlib, boost and icu are kept within the implementation of the runtime functions, and are not included in the public dependencies.

  • Thread-safe.

    It should be possible to use the members of an activity helper from multiple threads without issue. The helpers may contain some context dependent state, so different instances of the helpers are needed for concurrent use from different contexts (e.g., expansions of a graph.)

  • Concise.

    The code should be broadly readable, but the variable names etc. are chosen to generate compact code.

  • Functional.

    Generally the generated code assigns to a variable once, and doesn't modify it afterwards. Some assignments may be conditional, but once the variable is evaluated it isn't updated. (There are of course a few exceptions - e.g., dataset iterators)

Implementation details

First a few pointers to help understand the code within eclcc:

  • It makes extensive use of link counting. You need understand that concept to get very far.

  • If something is done more than once then that is generally split into a helper function.

    The helper functions aren't generally added to the corresponding interface (e.g., IHqlExpression) because the interface would become bloated. Instead they are added as global functions. The big disadvantage of this approach is they can be hard to find. Even better would be for them to be rationalised and organised into namespaces.

  • The code is generally thread-safe unless there would be a significant performance implication. In generally all the code used by the parser for creating expressions is thread safe. Expression graph transforms are thread-safe, and can execute in parallel if a constant (NUM_PARALLEL_TRANSFORMS) is increased. The data structures used to represent the generated code are NOT thread-safe.

  • Much of the code generation is structured fairly procedurally, with classes used to process the stages within it.

  • There is a giant "God" class HqlCppTranslator - which could really do with refactoring.

Parser

The eclcc parser uses the standard tools bison and flex to process the ECL and convert it to a

: expression graph. There are a couple of idiosyncrasies with the way it is implemented.

  • Macros with fully qualified scope.

    Slightly unusually macros are defined in the same way that other definitions are - in particular to can have references to macros in other modules. This means that there are references to macros within the grammar file (instead of being purely handled by a pre-processor). It also means the lexer keeps an active stack of macros being processed.

  • Attributes on operators.

    Many of the operators have optional attributes (e.g., KEEP, INNER, LOCAL, ...). If these were all reserved words it would remove a significant number of keywords from use as symbols, and could also mean that when a new attribute was added it broke existing code. To avoid this the lexer looks ahead in the parser tables (by following the potential reductions) to see if the token really could come next. If it can't then it isn't reserved as a symbol.

Generated code

As the workunit is created the code generator builds up the generated code and the xml for the workunit. Most of the xml generation is encapsulated within the IWorkUnit interface. The xml for the graphs is created in an IPropertyTree, and added to the workunit as a block.

C++ Output structures

The C++ generation is ultimately controlled by some template files (thortpl.cpp). The templates are plain text and contain references to allow named sections of code to be expanded at particular points.

The code generator builds up some structures in memory for each of those named sections. Once the generation is complete some peephole optimization is applied to the code. This structure is walked to expand each named section of code as required.

The BuildCtx class provides a cursor into that generated C++. It will either be created for a given named section, or more typically from another BuildCtx. It has methods for adding the different types of statements. Some are simple (e.g., addExpr()), whilst some create a compound statement (e.g., addFilter). The compound statements change the active selector so any new statements are added within that compound statement.

As well as building up a tree of expressions, this data structure also maintains a tree of associations. For instance when a value is evaluated and assigned to a temporary variable, the logical value is associated with that temporary. If the same expression is required later, the association is matched, and the temporary value is used instead of recalculating it. The associations are also used to track the active datasets, classes generated for row-meta information, activity classes etc. etc.

Activity Helper

Each activity in an expression graph will have an associated class generated in the C++. Each different activity kind expects a helper that implements a particular IHThorArg interface. E.g., a sort activity of kind TAKsort requires a helper that implements IHThorSortArg. The associated factory function is used to create instances of the helper class.

The generated class might take one of two forms:

  • A parameterised version of a library class. These are generated for simple helpers that don't have many variations (e.g., CLibrarySplitArg for TAKsplit), or for special cases that occur very frequently (CLibraryWorkUnitReadArg for internal results).
  • A class derived from a skeleton implementation of that helper (typically CThorXYZ implementing interface IHThorXYZ). The base class has default implementations of some of the functions, and any exceptions are implemented in the derived class.

Meta helper

This is a class that is used by the engines to encapsulate all the information about a single row -e.g., the format that each activity generates. It is an implementation of the IOutputMeta interface. It includes functions to

  • Return the size of the row.
  • Serialize and deserialize from disk.
  • Destroy and clean up row instances.
  • Convert to xml.
  • Provide information about the contained fields.

Building expressions

The same expression nodes are used for representing expressions in the generated C++ as the original ECL expression graph. It is important to keep track of whether an expression represents untranslated ECL, or the "translated" C++. For instance ECL has 1 based indexes, while C++ is zero based. If you processed the expression x[1] it might get translated to x[0] in C++. Translating it again would incorrectly refer to x[-1].

There are two key classes used while building the C++ for an ECL expression:

CHqlBoundExpr.

This represents a value that has been converted to C++. Depending on the type, one or more of the fields will be filled in.

CHqlBoundTarget.

This represents the target of an assignment -C++ variable(s) that are going to be assigned the result of evaluating an expression. It is almost always passed as a const parameter to a function because the target is well-defined and the function needs to update that target.

A C++ expression is sometimes converted back to an ECL pseudo-expression by calling getTranslatedExpr(). This creates an expression node of kind no_translated to indicate the child expression has already been converted.

Scalar expressions

The generation code for expressions has a hierarchy of calls. Each function is there to allow optimal code to be generated - e.g., not creating a temporary variable if none are required. A typical flow might be:

  • buildExpr(ctx, expr, bound).

    Evaluate the ecl expression "expr" and save the C++ representation in the class bound. This might then call through to...

  • buildTempExpr(ctx, expr, bound);

    Create a temporary variable, and evaluate expr and assign it to that temporary variable.... Which then calls.

  • buildExprAssign(ctx, target, expr);

    evaluate the expression, and ensure it is assigned to the C++ target "target".

    The default implementation might be to call buildExpr....

An operator must either be implemented in buildExpr() (calling a function called doBuildExprXXX) or in buildExprAssign() (calling a function called doBuildAssignXXX). Some operators are implemented in both places if there are different implementations that would be more efficient in each context.

Similarly there are several different assignment functions:

  • buildAssign(ctx, <ecl-target>, <ecl-value>);
  • buildExprAssign(ctx, <c++-target>, <ecl-value>);
  • assign(ctx, <C++target>, <c++source>)

The different varieties are there depending on whether the source value or targets have already been translated. (The names could be rationalised!)

Datasets

Most dataset operations are only implemented as activities (e.g., PARSE, DEDUP). If these are used within a transform/filter then eclcc will generate a call to a child query. An activity helper for the appropriate operation will then be generated.

However a subset of the dataset operations can also be evaluated inline without calling a child query. Some examples are filters, projects, and simple aggregation. It removes the overhead of the child query call in the simple cases, and often generates more concise code.

When datasets are evaluated inline there is a similar hierarchy of function calls:

  • buildDatasetAssign(ctx, target, expr);

    Evaluate the dataset expression, and assign it to the target (a builder interface). This may then call....

  • buildIterate(ctx, expr)

    Iterate through each of the rows in the dataset expression in turn. Which may then call...

  • buildDataset(ctx, expr, target, format)

    Build the entire dataset, and return it as a single value.

Some of the operations (e.g., aggregating a filtered dataset) can be done more efficiently by summing and filtering an iterator, than forcing the filtered dataset to be evaluated first.

Dataset cursors

The interface IHqlCppDatasetCursor allows the code generator to iterate through a dataset, or select a particular element from a dataset. It is used to hide the different representation of datasets, e.g.,

  • Blocked - the rows are in a contiguous block of memory appended one after another.
  • Array - the dataset is represented by an array of pointers to the individual rows.
  • Link counted - similar to array, but each element is also link counted.
  • Nested. Sometimes the cursor may iterate through multiple levels of child datasets.

Generally rows that are serialized (e.g., on disk) are in blocked format, and they are stored as link counted rows in memory.

Field access classes

The IReferenceSelector interface and the classes in hqltcppc[2] provide an interface for getting and setting values within a row of a dataset. They hide the details of the layout - e.g., csv/xml/raw data, and the details of exactly how each type is represented in the row.

Key filepos weirdness

The current implementation of keys in HPCC uses a format which uses a separate 8 byte integer field which was historically used to store the file position in the original file. Other complications are that the integer fields are stored big-endian, and signed integer values are biased.

This introduces some complication in the way indexes are handled. You will often find that the logical index definition is replaced with a physical index definition, followed by a project to convert it to the logical view. A similar process occurs for disk files to support VIRTUAL(FILEPOSITION) etc.

Source code

The following are the main directories used by the ecl compiler.


Directory Contents


rtl/eclrtpl Template text files used to generate the C++ code

rtl/include Headers that declare interfaces implemented by the generated code

common/deftype Interfaces and classes for scalar types and values.

common/workunit Code for managing the representation of a work unit.

ecl/hql Classes and interfaces for parsing and representing an ecl expression graph

ecl/hqlcpp Classes for converting an expression graph to a work unit (and C++)

ecl/eclcc The executable which ties everything together.

Challenges

From declarative to imperative

As mentioned at the start of this document, one of the main challenges with eclcc is converting the declarative ECL code into imperative C++ code. The direction we are heading in is to allow the engines to support more lazy-evaluation so possibly in this instance to evaluate it the first time it is used (although that may potentially be much less efficient). This will allow the code generator to relax some of its current assumptions.

There are several example queries which are already producing pathological behaviour from eclcc, causing it to generate C++ functions which are many thousands of lines long.

The parser

Currently the grammar for the parser is too specialised. In particular the separate productions for expression, datasets, actions cause problems - e.g., it is impossible to properly allow sets of datasets to be treated in the same way as other sets.

The semantic checking (and probably semantic interpretation) is done too early. Really the parser should build up a syntax tree, and then disambiguate it and perform the semantic checks on the syntax tree.

The function calls should probably be expanded later than they are. I have tried in the past and hit problems, but I can't remember all the details. Some are related to the semantic checking.

`,223),s=[r];function n(l,h,d,c,p,u){return a(),t("div",null,s)}const g=e(o,[["render",n]]);export{m as __pageData,g as default}; diff --git a/assets/devdoc_CodeGenerator.md.Sr_00DEY.lean.js b/assets/devdoc_CodeGenerator.md.Sr_00DEY.lean.js new file mode 100644 index 00000000000..693c96b7f78 --- /dev/null +++ b/assets/devdoc_CodeGenerator.md.Sr_00DEY.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as a,V as i}from"./chunks/framework.gBlNPWt_.js";const m=JSON.parse('{"title":"Eclcc/Code generator","description":"","frontmatter":{"title":"Eclcc/Code generator"},"headers":[],"relativePath":"devdoc/CodeGenerator.md","filePath":"devdoc/CodeGenerator.md","lastUpdated":1721825996000}'),o={name:"devdoc/CodeGenerator.md"},r=i("",223),s=[r];function n(l,h,d,c,p,u){return a(),t("div",null,s)}const g=e(o,[["render",n]]);export{m as __pageData,g as default}; diff --git a/assets/devdoc_CodeReviews.md.yXOcGqwe.js b/assets/devdoc_CodeReviews.md.yXOcGqwe.js new file mode 100644 index 00000000000..3653186177a --- /dev/null +++ b/assets/devdoc_CodeReviews.md.yXOcGqwe.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as i,V as o}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"Code Review Guidelines","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/CodeReviews.md","filePath":"devdoc/CodeReviews.md","lastUpdated":1721825996000}'),a={name:"devdoc/CodeReviews.md"},s=o('

Code Review Guidelines

The Code Submissions document is aimed at developers that are submitting PRs. This document describes some of the goals and expectations for code reviewers.

Review Goals

Code reviews have a few different goals:

  • Catch architectural or design problems.
    These should have been caught earlier, but better later than never...
  • Catch bugs early (incorrect behaviour, inefficiencies, security issues)
  • Ensure the code is readable and maintainable.
    This includes following the project coding standards (see Style Guide).
  • A opportunity for training/passing on information.
    For example providing information about how the current system works, functionality that is already available or suggestions of other approaches the developer may not have thought of.

It is NOT a goal to change the submission until it matches how the reviewer would have coded it.

General comments

Some general comments on code reviews:

  • Code reviewers should be explicit and clearly describe the problem.
    This should include what change is expected if not obvious. Don’t assume the contributor has same understanding/view as reviewer.
  • If a comment is not clear the contributor should ask for clarification.
    ...rather than wasting time trying to second-guess the reviewer.
  • Contributors should feel free to push back if they consider comments are too picky.
    The reviewer can either agree, or provide reasons why they consider it to be an issue.
  • The reviewer should not extend the scope of the original change.
    If the change could be extended, or only partially solves the issue, a new JIRA should be created for the extra work. If the change will introduce regressions, or fundamentally fails to solve the problem then this does not apply!
  • Clearly indicate if a review is incomplete.
    Sometimes a significant design problem means the rest of the code has not been reviewed in detail. Other times an initial review has picked up a set of issues, but the reviewer needs to go back and check other aspects in detail. If this is the case it should be explicitly noted.
  • Repeated issues.
    The reviewer is free to comment on every instance of a repeated issue, but a simple annotation should alert contributor to address appropriately eg: [Please address all instances of this issue]
  • Contributers should provide feedback to the reviewer.
    The contributor should respond to a comment if it isn't obvious where/how they have been addressed (but no need to acknowledge typo/indentation/etc)
  • Only the reviewer should mark issues as resolved using the Github resolve conversation button.
  • Code reviews should be a priority.
    Both reviewers and contributors should respond in a timely manner - don't leave it for days. It destroys the flow of thought and conversation.
  • Check all review comments have been addressed.
    If they have not been addressed you are guaranteed another review/submit cycle. In particular watch out for collapsed conversations. If there are large numbers of comments GitHub will collapse them, which can make comments easy to miss.
  • Sometimes PRs need to be restarted.
    If there are large number of comments > 100, it can be hard to track all the comments and GitHub can become unresponsive. It may be better to close the PR and open a new one.
  • Submit any changes as extra commits. This makes it clear to the reviewer what has changed, and avoids them having to re-review everything. Please do not squash them until the reviewers approve the PR. The few exceptions to this are if the PR is only a couple of lines, or the PR is completely rewritten in response to the review.
  • Reviewers use GitHub's features
    Making use of the "viewed" button can make it easier to track what has changed - or quickly remove trivial changes from view. Ignoring whitespace can often simplify comparisons - especially when code has been refactored or extra conditions or try/catch bocks have been introduced.

Strictness

All code reviews don't need to be equally strict. The "strictness" of the review should reflect the importance and location of the change. Some examples:

  • If it is closely associated with an existing file, then the indentation, style should match the surrounding code - a mixture of styles makes it much harder to read. If it is in a new, independent source file or project this is less of an issue.
  • If the code is in a core library then efficiency and edge cases will be more important.
  • If it is a core part of the system then security is key. If it is a developer only tool then edge cases are less significant.
  • Reviews of draft pull requests are likely to concentrate on the overall approach, rather than the details. They are likely to be more informal (e.g. not always using comments tags).

Checklist

What are some examples of checks to bear in mind when reviewing code?

General:

  • Is the commit title in the correct format, and understandable in a change log?
  • Is the target correct?
  • Is the size appropriate. Could it have been split up?
  • Does the jira contain details of the change, especially the reason?
  • Does it duplicate other functionality?
  • Does the style match the context and the style guide?
  • Is the design encapsulated at the right level? Too abstract or too concrete?

Content:

  • Silly mistakes - indent, typos, commented outcode, spurious changes.
  • Does it introduce any memory leaks? E.g. Correct use of linking? Are exceptions released?
  • Thread safety
    • critical sections or atomic variables if accessed by more than one thread
    • race conditions
    • deadlock
  • authorization. Should it be checked, does it fail by default?
  • Any potential for overflow or DOS? Are all user inputs validated and all lengths protected?
  • Are all secrets stored and passed securely?
  • Comments explaining why for any code that is complex or counter-intuitive.
  • Backward compatibility.
    Could this possibly cause problems if data produced with this change is used in earlier/later versions? Could there be problems if it was used in a mixed-version environment?

Comment tags

When reading comments in a review it can sometimes be hard to know why the reviewer made a comment, or what response is expected. If there is any doubt the contributor should ask. However to make it clearer we are aiming to always add a tag to the front of each review comment. The tag will give an indication of why the comment is being made, its severity and what kind of response is expected. Here is a provisional table of tags:

TagWhatWhyExpected response
design:An architectural or design issueThe reviewer considers the PR has a significant problem which will affect its functionality or future extensibilityreviewer/developer redesign expected before any further changes
scope:The scope of the PR does not match the JiraIf the scope of the fix is too large it can be hard to review, and take much longer to resolve all the issues before the PR is accepted.Discussion. Split the PR into multiple simpler PRs.
function:Incorrect/unexpected functionality implementedThe function doesn't match the description in the jira, or doesn't solve the original problemdeveloper expected to address issue (or discuss)
security:Something in the code introduces a security problemThe reviewer has spotted potential security issues e.g. injection attacksdeveloper expected to discuss the issue (and then address)
bug:A coding issue that will cause incorrect behaviourLikely to cause confusion, invalid results or crashes.developer expected to address issue
efficiency:The code works, but may have scaling or other efficiency issues.Inefficiency can cause problem in some key functions and areasdeveloper addressing the problem (or discuss)
discuss:Reviewer has thought of a potential problem, but not sure if it appliesReviewer has a concern it may be an issue, and wants to check the developer has thought about and addressed the issueDiscussion - either in the PR or offline.
style:Reviewer points out non-conforming code styleMakes the code hard to readDeveloper to fix
indent:A fairly obvious indentation issueMakes the code hard to readDeveloper to fix.
format:Any other unusual formattingMakes the code hard to readDeveloper to fix.
typo:Minor typing errorMakes something (code/message/comment) harder to readDeveloper to fix.
minor:A minor issue that could be improved.Education (the suggestion is better for a particular reason), or something simple to clean up at the same time as other changesDeveloper recommended to fix, but unlikely to stop a merge
picky:A very minor issue that could be improved, but is barely worth commenting onEducation, or something to clean up at the same time as other changesDeveloper discretion to fix, wouldn't stop a merge
future:An additional feature or functionality that fits in but should be done as a separate PR.Ensure that missing functionality is tracked, but PRs are not held up by additional requirements.Contributor to create Jira (unless trivial) and number noted in response.
question:Review has a question that they are not sure of the answer toReviewer would like clarification to help understand the code or design. The answer may lead to further comments.An answer to the question.
note:Reviewer wants to pass on some information to the contributor which they may not knowPassing on knowledge/backgroundcontributor should consider the note, but no change expected/required
personal:Reviewer has an observation based on personal experienceReviewer has comments that would improve the code, but not part of the style guide or required. E.g. patterns for guard conditionsReflect on the suggestion, but no change expected.
documentation:This change may have an impact on documentationMake sure changes can be usedContributor to create Jira describing the impact created, and number noted in response.

The comments should always be constructive. The reviewer should have a reason for each of them, and be able to articulate the reason in the comment or when asked. "I wouldn't have done it like that" is not a good enough on its own!

Similarly there is a difference in opinion within the team on some style issues - e.g. standard libraries or jlib, inline or out of line functions, nested or non-nested classes. Reviews should try and avoid commenting on these unless there is a clear reason why they are significant (functionality, efficiency, compile time) and if so spell it out. Code reviewers should discuss any style issues that they consider should be universally adopted that are not in the style guide.

',23),r=[s];function n(d,l,h,c,u,m){return i(),t("div",null,r)}const b=e(a,[["render",n]]);export{f as __pageData,b as default}; diff --git a/assets/devdoc_CodeReviews.md.yXOcGqwe.lean.js b/assets/devdoc_CodeReviews.md.yXOcGqwe.lean.js new file mode 100644 index 00000000000..d63b64094eb --- /dev/null +++ b/assets/devdoc_CodeReviews.md.yXOcGqwe.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as i,V as o}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"Code Review Guidelines","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/CodeReviews.md","filePath":"devdoc/CodeReviews.md","lastUpdated":1721825996000}'),a={name:"devdoc/CodeReviews.md"},s=o("",23),r=[s];function n(d,l,h,c,u,m){return i(),t("div",null,r)}const b=e(a,[["render",n]]);export{f as __pageData,b as default}; diff --git a/assets/devdoc_CodeSubmissions.md.wf0ooHZx.js b/assets/devdoc_CodeSubmissions.md.wf0ooHZx.js new file mode 100644 index 00000000000..3092614e009 --- /dev/null +++ b/assets/devdoc_CodeSubmissions.md.wf0ooHZx.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as i,V as o}from"./chunks/framework.gBlNPWt_.js";const g=JSON.parse('{"title":"Code Submission Guidelines","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/CodeSubmissions.md","filePath":"devdoc/CodeSubmissions.md","lastUpdated":1721825996000}'),s={name:"devdoc/CodeSubmissions.md"},r=o('

Code Submission Guidelines

We welcome submissions to the platform especially in the form of pull requests into the HPCC-Systems github repository. The following describes some of processes for merging PRs.

Pull requests

There are a few things that should be considered when creating a PR to increase the likelihood that they can be accepted quickly.

  • Write a good commit message
    The format should be HPCC-XXXXX (where XXXXX is the bug number) followed by a description of the issue. The text should make sense in a change log by itself - without reference to the jira or the contents of the PR. We should aim to increase the information that is included as part of the commit message - not rely on on the jira.
  • Ensure the reviewer has enough information to review the change.
    The code reviewer only has the JIRA and the PR to go on. The JIRA (or associated documentation) should contain enough details to review the PR - e.g. the purpose, main aim, why the change was made etc.. If the scope of the jira has changed then the jira should be updated to reflect that.
    If the submission requires changes to the documentation then the JIRA should contain all the details needed to document it, and the PR should either contain the documentation changes, or a documentation JIRA should be created.
  • Fill in the checklist
    The check boxes are there to remind you to consider different aspects of the PR. Not all of them apply to every submission, but if you tick a box and have not really thought about the item then prepare to be embarrassed!
  • Prefer small submissions
    It isn't always possible, but several smaller PRs are much easier to review than one large change. If your submission includes semi-automatic/mechanical changes (e.g. renaming large numbers of function calls, or adding an extra parameter) please keep it as a separate commit. This makes it much easier to review the PR - since the reviewer will be looking for different errors in the different types of changes.
  • Check for silly mistakes
    Review your own code in github, after creating the PR to check for silly mistakes. It doesn't take long, and often catches trivial issues. It may avoid the need for a cycle of code-review/fixes. It may be helpful to add some notes to specific changes e.g. "this change is mainly or solely refactoring method A into method B and C. ". Some common examples of trivial issues to look for include:
    • Inconsistent indentation, or using tabs rather than spaces to indent.
    • Lines of tracing left in.
    • Lines of code commented out that should be deleted.
    • TBD reminders of work that need implementing or removing.
    • Unrelated files that have been accidentally modified.
    • Accidental changes to submodule versions.
    • Typos in error messages, tracing or comments, or in the commit message.
    • Incomplete edits when copy and pasting code.
    • Check subtractions are the right way around, and potential for overflow.
    • New files with the wrong copyright date
  • Check the target branch (see below)
  • Request one or more reviews. For relatively simple changes a single reviewer is normally enough.

Reviewers

All pull requests should be reviewed by someone who is not the author before merging. Complex changes, changes that require input from multiple experts, or that have implications throughout the system should be reviewed by multiple reviewers. This should include someone who is responsible for merging changes for that part of the system. (Unless it is a simple change written by someone with review rights.)

Contributors should use the github reviewers section on the PR to request reviews. After a contributor has pushed a set of changes in response to a review, they should refresh the github review status, so the users are notified it is ready for re-review. When the review is complete, a person with responsibility for merging changes to that part of the system should be added as a reviewer (or refreshed), with a comment that it is ready to merge.

Reviewers should check for PRs that are ready for their review via github's webpage (filter "review-requested:<reviewer-id>") or via the github CLI (e.g. gh pr status). Contributors should similarly ensure they stay up to date with any comments on requests for change on their submissions.

Target branch

The Version support document contains details of the different versions that are supported, and which version should be targetted for different kinds of changes. Occasionally earlier branches will be chosen, (e.g. security fixes to even older versions) but they should always be carefully discussed (and documented).

Changes will always be upmerged into the next point release for all the more recent major and minor versions (and master).

',12),a=[r];function n(h,l,d,c,u,m){return i(),t("div",null,a)}const p=e(s,[["render",n]]);export{g as __pageData,p as default}; diff --git a/assets/devdoc_CodeSubmissions.md.wf0ooHZx.lean.js b/assets/devdoc_CodeSubmissions.md.wf0ooHZx.lean.js new file mode 100644 index 00000000000..f582eb92b0d --- /dev/null +++ b/assets/devdoc_CodeSubmissions.md.wf0ooHZx.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as i,V as o}from"./chunks/framework.gBlNPWt_.js";const g=JSON.parse('{"title":"Code Submission Guidelines","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/CodeSubmissions.md","filePath":"devdoc/CodeSubmissions.md","lastUpdated":1721825996000}'),s={name:"devdoc/CodeSubmissions.md"},r=o("",12),a=[r];function n(h,l,d,c,u,m){return i(),t("div",null,a)}const p=e(s,[["render",n]]);export{g as __pageData,p as default}; diff --git a/assets/devdoc_DevDocs.md.p38z6BoL.js b/assets/devdoc_DevDocs.md.p38z6BoL.js new file mode 100644 index 00000000000..933a95a5da3 --- /dev/null +++ b/assets/devdoc_DevDocs.md.p38z6BoL.js @@ -0,0 +1,8 @@ +import{_ as e,c as t,o as a,V as o}from"./chunks/framework.gBlNPWt_.js";const g=JSON.parse('{"title":"Working with developer documentation","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/DevDocs.md","filePath":"devdoc/DevDocs.md","lastUpdated":1721825996000}'),i={name:"devdoc/DevDocs.md"},n=o(`

Working with developer documentation

Some basic guidlines to ensure your documentation works well with VitePress

Documentation location

Documents can be located anywhere in the repository folder structure. If it makes sense to have documentation "close" to specific components, then it can be located in the same folder as the component. For example, any developer documentation for specific plugins can be located in those folders. If this isn't appropriate then the documentation can be located in the devdoc or subfolders of devdoc.

WARNING

There is an exclusion list in the devdoc/.vitepress/config.js file that prevents certain folders from being included in the documentation. If you add a new document to a folder that is excluded, then it will not be included in the documentation. If you need to add a new document to an excluded folder, then you will need to update the exclusion list in the devdoc/.vitepress/config.js file.

Documentation format

Documentation is written in Markdown. This is a simple format that is easy to read and write. It is also easy to convert to other formats, such as HTML, PDF, and Word. Markdown is supported by many editors, including Visual Studio Code, and is supported by VitePress.

TIP

VitePress extends Markdown with some additional features, such as custom containers, it is recommended that you refer to the VitePress documentation for more details.

Rendering documentation locally with VitePress

To assist with the writing of documentation, VitePress can be used to render the documentation locally. This allows you to see how the documentation will look when it is published. To start the local development server you need to type the following commands in the root HPCC-Platform folder:

sh
npm install
+npm run docs-dev

This will start a local development server and display the URL that you can use to view the documentation. The default URL is http://localhost:5173/HPCC-Platform, but it may be different on your machine. The server will automatically reload when you make changes to the documentation.

WARNING

The first time you start the VitePress server it will take a while to complete. This is because it is locating all the markdown files in the repository and creating the html pages. Once it has completed this step once, it will be much faster to start the server again.

Adding a new document

To add a new document, you need to add a new markdown file to the repository. The file should be named appropriately and have the .md file extension. Once the file exists, you can view it by navigating to the appropriate URL. For example, if you add a new file called MyNewDocument.md to the devdoc folder, then you can view it by navigating to http://localhost:5173/HPCC-Platform/devdoc/MyNewDocument.html.

Adding a new document to the sidebar

To add a new document to the sidebar, you need to add an entry to the devdoc/.vitepress/config.js file. The entry should be added to the sidebar section. For example, to add a new document called MyNewDocument.md to the devdoc folder, you would add the following entry to the sidebar section:

js
sidebar: [
+    ...
+    {
+        text: 'My New Document',
+        link: '/devdoc/MyNewDocument'
+    }
+    ...

TIP

You can find more information on the config.js file in the VitePress documentation.

Editing the main landing page

The conent of the main landing page is located in index.md in the root folder. Its structure uses the VitePress "home" layout.

`,21),s=[n];function d(l,r,c,h,p,u){return a(),t("div",null,s)}const f=e(i,[["render",d]]);export{g as __pageData,f as default}; diff --git a/assets/devdoc_DevDocs.md.p38z6BoL.lean.js b/assets/devdoc_DevDocs.md.p38z6BoL.lean.js new file mode 100644 index 00000000000..f2025fff5e1 --- /dev/null +++ b/assets/devdoc_DevDocs.md.p38z6BoL.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as a,V as o}from"./chunks/framework.gBlNPWt_.js";const g=JSON.parse('{"title":"Working with developer documentation","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/DevDocs.md","filePath":"devdoc/DevDocs.md","lastUpdated":1721825996000}'),i={name:"devdoc/DevDocs.md"},n=o("",21),s=[n];function d(l,r,c,h,p,u){return a(),t("div",null,s)}const f=e(i,[["render",d]]);export{g as __pageData,f as default}; diff --git a/assets/devdoc_Development.md.qUtv1gjz.js b/assets/devdoc_Development.md.qUtv1gjz.js new file mode 100644 index 00000000000..59521d4dde2 --- /dev/null +++ b/assets/devdoc_Development.md.qUtv1gjz.js @@ -0,0 +1,3 @@ +import{_ as e,c as s,o as t,V as a}from"./chunks/framework.gBlNPWt_.js";const m=JSON.parse('{"title":"Development Guide","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/Development.md","filePath":"devdoc/Development.md","lastUpdated":1721825996000}'),i={name:"devdoc/Development.md"},n=a(`

Development Guide

HPCC Source

The most upto date details of building the system are found on the HPCC Wiki.

Getting the sources

The HPCC Platform sources are hosted on GitHub. You can download a snapshot of any branch using the download button there, or you can set up a git clone of the repository. If you are planning to contribute changes to the system, see the CONTRIBUTORS document for information about how to set up a GitHub fork of the project through which pull-requests can be made.

Building the system from sources

Requirements

The HPCC platform requires a number of third party tools and libraries in order to build. The HPCC Wiki contains the details of the dependencies that are required for different distributions.

For building any documentation, the following are also required:

bash
sudo apt-get install docbook
+sudo apt-get install xsltproc
+sudo apt-get install fop

NOTE: Installing the above via alternative methods (i.e. from source) may place installations outside of searched paths.

Building the system

The HPCC system is built using the cross-platform build tool cmake, which is available for Windows, virtually all flavors of Linux, FreeBSD, and other platforms. You should install cmake version 2.8.3 or later before building the sources.

On some distros you will need to build cmake from sources if the version of cmake in the standard repositories for that distro is not modern enough. It is good practice in cmake to separate the build directory where objects and executables are made from the source directory, and the HPCC cmake scripts will enforce this.

To build the sources, create a directory where the built files should be located, and from that directory, run:

bash
cmake <source directory>

Depending on your operating system and the compilers installed on it, this will create a makefile, Visual Studio .sln file, or other build script for building the system. If cmake was configured to create a makefile, then you can build simply by typing:

bash
make

If a Visual Studio solution file was created, you can load it simply by typing the name: hpccsystems-platform.sln

This will load the solution in Visual Studio where you can build in the usual way.

Packaging

To make an installation package on a supported linux system, use the command:

bash
make package

This will first do a make to ensure everything is up to date, then will create the appropriate package for your operating system, Currently supported package formats are rpm (for RedHat/Centos) and .deb (for Debian and Ubuntu). If the operating system is not one of the above, or is not recognized, make package will create a tarball.

The package installation does not start the service on the machine, so if you want to give it a go or test it (see below), make sure to start the service manually and wait until all services are up (mainly wait for EclWatch to come up on port 8010).

Testing the system

After compiling, installing the package and starting the services, you can test the HPCC platform on a single-node setup.

Unit Tests

Some components have their own unit-tests. Once you have compiled (no need to start the services), you can already run them. Supposing you build a Debug version, from the build directory you can run:

bash
./Debug/bin/roxie -selftest

and:

bash
./Debug/bin/eclagent -selftest

You can also run the Dali regression self-tests:

bash
./Debug/bin/daregress localhost

Regression Tests

MORE Completely out of date - needs rewriting.

Compiler Tests

The ECLCC compiler tests rely on two distinct runs: a known good one and your test build. For normal development, you can safely assume that the OSS/master branch in github is good. For overnight testing, golden directories need to be maintained according to the test infrastructure. There are Bash (Linux) and Batch (Windows) scripts to run the regressions:

The basic idea behind this tests is to compare the output files (logs and XML files) between runs. The log files should change slightly (the comparison should be good enough to filter most irrelevant differences), but the XML files should be identical if nothing has changed. You should only see differences in the XML where you have changed in the code, or new tests were added as part of your development.

On Linux, there are two steps:

Step 1: Check-out OSS/master, compile and run the regressions to populate the 'golden' directory:

bash
./regress.sh -t golden -e buildDir/Debug/bin/eclcc

This will run the regressions in parallel, using as many CPUs as you have, and using your just-compiled ECLCC, assuming you compiled for Debug version.

Step 2: Make your changes (or check-out your branch), compile and run again, this time output to a new directory and compare to the 'golden' repo.:

bash
./regress.sh -t my_branch -c golden -e buildDir/Debug/bin/eclcc

This will run the regressions in the same way, output to 'my_branch' dir and compare it to the golden version, highlighting the differences.

NOTE: If you changed the headers that the compiled binaries will use, you must re-install the package (or provide -i option to the script to the new headers).

Step 3: Step 2 only listed the differences, now you need to see what they are. For that, re-run the regressing script omitting the compiler, since the only thing we'll do is to compare verbosely.:

bash
./regress.sh -t my_branch -c golden

This will show you all differences, using the same ignore filters as before, between your two branches. Once you're happy with the differences, commit and issue a pull-request.

TODO: Describe compiler tests on Windows.

Debugging the system

On linux systems, the makefile generated by cmake will build a specific version (debug or release) of the system depending on the options selected when cmake is first run in that directory. The default is to build a release system. In order to build a debug system instead, use command:

bash
cmake -DCMAKE_BUILD_TYPE=Debug <source directory>

You can then run make or make package in the usual way to build the system.

On a Windows system, cmake always generates s solution file with both debug and release target platforms in it, so you can select which one to build within Visual Studio.

`,56),o=[n];function r(l,h,p,d,c,u){return t(),s("div",null,o)}const k=e(i,[["render",r]]);export{m as __pageData,k as default}; diff --git a/assets/devdoc_Development.md.qUtv1gjz.lean.js b/assets/devdoc_Development.md.qUtv1gjz.lean.js new file mode 100644 index 00000000000..bb291083eaa --- /dev/null +++ b/assets/devdoc_Development.md.qUtv1gjz.lean.js @@ -0,0 +1 @@ +import{_ as e,c as s,o as t,V as a}from"./chunks/framework.gBlNPWt_.js";const m=JSON.parse('{"title":"Development Guide","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/Development.md","filePath":"devdoc/Development.md","lastUpdated":1721825996000}'),i={name:"devdoc/Development.md"},n=a("",56),o=[n];function r(l,h,p,d,c,u){return t(),s("div",null,o)}const k=e(i,[["render",r]]);export{m as __pageData,k as default}; diff --git a/assets/devdoc_GitAuthenticate.md.BC7Bmr5A.js b/assets/devdoc_GitAuthenticate.md.BC7Bmr5A.js new file mode 100644 index 00000000000..3f95e0db568 --- /dev/null +++ b/assets/devdoc_GitAuthenticate.md.BC7Bmr5A.js @@ -0,0 +1,12 @@ +import{_ as e,c as a,o as t,V as s}from"./chunks/framework.gBlNPWt_.js";const b=JSON.parse('{"title":"HPCC git support","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/GitAuthenticate.md","filePath":"devdoc/GitAuthenticate.md","lastUpdated":1721825996000}'),n={name:"devdoc/GitAuthenticate.md"},i=s(`

HPCC git support

Version 8.4 of the HPCC platform allows package files to define dependencies between git repositories and also allows you to compile directly from a git repository.

E.g.

ecl run hthor --main demo.main@ghalliday/gch-ecldemo-d#version1 --server=...

There are no futher requirements if the repositories are public, but private repositories have the additional complication of supplying authentication information. Git provides various methods for providing the credentials...

Credentials for local development

The following are the recommended approaches configuring the credentials on a local development system interacting with github:

  1. ssh key.

In this scenario, the ssh key associated with the local developer machine is registered with the github account. For more details see https://docs.github.com/en/authentication/connecting-to-github-with-ssh/about-ssh

This is used when the github reference is of the form ssh://github.com. The sshkey can be protected with a passcode, and there are various options to avoid having to enter the passcode each time.

It is preferrable to use the https:// protocol instead of ssh:// for links in package-lock.json files. If ssh:// is used it requires any machine that processes the dependency to have access to a registered ssh key.

  1. github authentication

Download the GitHub command line tool (https://github.com/cli/cli). You can then use it to authenticate all git access with

gh auth login

Probably the simplest option if you are using github. More details are found at https://cli.github.com/manual/gh_auth_login

  1. Use a personal access token

These are similar to a password, but with additional restrictions on their lifetime and the resources that can be accessed.

Details on how to to create them are found : https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token

These can then be used with the various git credential caching options. E.g. see https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage

Configuring eclccserver

All of the options above are likely to involve some user interaction - passphrases for ssh keys, web interaction with github authentication, and initial entry for cached access tokens. This is problematic for eclccserver - which cannot support user interaction, and it is preferrable not to pass credentials around.

The solution is to use a personal access token securely stored as a secret. (This would generally be associated with a special service account.) This avoids the need to pass credentials and allows the keys to be rotated.

The following describes the support in the different versions:

Kubernetes

In Kubernetes you need to take the following steps:

a) add the gitUsername property to the eclccserver component in the value.yaml file:

eclccserver:
+- name: myeclccserver
+  gitUsername: ghalliday

b) add a secret to the values.yaml file, with a key that matches the username:

secrets:
+  git:
+    ghalliday: my-git-secret

note: this cannot currently use a vault - probably need to rethink that. (Possibly extract from secret and supply as an optional environment variable to be picked up by the bash script.)

c) add a secret to Kubernetes containing the personal access token:

apiVersion: v1
+kind: Secret
+metadata:
+  name: my-git-secret
+type: Opaque
+stringData:
+  password: ghp_eZLHeuoHxxxxxxxxxxxxxxxxxxxxol3986sS=
kubectl apply -f ~/dev/hpcc/helm/secrets/my-git-secret

When a query is submitted to eclccserver, any git repositories are accessed using the user name and password.

Bare-metal

Bare-metal require some similar configuration steps:

a) Define the environment variable HPCC_GIT_USERNAME

export HPCC_GIT_USERNAME=ghalliday

b) Store the access token in /opt/HPCCSystems/secrets/git/$HPCC_GIT_USERNAME/password

E.g.

$cat /opt/HPCCSystems/secrets/git/ghalliday/password
+ghp_eZLHeuoHxxxxxxxxxxxxxxxxxxxxol3986sS=
`,41),o=[i];function p(r,c,l,h,d,u){return t(),a("div",null,o)}const m=e(n,[["render",p]]);export{b as __pageData,m as default}; diff --git a/assets/devdoc_GitAuthenticate.md.BC7Bmr5A.lean.js b/assets/devdoc_GitAuthenticate.md.BC7Bmr5A.lean.js new file mode 100644 index 00000000000..0c346ef0fd0 --- /dev/null +++ b/assets/devdoc_GitAuthenticate.md.BC7Bmr5A.lean.js @@ -0,0 +1 @@ +import{_ as e,c as a,o as t,V as s}from"./chunks/framework.gBlNPWt_.js";const b=JSON.parse('{"title":"HPCC git support","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/GitAuthenticate.md","filePath":"devdoc/GitAuthenticate.md","lastUpdated":1721825996000}'),n={name:"devdoc/GitAuthenticate.md"},i=s("",41),o=[i];function p(r,c,l,h,d,u){return t(),a("div",null,o)}const m=e(n,[["render",p]]);export{b as __pageData,m as default}; diff --git a/assets/devdoc_LDAPSecurityManager.md.6QV0TOn6.js b/assets/devdoc_LDAPSecurityManager.md.6QV0TOn6.js new file mode 100644 index 00000000000..d48959a1856 --- /dev/null +++ b/assets/devdoc_LDAPSecurityManager.md.6QV0TOn6.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as a,V as n}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"LDAP Security Manager Init","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/LDAPSecurityManager.md","filePath":"devdoc/LDAPSecurityManager.md","lastUpdated":1721825996000}'),o={name:"devdoc/LDAPSecurityManager.md"},i=n('

LDAP Security Manager Init

This document covers the main steps taken by the LDAP Security Manager during initialization. It is important to note that the LDAP Security Manager uses the LDAP protocol to access an Active Directory, AD. The AD is the store for users, groups, permissions, resources, and more. The term LDAP is generally overused to refer to both.

LDAP Instances

Each service and/or component using the LDAP security manager gets its own instance of the security manager. This includes a unique connection pool (see below). All operations described apply to each LDAP instance.

Initialization Steps

The following sections cover the main steps taken during initialization

Load Configuration

The following items are loaded from the configuration:

AD Hosts

The LDAP Security Manager supports using multiple ADs. The FQDN or IP address of each AD host is read from configuration data and stored internally. The source is a comma separated list stored in the ldapAddress config value. Each entry is added to a pool of ADs.

Note that all ADs are expected to use the same credentials and have the same configuration

AD Credentials

AD credentials consist of a username and password. The LDAP security manager users these to perform all operations on behalf of users and components in the cluster. There are three potential sources for credentials.

  1. A Kubernetes secret. If the ldapAdminSecretKey config value is set, but ldapAdminVaultId is not (see 2) then the AD credentials are retrieved as Kubernetes secrets.
  2. If both ldapAdminSecretKey and ldapAdminVaultId config values are present, the AD credentials are retrieved from the vault.
  3. Hardcoded values from the systemCommonName and systemPassword config values stored in the environment.xml file.

As stated above, when multiple ADs are in use, the configuration of each must be the same. This includes credentials.

Retrieve Server Information from the AD

During initialization, the security manager begins incrementing through the set of defined ADs until it is able to connect and retrieve information from an AD. Once retrieved, the information is used for all ADs (see statement above about all ADs being the same). The accessed AD is marked as the current AD and no other ADs are accessed during initialization.

The retrieved information is used to verify the AD type so the security manager adjusts for variations between types. Additionally, defined DNs may be adjusted to match AD type requirements.

Connections

The manager handles connections to an AD in order to perform required operations. It is possible that values such as permissions and resources may be cached to improve performance.

Connection Pool

The LDAP security manager maintains a pool of LDAP connections. The pool is limited in size to maxConnections from the configuration. The connection pool starts empty. As connections are created, each is added to the pool until the max allowed is reached.

The following process is used when an LDAP connection is needed.

First, the connection pool is searched for a free connection. If found and valid, the connection is returned. A connection is considered free if no one is using it and valid if the AD can be accessed. If no valid free connections are found, a new uninitialized connection is created.

For a new connection, an attempt is made to connect to each AD starting with the current. See Handling AD Hosts below for how ADs are cycled when a connection fails. For each AD, as it cycles through, connection attempts are retried with a short delay between each. If unable to connect, the AD host is marked rejected and the next is attempted. Once a new connection has been established, if the max number of connections has not been reached yet, the connection is added to the pool.

It is important to note that if the pool has reached its max size, new connections will continue to be made, but are not saved in the pool. This allows the pool to maintain a steady working state, but allow for higher demand. Connections not saved to the pool are deleted once no longer in use.

Handling AD Hosts

The manager keeps a list of AD hosts and the index of the current host. The current host is used for all AD operations until there is a failure. At that time the manager marks the host as "rejected" and moves to the next host using a round-robin scheme.

',28),s=[i];function r(c,d,h,l,u,m){return a(),t("div",null,s)}const A=e(o,[["render",r]]);export{f as __pageData,A as default}; diff --git a/assets/devdoc_LDAPSecurityManager.md.6QV0TOn6.lean.js b/assets/devdoc_LDAPSecurityManager.md.6QV0TOn6.lean.js new file mode 100644 index 00000000000..e28431b0f99 --- /dev/null +++ b/assets/devdoc_LDAPSecurityManager.md.6QV0TOn6.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as a,V as n}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"LDAP Security Manager Init","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/LDAPSecurityManager.md","filePath":"devdoc/LDAPSecurityManager.md","lastUpdated":1721825996000}'),o={name:"devdoc/LDAPSecurityManager.md"},i=n("",28),s=[i];function r(c,d,h,l,u,m){return a(),t("div",null,s)}const A=e(o,[["render",r]]);export{f as __pageData,A as default}; diff --git a/assets/devdoc_MemoryManager.md.WzZxL7Sr.js b/assets/devdoc_MemoryManager.md.WzZxL7Sr.js new file mode 100644 index 00000000000..ef97612afc8 --- /dev/null +++ b/assets/devdoc_MemoryManager.md.WzZxL7Sr.js @@ -0,0 +1 @@ +import{_ as e,c as a,o as t,V as o}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"The Roxie Memory Manager","description":"","frontmatter":{"title":"The Roxie Memory Manager"},"headers":[],"relativePath":"devdoc/MemoryManager.md","filePath":"devdoc/MemoryManager.md","lastUpdated":1721825996000}'),i={name:"devdoc/MemoryManager.md"},r=o('

Introduction

This memory manager started life as the memory manager which was only used for the Roxie engine. It had several original design goals:

  • Support link counted rows. (When the last reference is released the row is freed.)
  • Be as fast as possible on allocate and deallocate of small rows.
  • Allow rows serialized from slaves to be used directly without being cloned first.
  • Allow the memory used by a single query, or by all queries combined, to be limited, with graceful recovery.
  • Isolate roxie queries from one another, so that one query can't bring down all the rest by allocating too much memory.
  • Guarantee all the memory used by a query is freed when the query finishes, reducing the possibility of memory leaks.
  • Predictable behaviour with no pathogenic cases.

(Note that efficient usage of memory does not appear on that list - the expectation when the memory manager was first designed was that Roxie queries would use minimal amounts of memory and speed was more important. Some subsequent changes e.g., Packed heaps, and configurable bucket sizes help mitigate that.)

Main Structure

The basic design is to reserve (but not commit) a single large block of memory in the virtual address space. This memory is subdivided into "pages". (These are not the same as the os virtual memory pages. The memory manager pages are currently defined as 1Mb in size.)

The page bitmap

The system uses a bitmap to indicate whether each page from the global memory has been allocated. All active IRowManager instances allocate pages from the same global memory space. To help reduce fragmentation allocations for single pages are fulfilled from one end of the address space, while allocations for multiple pages are fulfilled from the other.

IRowManager

This provides the primary interface for allocating memory. The size of a requested allocation is rounded up to the next "bucket" size, and the allocation is then satisfied by the heap associated with that bucket size. Different engines can specify different bucket sizes - an optional list is provided to setTotalMemoryLimit. Roxie tends to use fewer buckets to help reduce the number of active heaps. Thor uses larger numbers since it is more important to minimize the memory wasted.

Roxie uses a separate instance of IRowManager for each query. This provides the mechanism for limiting how much memory a query uses. Thor uses a single instance of an IRowManager for each slave/master.

Heaps

Memory is allocated from a set of "heaps - where each heap allocates blocks of memory of a single size. The heap exclusively owns a set of heaplet (each 1 page in size), which are held in a doubly linked list, and sub allocates memory from those heaplets.

Information about each heaplet is stored in the base of the page (using a class with virtual functions) and the address of an allocated row is masked to determine which heap object it belongs to, and how it should be linked/released etc. Any pointer not in the allocated virtual address (e.g., constant data) can be linked/released with no effect.

Each heaplet contains a high water mark of the address within the page that has already been allocated (freeBase), and a lockless singly-linked list of rows which have been released (r_block). Releasing a row is non-blocking and does not involve any spin locks or critical sections. However, this means that empty pages need to be returned to the global memory pool at another time. (This is done in releaseEmptyPages()).

When the last row in a page is released a flag (possibleEmptyPages) is set in its associated heap. * This is checked before trying to free pages from a particular heap, avoiding waiting on a lock and traversing a candidate list.

Any page which might contain some spare memory is added to a lockless spare memory linked list. * Items are popped from this list when a heap fails to allocate memory from the current heaplet. Each item is checked in turn if it has space before allocating a new heaplet. * The list is also walked when checking to see which pages can be returned to the global memory. The doubly linked heaplet list allows efficient freeing.

Each allocation has a link count and an allocator id associated with it. The allocator id represents the type of the row, and is used to determine what destructor needs to be called when the row is destroyed. (The count for a row also contains a flag in the top bit to indicate if it is fully constructed, and therefore valid for the destructor to be called.)

Huge Heap

A specialized heap is used to manage all allocations that require more than one page of memory. These allocations are not held on a free list when they are released, but each is returned directly to the global memory pool. Allocations in the huge heap can be expanded and shrunk using the resizeRow() functions - see below.

Specialised Heaps:

Packed

By default a fixed size heaps rounds the requested allocation size up to the next bucket size. A packed heap changes this behaviour and it is rounded up to the next 4 byte boundary instead. This reduces the amount of memory wasted for each row, but potentially increases the number of distinct heaps.

Unique

By default all fixed size heaps of the same size are shared. This reduces the memory consumption, but if the heap is used by multiple threads it can cause significant contention. If a unique heap is specified then it will not be shared with any other requests. Unique heaps store information about the type of each row in the heaplet header, rather than per row - which reduces the allocation overhead for each row. (Note to self: Is there ever any advantage having a heap that is unique but not packed??)

Blocked

Blocked is an option on createFixedRowHeap() to allocate multiple rows from the heaplet, and then return the additional rows on subsequent calls. It is likely to reduce the average number of atomic operations required for each row being allocated, but the heap that is returned can only be used from a single thread because it is not thread safe.

Scanning

By default the heaplets use a lock free singly linked list to keep track of rows that have been freed. This requires an atomic operation for each allocation and for each free. The scanning allocator uses an alternative approach. When a row is freed the row header is marked, and a row is allocated by scanning through the heaplet for rows that have been marked as free. Scanning uses atomic store and get, rather than more expensive synchronized atomic operations, so is generally faster than the linked list - provided a free row is found fairly quickly.

The scanning heaps have better best-case performance, but worse worse-case performance (if large numbers of rows need to be scanned before a free row is found). The best-case tends to be true if only one thread/activity is accessing a particular heap, and the worse-case if multiple activities are accessing a heap, particularly if the rows are being buffered. It is the default for thor which tends to have few active allocators, but not for roxie, which tends to have large numbers of allocators.

Delay Release

This is another varation on the scanning allocator, which further reduces the number of atomic operations. Usually when a row is allocated the link count on the heaplet is increased, and when it is freed the link count is decremented. This option delays decrementing the link count when the row is released, by marking the row with a different free flag. If it is subsequently reallocated there is no need to increment the link count. The downside is that it is more expensive to check whether a heaplet is completely empty (since you can no longer rely on the heaplet linkcount alone).

Dynamic Spilling

Thor has additional requirements to roxie. In roxie, if a query exceeds its memory requirements then it is terminated. Thor needs to be able to spill rows and other memory to disk and continue. This is achieved by allowing any process that stores buffered rows to register a callback with the memory manager. When more memory is required these callbacks are called to free up memory, and allow the job to continue.

Each callback can specify a priority - lower priority callbacks are called first since they are assumed to have a lower cost associated with spilling. When more memory is required the callbacks are called in priority order until one of them succeeds. The can also be passed a flag to indicate it is critical to force them to free up as much memory as possible.

Complications

There are several different complications involved with the memory spilling:

  • There will be many different threads allocating rows.
  • Callbacks could be triggered at any time.
  • There is a large scope for deadlock between the callbacks and allocations.
  • It may be better to not resize a large array if rows had to be evicted to resize it.
  • Filtered record streams can cause significant wasted space in the memory blocks.
  • Resizing a multi-page allocation is non trivial.

Callback Rules

Some rules to follow when implementing callbacks:

  • A callback cannot allocate any memory from the memory manager. If it does it is likely to deadlock.

  • You cannot allocate memory while holding a lock if that lock is also required by a callback.

    Again this will cause deadlock. If it proves impossible you can use a try-lock primitive in the callback, but it means you won't be able to spill those rows.

  • If the heaps are fragmented it may be more efficient to repack the heaps than spill to disk.

  • If you're resizing a potentially big block of memory use the resize function with the callback.

Resizing Large memory blocks

Some of the memory allocations cover more than one "page" - e.g., arrays used to store blocks of rows. (These are called huge pages internally, not to be confused with operating system support for huge pages...) When one of these memory blocks needs to be expanded you need to be careful:

  • Allocating a new page, copying, updating the pointer (within a cs) and then freeing is safe. Unfortunately it may involve copying a large chunk of memory. It may also fail if there isn't memory for the new and old block, even if the existing block could have been expanded into an adjacent block.
  • You can't lock, call a resize routine and update the pointer because the resize routine may need to allocate a new memory block- that may trigger a callback, which could in turn deadlock trying to gain the lock. (The callback may be from another thread...)
  • Therefore the memory manager contains a call which allows you to resize a block, but with a callback which is used to atomically update the pointer so it always remains thread safe.

Compacting heaps

Occasionally you have processes which read a large number of rows and then filter them so only a few are still held in memory. Rows tend to be allocated in sequence through the heap pages, which can mean those few remaining rows are scattered over many pages. If they could all be moved to a single page it would free up a significant amount of memory.

The memory manager contains a function to pack a set of rows into a smaller number of pages: IRowManager->compactRows().

This works by iterating through each of the rows in a list. If the row belongs to a heap that could be compacted, and isn't part of a full heaplet, then the row is moved. Since subsequent rows tend to be allocated from the same heaplet this has the effect of compacting the rows.

Shared Memory

Much of the time Thor doesn't uses full memory available to it. If you are running multiple Thor processes on the same machine you may want to configure the system so that each Thor has a private block of memory, but there is also a shared block of memory which can be used by whichever process needs it.

The ILargeMemCallback provides a mechanism to dynamically allocate more memory to a process as it requires it. This could potentially be done in stages rather than all or nothing.

(Currently unused as far as I know... the main problem is that borrowing memory needs to be coordinated.)

Huge pages

When OS processes use a large amount of memory, mapping virtual addresses to physical addresses can begin to take a significant proportion of the execution time. This is especially true once the TLB is not large enough to store all the mappings. Huge pages can significantly help with this problem by reducing the number of TLB entries needed to cover the virtual address space. The memory manager supports huge pages in two different ways:

Huge pages can be preallocated (e.g., with hugeadm) for exclusive use as huge pages. If huge pages are enabled for a particular engine, and sufficient huge pages are available to supply the memory for the memory manager, then they will be used.

Linux kernels from 2.6.38 onward have support for transparent huge pages. These do not need to be preallocated, instead the operating system tries to use them behind the scenes. HPCC version 5.2 and following takes advantage of this feature to significantly speed memory access up when large amounts of memory are used by each process.

Preallocated huge pages tend to be more efficient, but they have the disadvantage that the operating system currently does not reuse unused huge pages for other purposes e.g., disk cache.

There is also a memory manager option to not return the memory to the operating system when it is no longer required. This has the advantage of not clearing the memory whenever it is required again, but the same disadvantage as preallocated huge pages that the unused memory cannot be used for disk cache. We recommend this option is selected when preallocated huge pages are in use - until the kernel allows them to be reused.

Global memory and channels

Changes in 6.x allow Thor to run multiple channels within the same process. This allows data that is constant for all channels to be shared between all slave channels - a prime example is the rhs of a lookup join. For the queries to run efficiently the memory manager needs to ensure that each slave channel has the same amount of memory - especially when memory is being used that is shared between them.

createGlobalRowManager() allows a single global row manager to be created which also provides slave row managers for the different channels via the querySlaveRowManager(unsigned slave) method.

',61),s=[r];function n(l,h,c,d,m,p){return t(),a("div",null,s)}const g=e(i,[["render",n]]);export{f as __pageData,g as default}; diff --git a/assets/devdoc_MemoryManager.md.WzZxL7Sr.lean.js b/assets/devdoc_MemoryManager.md.WzZxL7Sr.lean.js new file mode 100644 index 00000000000..e9b85bad9aa --- /dev/null +++ b/assets/devdoc_MemoryManager.md.WzZxL7Sr.lean.js @@ -0,0 +1 @@ +import{_ as e,c as a,o as t,V as o}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"The Roxie Memory Manager","description":"","frontmatter":{"title":"The Roxie Memory Manager"},"headers":[],"relativePath":"devdoc/MemoryManager.md","filePath":"devdoc/MemoryManager.md","lastUpdated":1721825996000}'),i={name:"devdoc/MemoryManager.md"},r=o("",61),s=[r];function n(l,h,c,d,m,p){return t(),a("div",null,s)}const g=e(i,[["render",n]]);export{f as __pageData,g as default}; diff --git a/assets/devdoc_Metrics.md.ipIaUVOL.js b/assets/devdoc_Metrics.md.ipIaUVOL.js new file mode 100644 index 00000000000..7996797d4ec --- /dev/null +++ b/assets/devdoc_Metrics.md.ipIaUVOL.js @@ -0,0 +1,17 @@ +import{_ as e,c as t,o as i,V as s}from"./chunks/framework.gBlNPWt_.js";const k=JSON.parse('{"title":"Metrics Framework Design","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/Metrics.md","filePath":"devdoc/Metrics.md","lastUpdated":1721825996000}'),a={name:"devdoc/Metrics.md"},n=s(`

Metrics Framework Design

Introduction

This document describes the design of a metrics framework that allows HPCC Systems components to implement a metric collection strategy. Metrics provide the following functionality:

  • Alerts and monitoring

    An important DevOps function is to monitor the cluster and providing alerts when problems are detected. Aggregated metric values from multiple sources provide the necessary data to build a complete picture of cluster health that drives monitoring and alerts.

  • Scaling

    As described above, aggregated metric data is also used to dynamically respond to changing cluster demands and load. Metrics provide the monitoring capability to react and take action

  • Fault diagnosis and resource monitoring

    Metrics provide historical data useful in diagnosing problems by profiling how demand and usage patterns may change prior to a fault. Predictive analysis can also be applied.

  • Analysis of jobs/workunits and profiling

    With proper instrumentation, a robust dynamic metric strategy can track workunit processing. Internal problems with queries should be diagnosed from deep drill down logging.

The document consists of several sections in order to provide requirements as well as the design of framework components.

Definitions

Some definitions are useful.

Metric

: A measurement defined by a component that represents an internal state that is useful in a system reliability engineering function. In the context of the framework, a metric is an object representing the above.

Metric Value

: The current value of a metric.

Metric Updating

: The component task of updating metric state.

Collection

: A framework process of selecting relevant metrics based on configuration and then retrieving their values.

Reporting

: A framework process of converting values obtained during a collection into a format suitable for ingestion by a collection system.

Trigger

: What causes the collection of metric values.

Collection System

: The store for metric values generated during the reporting framework process.

Use Scenarios

This section describes how components expect to use the framework. It is not a complete list of all requirements but rather a sample.

Roxie

Roxie desires to keep a count of many different internal values. Some examples are

  • Disk type operations such as seeks and reads

  • Execution totals

    Need to track items such as total numbers of items such as success and failures as well as breaking some counts into individual reasons. For example, failures may need be categorized such as as

    • Busy
    • Timeout
    • Bad input

    Or even by priority (high, low, sla, etc.)

  • Current operational levels such as the length of internal queues

  • The latency of operations such as queue results, agent responses, and gateway responses

Roxie also has the need to track internal memory usage beyond the pod/system level capabilities. Tracking the state of its large fixed memory pool is necessary.

The Roxie buddy system also must track how often and who is completing requests. The "I Beat You To It" set of metrics must be collected and exposed in order to detect pending node failure. While specific action on these counts is not known up front, it appears clear that these values are useful and should be collected.

There does not appear to be a need for creating and destroying metrics dynamically. The set of metrics is most likely to be created at startup and remain active through the life of the Roxie. If, however, stats collection seeps into the metrics framework, dynamic creation and destruction of stats metrics is a likely requirement.

ESP

There are some interesting decisions with respect to ESP and collection of metrics. Different applications within ESP present different use cases for collection. Ownership of a given task drives some of these use cases. Take workunit queues. If ownership of the task, with respect to metrics, is WsWorkunits, then use cases are centric to that component. However, if agents listening on the queue are to report metrics, then a different set of use cases emerge. It is clear that additional work is needed to generate clear ownership of metrics gathered by ESP and/or the tasks it performs.

ESP needs to report the activeTransactions value from the TxSummary class(es). This gives an indication of how busy the ESP is in terms of client requests.

Direct measurement of response time in requests may not be useful since the type of request causes different execution paths within ESP that are expected to take widely varying amounts of time. Creation of metrics for each method is not recommended. However, two possible solutions are to a) create a metric for request types, or b) use a histogram to measure response time ranges. Another option mentioned redefines the meaning of a bucket in a histogram. Instead of a numeric distribution, each bucket represents a unique subtask within an overall "metric" representing a measured operation. This should be explored whether for operational or developmental purposes.

For tracking specific queries and their health, the feeling is that logging can accomplish this better than metrics since the list of queries to monitor will vary between clusters. Additionally, operational metrics solving the cases mentioned above will give a view into the overall health of ESP which will affect the execution of queries. Depending on actions taken by these metrics, scaling may solve overload conditions to keep cluster responsiveness acceptable.

For Roxie a workunit operates as a service. Measuring service performance using a histogram to capture response times as a distribution may be appropriate. Extracting the 95th percentile of response time may be useful as well.

There are currently no use cases requiring consistency between values of different metrics.

At this time the only concrete metric identified is the number of requests received. As the framework design progresses and ESP is instrumented, the list will grow.

Dali Use Cases

From information gathered, Dali plans to keep counts and rates for many of the items it manages.

Framework Design

This section covers the design and architecture of the framework. It discusses the main areas of the design, the interactions between each area, and an overall process model of how the framework operates.

The framework consists of three major areas: metrics, sinks, and the glue logic. These areas work together with the platform and the component to provide a reusable metrics collection function.

Metrics represent the quantifiable component state measurements used to track and assess the status of the component. Metrics are typically scalar values that are easily aggregated by a collection system. Aggregated values provide the necessary input to take component and cluster actions such as scaling up and down. The component is responsible for creating metrics and instrumenting the code. The framework provides the support for collecting and reporting the values. Metrics provide the following:

  • Simple methods for the component to update the metric
  • Simple methods for the framework to retrieve metric value(s)
  • Handling of all synchronization between updating and retrieving metric values

In addition, the framework provides the support for retrieving values so that the component does not participate in metric reporting. The component simply creates the metrics it needs, then instruments the component to update the metric whenever its state changes. For example, the component may create a metric that counts the total number of requests received. Then, wherever the component receives a request, a corresponding update to the count is added. Nowhere in the component is any code added to retrieve the count as that is handled by the framework.

Sinks provide a pluggable interface to hide the specifics of collection systems so that the metrics framework is independent of those dependencies. Sinks:

  • Operate independently of other sinks in the system
  • Convert metric native values into collection system specific measurements and reports
  • Drive the collection and reporting processes

The third area of the framework is the glue logic, referred to as the MetricsManager. It manages the metrics system for the component. It provides the following:

  • Handles framework initialization
  • Loads sinks as required
  • Manages the list of metrics for the component
  • Handles collection and reporting with a set of convenience methods used by sinks

The framework is designed to be instantiated into a component as part of its process and address space. All objects instantiated as part of the framework are owned by the component and are not shareable with any other component whether local or remote. Any coordination or consistency requirements that may arise in the implementation of a sink shall be the sole responsibility of the sink.

Framework Implementation

The framework is implemented within jlib. The following sections describe each area of the framework.

Metrics

Components use metrics to measure their internal state. Metrics can represent everything from the number of requests received to the average length some value remains cached. Components are responsible for creating and updating metrics for each measured state. The framework shall provide a set of metrics designed to cover the majority of component measurement requirements. All metrics share a common interface to allow the framework to manage them in a common way.

To meet the requirement to manage metrics independent of the underlying metric state, all metrics implement a common interface. All metrics then add their specific methods to update and retrieve internal state. Generally the component uses the update method(s) to update state and the framework uses retrieval methods to get current state when reporting. The metric insures synchronized access.

For components that already have an implementation that tracks a metric, the framework provides a way to instantiate a custom metric. The custom metric allows the component to leverage the existing implementation and give the framework access to the metric value for collection and reporting. Note that custom metrics only support simple scalar metrics such as a counter or a gauge.

Sinks

The framework defines a sink interface to support the different requirements of collection systems. Examples of collection systems are Prometheus, Datadog, and Elasticsearch. Each has different requirements for how and when measurements are ingested. The following are examples of different collection system requirements:

  • Polled vs Periodic
  • Single measurement vs multiple reports
  • Report format (JSON, text, etc.)
  • Push vs Pull

Sinks are responsible for two main functions: initiating a collection and reporting measurements to the collection system. The Metrics Reporter provides the support to complete these functions.

The sink encapsulates all of the collection system requirements providing a pluggable architecture that isolates components from these differences. The framework supports multiple sinks concurrently, each operating independently.

Instrumented components are not aware of the sink or sinks in use. Sinks can be changed without requiring changes to a component. Therefore, components are independent of the collection system(s) in use.

Metrics Reporter

The metrics reporter class provides all of the common functions to bind together the component, the metrics it creates, and the sinks to which measurements are reported. It is responsible for the following:

  • Initialization of the framework
  • Managing the metrics created by the component
  • Handling collection and reporting as directed by configured sinks

Metrics Implementations

The sections that follow discuss metric implementations.

Counter Metric

A counter metric is a monotonically increasing value that "counts" the total occurrences of some event. Examples include the number of requests received, or the number of cache misses. Once created, the component instruments the code with updates to the count whenever appropriate.

Gauge Metric

A gauge metric is a continuously updated value representing the current state of an interesting value in the component. For example, the amount of memory used in an internal buffer, or the number of requests waiting on a queue. A gauge metric may increase or decrease in value as needed. Reading the value of a gauge is a stateless operation in that there are no dependencies on the previous reading. The value returned shall always be the current state.

Once created, the component shall update the gauge anytime the state of what is measured is updated. The metric shall provide methods to increase and decrease the value. The sink reads the value during collection and reporting.

Custom Metric

A custom metric is a class that allows a component to leverage existing metrics. The component creates an instance of a custom metric (a templated class) and passes a reference to the underlying metric value. When collection is performed, the custom metric simply reads the value of the metric using the reference provided during construction. The component maintains full responsibility for updating the metric value as the custom metric class provides no update methods. The component is also responsible for ensuring atomic access to the value if necessary.

Histogram Metric

Records counts of measurements according to defined bucket limits. When created, the caller defines as set of bucket limits. During event recording, the component records measurements. The metric separates each recorded measurement into its bucket by testing the measurement value against each bucket limit using a less than or equal test. Each bucket contains a count of measurements meeting that criteria. Additionally, the metric maintains a default bucket for measurements outside of the maximum bucket limit. This is sometimes known as the "inf" bucket.

Some storage systems, such as Prometheus, require each bucket to accumulate its measurements with the previous bucket(s). It is the responsibility of the sink to accumulate values as needed.

Scaled Histogram Metric

A histogram metric that allows setting the bucket limit units in one domain, but take measurements in another domain. For example, the bucket limits may represent millisecond durations, yet it is more effecient to use execution cycles to take the measurements. A scaled histogram converts from the the measurement domain (cycles) to the limit units domain using a scale factor provided at initialization. All conversions are encapsulated in the scaled histogram class such that no external scaling is required by any consumer such as a sink.

Configuration

This section discusses configuration. Since Helm charts are capable of combining configuration data at a global level into a component's specific configuration, The combined configuration takes the form as shown below. Note that as the design progresses it is expected that there will be additions.

yaml
    component:
+      metrics:
+        sinks:
+        - type: <sink_type>
+          name: <sink name>
+          settings:
+            sink_setting1: sink_setting_value1
+            sink_setting2: sink_setting_value2

Where (based on being a child of the current component):

metrics

: Metrics configuration for the component

metrics.sinks

: List of sinks defined for the component (may have been combined with global config)

metrics.sinks[].type

: The type for the sink. The type is substituted into the following pattern to determine the lib to load: libhpccmetrics<type><shared_object_extension>

metrics.sinks[].name

: A name for the sink.

metrics.sinks[].settings

: A set of key/value pairs passed to the sink when initialized. It should contain information necessary for the operation of the sink. Nested YML is supported. Example settings are the prometheus server name, or the collection period for a periodic sink.

Metric Naming

Metric names shall follow a convention as outlined in this section. Because different collection systems have different requirements for how metric value reports are generated, naming is split into two parts.

First, each metric is given a base name that describes what the underlying value is. Second, meta data is assigned to each metric to further qualify the value. For example, a set of metrics may count the number of requests a component has received. Each metric would have the same base name, but meta data would separate types of request (GET vs POST), or disposition such as pass or fail.

Base Name

The following convention defines how metric names are formed:

  • Names consist of parts separated by a period (.)

  • Each part shall use snake case (allows for compound names in each part)

  • Each name shall begin with a prefix representing the scop of the metric

  • Names for metric types shall be named as follows (followed by examples):

    Gauges: <scope>.<plural-noun>.<state> esp.requests.waiting, esp.status_requests.waiting

    Counters: <scope>.<plural-noun>.<past-tense-verb> thor.requests.failed, esp.gateway_requests.queued

    Time: <scope>.<singular-noun>.<state or active-verb>.time dali.request.blocked.time, dali.request.process.time

Meta Data

Meta data further qualifies a metric value. This allows metrics to have the same name, but different scopes or categories. Generally, meta data is only used to furher qualify metrics that would have the same base name, but need further distinction. An example best describes a use case for meta data. Consider a component that accepts HTTP requests, but needs to track GET and POST requests separately. Instead of defining metrics with names post_requests.received and get_requests.received, the component creates two metrics with the base name requests.received and attaches meta data describing the request type of POST to one and GET to the other.

Use of meta data allows aggregating both types of requests into a single combined count of received requests while allowing a breakdown by type.

Meta data is represented as a key/value pair and is attached to the metric by the component during metric creation. The sink is responsible for converting meta data into useful information for the collection system during reporting.

The Component Instrumentation section covers how meta data is added to a metric.

Component Instrumentation

In order to instrument a component for metrics using the framework, a component must include the metrics header from jlib (jmetrics.hpp) and add jlib as a dependent lib (if not already doing so).

The general steps for instrumentation are

  1. Create a metrics reporter object
  2. Create metric objects for each internal state to measure and add each to the reporter
  3. Add updates to each metric throughout the component wherever metric state changes

The metrics reporter is a singleton created using the platform defined singleton pattern template. The component must obtain a reference to the reporter. Use the following example:

cpp
using namespace hpccMetrics;
+MetricsManager &metricsManager = queryMetricsManager();

Metrics are wrapped by a standard C++ shared pointer. The component is responsible for maintaining a reference to each shared pointer during the lifetime of the metric. The framework keeps a weak pointer to each metric and thus does not maintain a reference. The following is an example of creating a counter metric and adding it to the reporter. The using namespace eliminates the need to prefix all metrics types with hpccMetrics. Its use is assumed for all code examples that follow.

cpp
std::shared_ptr<CounterMetric> pCounter = std::make_shared<CounterMetric>("metricName", "description");
+metricsManager.add(pCounter);

Note the metric type for both the shared pointer variable and in the make_shared template that creates the metric and returns a shared pointer. Simply substitute other metric types and handle any differences in the constructor arguments as needed.

Once created, add updates to the metric state throughout the component code where required. Using the above example, the following line of code increments the counter metric by 1.

cpp
pCounter->inc(1);

Note that only a single line of code is required to update the metric.

That's it! There are no component requirements related to collection or reporting of metric values. That is handled by the framework and loaded sinks.

For convenience, there are function templates that handle creating the reporter, creating a metric, and adding the metric to the reporter. For example, the above three lines of code that created the reporter, a metric, and added it, can be replaced by the following:

auto pCount = createMetricAndAddToManager<CounterMetric>("metricName", "description");
+

For convenience a similar function template exists for creating custom metrics. For a custom metric the framework must know the metric type and have a reference to the underlying state variable. The following template function handles creating a custom metric and adding it to the reporter (which is created if needed as well):

auto pCustomMetric = createCustomMetricAndAddToManager("customName", "description", metricType, value);
+

Where:

  • metricType

    A defined metric type as defined by the MetricType enum.

  • value

    A reference to the underlying event state which must be a scalar value convertable to a 64bit unsigned integer (__uint64)

Adding Metric Meta Data

A component, depending on requirements, may attach meta data to further qualify created metrics. Meta data takes the form of key value pairs. The base metric class MetricBase constructor defines a parameter for a vector of meta data. Metric subclasses also define meta data as a constructor parameter, however an empty vector is the default. The IMetric interface defines a method for retrieving the meta data.

Meta data is order dependent.

Below are two examples of constructing a metric with meta data. One creates the vector and passes it as a parameter, the other constructs the vector in place.

cpp
MetricMetaData metaData1{{"key1", "value1"}};
+std::shared_ptr<CounterMetric> pCounter1 =
+    std::make_shared<CounterMetric>("requests.completed", "description", SMeasureCount, metaData1);
+
+std::shared_ptr<CounterMetric> pCounter2 =
+    std::make_shared<CounterMetric>("requests.completed", "description", SMeasureCount, MetricMetaData{{"key1", "value2"}});

Metric Units

Metric units are treated separately from the base name and meta data. The reason is to allow the sink to translate based on collection system requirements. The base framework provides a convenience method for converting units into a string. However, the sink is free to do any conversions, both actual units and the string representation, as needed.

Metric units are defined using a subset of the StaticsMeasure enumeration values defined in jstatscodes.h. The current values are used:

  • SMeasureTimeNs - A time measurement in nanoseconds
  • SMeasureCount - A count of events
  • SMeasureSize - Size in bytes
`,132),r=[n];function o(l,h,c,p,d,m){return i(),t("div",null,r)}const g=e(a,[["render",o]]);export{k as __pageData,g as default}; diff --git a/assets/devdoc_Metrics.md.ipIaUVOL.lean.js b/assets/devdoc_Metrics.md.ipIaUVOL.lean.js new file mode 100644 index 00000000000..0068151566c --- /dev/null +++ b/assets/devdoc_Metrics.md.ipIaUVOL.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as i,V as s}from"./chunks/framework.gBlNPWt_.js";const k=JSON.parse('{"title":"Metrics Framework Design","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/Metrics.md","filePath":"devdoc/Metrics.md","lastUpdated":1721825996000}'),a={name:"devdoc/Metrics.md"},n=s("",132),r=[n];function o(l,h,c,p,d,m){return i(),t("div",null,r)}const g=e(a,[["render",o]]);export{k as __pageData,g as default}; diff --git a/assets/devdoc_NewFileProcessing.md.sdNSfUu7.js b/assets/devdoc_NewFileProcessing.md.sdNSfUu7.js new file mode 100644 index 00000000000..1b2ac1a63c1 --- /dev/null +++ b/assets/devdoc_NewFileProcessing.md.sdNSfUu7.js @@ -0,0 +1,14 @@ +import{_ as e,c as t,o as a,V as i}from"./chunks/framework.gBlNPWt_.js";const u=JSON.parse('{"title":"Storage planes","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/NewFileProcessing.md","filePath":"devdoc/NewFileProcessing.md","lastUpdated":1721825996000}'),o={name:"devdoc/NewFileProcessing.md"},r=i(`

Documentation about the new file work.

YAML files. The following are the YAML definitions which are used to serialize file information from dali/external store to the engines and if necessary to the worker nodes.

Storage planes

This is already covered in the deployed helm charts. It has been extended and rationalized slightly.

storage:

: hostGroups: - name: <required> hosts: [ .... ] - name: <required> hostGroup: <name> count: <unsigned:#hosts> # how many hosts within the host group are used ?(default is number of hosts) offset: <unsigned:0> # index of first host included in the derived group delta: <unsigned:0> # first host within the range[offset..offset+count-1] in the derived group

planes:
+
+:   name: \\<required\\> prefix: \\<path\\> \\# Root directory for
+    accessing the plane (if pvc defined), or url to access plane.
+    numDevices: 1 \\# number of devices that are part of the plane
+    hostGroup: \\<name\\> \\# Name of the host group for bare metal
+    hosts: \\[ host-names \\] \\# A list of host names for bare metal
+    secret: \\<secret-id\\> \\# what secret is required to access the
+    files. options: \\# not sure if it is needed
+

Changes: * The replication information has been removed from the storage plane. It will now be specified on the thor instance indicating where (if anywhere) files are replicated. * The hash character (#) in a prefix or a secret name will be substituted with the device number. This replaces the old includeDeviceInPath property. This allows more flexible device substition for both local mounts and storage accounts. The number of hashes provides the default padding for the device number. (Existing Helm charts will need to be updated to follow these new rules.) * Neither thor or roxie replication is special cased. They are represented as multiple locations that the file lives (see examples below). Existing baremetal environments would be mapped to this new representation with implicit replication planes. (It is worth checking the mapping to roxie is fine.)

Files

file: - name: <logical-file-name> format: <type> # e.g. flat, csv, xml, key, parquet meta: <binary> # (opt) format of the file, (serialized hpcc field information). metaCrc: <unsigned> # hash of the meta numParts # How many file parts. singlePartNoSuffix: <boolean> # Does a single part file include .part_1_of_1? numRows: # total number of rows in the file (if known) rawSize: # total uncompressed size diskSize # is this useful? when binary copying? planes: [] # list of storage planes that the file is stored on. tlk: # ???Should the tlk be stored in the meta and returned? splitType: <split-format> # Are there associated split points, and if so what format? (And if so what variant?)

#options relating to the format of the input file:

: grouped: <boolean> # is the file grouped? compressed: <boolean> blockCompressed: <boolean> formatOptions: # Any options that relate to the file format e.g. csvTerminator. These are nested because they can be completely free format recordSize: # if a fixed size record. Not really sure it is useful

part: \\# optional information about each of the file parts (Cannot
+implement virtual file position without this) - numRows: \\<count\\>
+\\# number of rows in the file part rawSize: \\<size\\> \\# uncompressed
+size of the file part diskSize: \\<size\\> \\# size of the part on disk
+

# extra fields that are used to return information from the file lookup service

missing: <boolean> # true if the file could not be found external: <boolean> # filename of the form external:: or plane:

If the information needs to be signed to be passed to dafilesrv for example, the entire structure of (storage, files) is serialized, and compressed, and that then signed.

Functions

Logically executed on the engine, and retrived from dali or in future versions from an esp service (even if for remote reads).

GetFileInfomation(<logical-filename>, <options>)

The logical-filename can be any logical name - including a super file, or an implicit superfile.

options include: * Are compressed sizes needed? * Are signatures required? * Is virtual fileposition (non-local) required? * name of the user

This returns a structure that provides information about a list of files

meta:

: hostGroups: storage: files: secrets: #The secret names are known, how do we know which keys are required for those secrets?

Some key questions: * Should the TLK be in the dali meta information? [Possibly, but not in first phase. ] * Should the split points be in the dali meta information? [Probably not, but the meta should indicate whether they exist, and if so what format they are. ] * Super files (implicit or explicit) can contain the same file information more than once. Should it be duplicated, or have a flag to indicate a repeat. [I suspect this is fairly uncommon, so duplication would be fine for the first version.] * What storage plane information is serialized back? [ all is simplest. Can optimize later. ]

NOTE: This doesn't address the question of writing to a disk file...


Local class for interpreting the results. Logically executed on the manager, and may gather extra information that will be serialized to all workers. The aim is that the same class implementations are used by all the engines (and fileview in esp).

MasterFileCollection : RemoteFileCollection : FileCollection(eclReadOptions, eclFormatOptions, wuid, user, expectedMeta, projectedMeta); MasterFileCollection //Master has access to dali RemoteFileCollection : has access to remote esp // think some more

FileCollection::GatherFileInformation(<logical-filename>, gatherOptions); - potentially called once per query. - class is responsible for optimizing case where it matches the previous call (e.g. in a child query). - possibly responsible for retrieving the split points ()

Following options are used to control whether split points are retrieved when file information is gathered * number of channels reading the data? * number of strands reading each channel? * preserve order?

gatherOptions: * is it a temporary file?

This class serializes all information to every worker, where it is used to recereate a copy of the master filecollection. This will contain information derived from dali, and locally e.g. options specified in the activity helper. Each worker has a complete copy of the file information. (This is similar to dafilesrv with security tokens.)

The files that are actually required by a worker are calculated by calling the following function. (Note the derived information is not serialized.)

FilePartition FileCollection::calculatePartition(numChannels, partitionOptions)

partitionOptions: * number of channels reading the data? * number of strands reading each channel? * which channel? * preserve order? * myIP

A file partition contains a list of file slices:

class FileSlice (not serialized) { IMagicRowStream * createRowStream(filter, ...); // MORE! File * logicalFile; offset_t startOffset; offset_t endOffset; };

Things to bear in mind: - Optimize same file reused in a child query (filter likely to change) - Optimize same format reused in a child query (filename may be dynamic) - Intergrating third party file formats and distributed file systems may require extra information. - optimize reusing the format options. - ideally fail over to a backup copy midstream.. and retry in failed read e.g. if network fault

Examples

Example definition for a thor400, and two thor200s on the same nodes:

hostGroup: - name: thor400Group host: [node400_01,node400_02,node400_03,...node400_400]

storage:

: planes: #Simple 400 way thor - name: thor400 prefix: /var/lib/HPCCSystems/thor400 hosts: thor400Group #The storage plane used for replicating files on thor. - name: thor400_R1 prefix: /var/lib/HPCCSystems/thor400 hosts: thor400Group offset: 1 # A 200 way thor using the first 200 nodes as the thor 400 - name: thor200A prefix: /var/lib/HPCCSystems/thor400 hosts: thor400Group size: 200 # A 200 way thor using the second 200 nodes as the thor 400 - name: thor200B prefix: /var/lib/HPCCSystems/thor400 hosts: thor400Group size: 200 start: 200 # The replication plane for a 200 way thor using the second 200 nodes as the thor 400 - name: thor200B_R1 prefix: /var/lib/HPCCSystems/thor400 hosts: thor400Group size: 200 start: 200 offset: 1 # A roxie storage where 50way files are stored on a 100 way roxie - name: roxie100 prefix: /var/lib/HPCCSystems/roxie100 hosts: thor400Group size: 50 # The replica of the roxie storage where 50way files are stored on a 100 way roxie - name: roxie100_R1 prefix: /var/lib/HPCCSystems/thor400 hosts: thor400Group start: 50 size: 50

device = (start + (part + offset) % size;

size <= numDevices offset < numDevices device <= numDevices;

There is no special casing of roxie replication, and each file exists on multiple storage planes. All of these should be considered when determining which is the best copy to read from a particular engine node.

Creating storage planes from an existing systems [implemented]

Milestones:

a) Create baremetal storage planes [done]

b) [a] Start simplifying information in dali meta (e.g. partmask, remove full path name) c) [a] Switch reading code to use storageplane away from using dali path and environment paths - in ALL disk reading and writing code - change numDevices so it matches the container d) [c] Convert dali information from using copies to multiple groups/planese) [a] Reimplement the current code to create an IPropertyTree from dali file information (in a form that can be reused in dali) *f) [e] Refactor existing PR to use data in an IPropertyTree and cleanly separate the interfaces. g) Switch hthor over to using the new classes by default and work through all issues h) Refactor stream reading code. Look at the spark interfaces for inspiration/compatibility i) Refactor disk writing code into common class? j) [e] create esp service for accessing meta information k) [h] Refactor and review azure blob code l) [k] Re-implement S3 reading and writing code.

m) Switch fileview over to using the new classes. (Great test they can be used in another context + fixes a longstanding bug.)

) Implications for index reading? Will they end up being treated as a normal file? Don't implement for 8.0, although interface may support it.

*) My primary focus for initial work.

File reading refactoring

Buffer sizes: - storage plane specifies an optimal reading minimum - compression may have a requirement - the use for the data may impose a requirement e.g. a subset of the data, or only fetching a single record

  • parallel disk reading may want to read a big chunk, but then process in sections. groan.

Look at lambda functions to create split points for a file. Can we use the java classes to implement it on binary files (and csv/xml)?

****************** Reading classes and meta information****************** meta comes from a combination of the information in dfs and the helper

The main meta information uses the same structure that is return by the function that returns file infromation from dali. The format specific options are contained in a nested attribute so they can be completely arbitrary

The helper class also generates a meta structure. Some options fill in root elements - e.g. compressed. Some fill in a new section (hints: @x=y). The format options are generated from the paramaters to the dataset format.

note normally there is only a single (or very few) files, so merging isn't too painful. queryMeta() queryOptions() rename meta to format? ???

DFU server

Where does DFUserver fit in in a container system?

DFU has the following main functionality in a bare metal system: a) Spray a file from a 1 way landing zone to an N-way thor b) Convert file format when spraying. I suspect utf-16->utf8 is the only option actually used. c) Spray multiple files from a landing zone to a single logical file on an N-way thor d) Copy a logical file from a remote environment e) Despray a logical file to an external landing zone. f) Replicate an existing logical file on a given group. g) Copy logical files between groups h) File monitoring i) logical file operations j) superfile operations

ECL has the ability to read a logical file directly from a landingzone using 'FILE::<ip>' file syntax, but I don't think it is used very frequently.

How does this map to a containerized system? I think the same basic operations are likely to be useful. a) In most scenarios Landing zones are likely to be replaced with (blob) storage accounts. But for security reasons these are likely to remain distinct from the main location used by HPCC to store datasets. (The customer will have only access keys to copy files to and from those storage accounts.) The containerized system has a way for ECL to directly read from a blob storage account ('PLANE::<plane'), but I imagine users will still want to copy the files in many situations to control the lifetime of the copies etc. b) We still need a way to convert from utf16 to utf8, or extend the platform to allow utf16 to be read directly. c) This is still equally useful, allowing a set of files to be stored as a single file in a form that is easy for ECL to process. d) Important for copying data from an existing bare metal system to the cloud, and from a cloud system back to a bare metal system. e) Useful for exporting results to customers f+g) Essentially the same thing in the cloud world. It might still be useful to have h) I suspect we will need to map this to cloud-specific apis. i+j) Just as applicable in the container world.

Broadly, landing zones in bare metal map to special storage planes in containerized, and groups also map to more general storage planes.

There are a couple of complications connected with the implementation:

  1. Copying is currently done by starting an ftslave process on either the source or the target nodes. In the container world there is no local node, and I think we would prefer not to start a process in order to copy each file. 2) Copying between storage groups should be done using the cloud provider api, rather than transferring data via a k8s job.

Suggestions:

  • Have a load balanced dafilesrv which supports multiple replicas. It would have a secure external service, and an internal service for trusted components.
  • Move the ftslave logic into dafilesrv. Move the current code for ftslave actions into dafilesrv with new operations.
  • When copying from/to a bare metal system the requests are sent to the dafilesrv for the node that currently runs ftslave. For a container system the requests are sent to the loadbalanced service.
  • It might be possible to migrate to lamda style functions for some of the work...
  • A later optimization would use a cloud service where it was possible.
  • When local split points are supported it may be better to spray a file 1:1 along with partition information. Even without local split points it may still be better to spray a file 1:1 (cheaper).
  • What are the spray targets? It may need to be storage plane + number of parts, rather than a target cluster. The default number of parts is the #devices on the storage plane.

=> Milestones a) Move ftslave code to dafilesrv (partition, pull, push) [Should be included in 7.12.x stream to allow remote read compatibility?] b) Create a dafilesrv component to the helm charts, with internal and external services. c) use storage planes to determine how files are sprayed etc. (bare-metal, #devices) Adapt dfu/fileservices calls to take (storageplane,number) instead of cluster. There should already be a 1:1 mapping from existing cluster to storage planes in a bare-metal system, so this may not involve much work. [May also need a flag to indicate if ._1_of_1 is appended?] d) Select correct dafilesrv for bare-metal storage planes, or load balanced service for other. (May need to think through how remote files are represented.)

=> Can import from a bare metal system or a containerized system using command line??

: NOTE: Bare-metal to containerized will likely need push operations on the bare-metal system. (And therefore serialized security information) This may still cause issues since it is unlikely containerized will be able to pull from bare-metal. Pushing, but not creating a logical file entry on the containerized system should be easier since it can use a local storage plane definition.

e) Switch over to using the esp based meta information, so that it can include details of storage planes and secrets.

: [Note this would also need to be in 7.12.x to allow remote export to containerized, that may well be a step too far]

f) Add option to configure the number of file parts for spray/copy/despray g) Ensure that eclwatch picks up the list of storage planes (and the default number of file parts), and has ability to specify #parts.

Later: h) plan how cloud-services can be used for some of the copies i) investigate using serverless functions to calculate split points. j) Use refactored disk read/write interfaces to clean up read and copy code. k) we may not want to expose access keys to allow remote reads/writes - in which they would need to be pushed from a bare-metal dafilesrv to a containerized dafilesrv.

Other dependencies: * Refactored file meta information. If this is switching to being plane based, then the meta information should also be plane based. Main difference is not including the path in the meta information (can just be ignored) * esp service for getting file information. When reading remotely it needs to go via this now...

`,80),s=[r];function n(l,p,h,c,d,f){return a(),t("div",null,s)}const g=e(o,[["render",n]]);export{u as __pageData,g as default}; diff --git a/assets/devdoc_NewFileProcessing.md.sdNSfUu7.lean.js b/assets/devdoc_NewFileProcessing.md.sdNSfUu7.lean.js new file mode 100644 index 00000000000..f64cab1aa5c --- /dev/null +++ b/assets/devdoc_NewFileProcessing.md.sdNSfUu7.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as a,V as i}from"./chunks/framework.gBlNPWt_.js";const u=JSON.parse('{"title":"Storage planes","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/NewFileProcessing.md","filePath":"devdoc/NewFileProcessing.md","lastUpdated":1721825996000}'),o={name:"devdoc/NewFileProcessing.md"},r=i("",80),s=[r];function n(l,p,h,c,d,f){return a(),t("div",null,s)}const g=e(o,[["render",n]]);export{u as __pageData,g as default}; diff --git a/assets/devdoc_README.md.bTEFReKq.js b/assets/devdoc_README.md.bTEFReKq.js new file mode 100644 index 00000000000..40fc5a77e68 --- /dev/null +++ b/assets/devdoc_README.md.bTEFReKq.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as a,V as i}from"./chunks/framework.gBlNPWt_.js";const p=JSON.parse('{"title":"Developer Documentation","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/README.md","filePath":"devdoc/README.md","lastUpdated":1721825996000}'),o={name:"devdoc/README.md"},r=i('

Developer Documentation

This directory contains the documentation specifically targeted at developers of the HPCC system.

TIP

These documents are generated from Markdown by VitePress. See VitePress Markdown for more details.

General documentation

Implementation details for different parts of the system

  • Workunit Workflow: An explanation of workunits, and a walk-through of the steps in executing a query.
  • Code Generator: Details of the internals of eclcc.
  • Roxie: History and design details for roxie.
  • Memory Manager: Details of the memory manager (roxiemem) used by the query engines.
  • Metrics: Metrics Framework Design.

Other documentation

The ECL language is documented in the ecl language reference manual (generated as ECLLanguageReference-<version>.pdf).

',9),n=[r];function l(s,d,m,h,c,u){return a(),t("div",null,n)}const g=e(o,[["render",l]]);export{p as __pageData,g as default}; diff --git a/assets/devdoc_README.md.bTEFReKq.lean.js b/assets/devdoc_README.md.bTEFReKq.lean.js new file mode 100644 index 00000000000..c613eb8a0b1 --- /dev/null +++ b/assets/devdoc_README.md.bTEFReKq.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as a,V as i}from"./chunks/framework.gBlNPWt_.js";const p=JSON.parse('{"title":"Developer Documentation","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/README.md","filePath":"devdoc/README.md","lastUpdated":1721825996000}'),o={name:"devdoc/README.md"},r=i("",9),n=[r];function l(s,d,m,h,c,u){return a(),t("div",null,n)}const g=e(o,[["render",l]]);export{p as __pageData,g as default}; diff --git a/assets/devdoc_SecurityConfig.md.z0iGl4iN.js b/assets/devdoc_SecurityConfig.md.z0iGl4iN.js new file mode 100644 index 00000000000..def76c244f1 --- /dev/null +++ b/assets/devdoc_SecurityConfig.md.z0iGl4iN.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as r,V as a}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"Security Configuration","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/SecurityConfig.md","filePath":"devdoc/SecurityConfig.md","lastUpdated":1721825996000}'),o={name:"devdoc/SecurityConfig.md"},n=a('

Security Configuration

This document covers security configuration values and meanings. It does not serve as the source for how to configure security, but rather what the different values mean. These are not covered in the docs nor does any reasonable help information exist in the config manager or yaml files.

Supported Configurations

Security is configured either through an LDAP server or a plugin. Additionally, these are supported in both legacy deployments that use environment.xml and containerized deployments using Kubernetes and Helm charts. While these methods differ, the configuration values remain the same. Focus is placed on the different values and not the deployment target. Differences based on deployment can be found in the relevant platform documents.

Security Managers

Security is implemented via a security manager interface. Managers are loaded and used by components within the system to check authorization and authentication. LDAP is an exception to the loadable manager model. It is not a compliant loadable module like other security plugins. For that reason, the configuration for each is separated into two sections below: LDAP and Plugin Security Managers.

LDAP

LDAP is a protocol that connects to an Active Directory server (AD). The term LDAP is used interchangeably with AD. Below are the configuration values for an LDAP connection. These are valid for both legacy (environment.xml) and containerized deployments. For legacy deployments the configuration manager is the primary vehicle for setting these values. However, some values are not available through the tool and must be set manually in the environment.xml if needed for a legacy deployment.

In containerized environments, a LDAP configuration block is required for each component. Currently, this results in a verbose configuration where much of the information is repeated.

LDAP is capable if handling user authentication and feature access authorization (such as filescopes).

ValueExampleMeaning
adminGroupNameHPCCAdminsGroup name containing admin users for the AD
cacheTimeout60Timeout in minutes to keep cached security data
ldapCipherSuiteN/AUsed when AD is not up to date with latest SSL libs.
AD admin must provide
ldapPort389 (default)Insecure port
ldapSecurePort636 (default)Secure port over TLS
ldapProtocolldapldap for insecure (default), using ldapPort
ldaps for secure using ldapSecurePort
ldapTimeoutSec60 (default 5 for debug, 60 otherwise)Connection timeout to an AD before rollint to next AD
serverTypeActiveDirectoryIdentifies the type of AD server. (2)
filesBasednou=files,ou=ecl_kr,DC=z0lpf,DC=onmicrosoft,DC=comDN where filescopes are stored
groupsBasednou=groups,ou=ecl_kr,DC=z0lpf,DC=onmicrosoft,DC=comDN where groups are stored
modulesBaseDnou=modules,ou=ecl_kr,DC=z0lpf,DC=onmicrosoft,DC=comDN where permissions for resource are stored (1)
systemBasednOU=AADDC Users,DC=z0lpf,DC=onmicrosoft,DC=comDN where the system user is stored
usersBasednOU=AADDC Users,DC=z0lpf,DC=onmicrosoft,DC=comDN where users are stored (3)
systemUserhpccAdminAppears to only be used for IPlanet type ADs, but may still be required
systemCommonNamehpccAdminAD username of user to proxy all AD operations
systemPasswordSystem user passwordAD user password
ldapAdminSecretKeynoneKey for Kubernetes secrets (4) (5)
ldapAdminVaultIdnoneVault ID used to load system username and password (5)
ldapDomainnoneAppears to be a comma separated version of the AD domain name components (5)
ldapAddress192.168.10.42IP address to the AD
commonBasednDC=z0lpf,DC=onmicrosoft,DC=comOverrides the domain retrieved from the AD for the system user (5)
templateNamenoneTemplate used when adding resources (5)
authMethodnoneNot sure yet

Notes:

  1. modulesBaseDn is the same as resourcesBaseDn The code looks for first for modulesBaseDn and if not found will search for resourcesBaseDn
  2. Allowed values for serverType are ActiveDirectory, AzureActiveDirectory, 389DirectoryServer, OpenLDAP, Fedora389
  3. For AzureAD, users are managed from the AD dashboard, not via ECLWatch or through LDAP
  4. If present, ldapAdminVaultId is read and systemCommonName and systemPassword are read from the Kubernetes secrets store and not from the LDAP config values
  5. Must be configured manually in the environment.xml in legacy environments

Plugin Security Managers

Plugin security managers are separate shared objects loaded and initialized by the system. The manager interface is passed to components in order to provide necessary security functions. Each plugin has its own configuration. HPCC components can be configured to use a plugin as needed.

httpasswd Security Manager

See documentation for the settings and how to enable.

Single User Security Manager

To be added.

JWT Security Manager

To be added

',21),d=[n];function s(i,u,c,l,m,h){return r(),t("div",null,d)}const g=e(o,[["render",s]]);export{f as __pageData,g as default}; diff --git a/assets/devdoc_SecurityConfig.md.z0iGl4iN.lean.js b/assets/devdoc_SecurityConfig.md.z0iGl4iN.lean.js new file mode 100644 index 00000000000..49cc9555d22 --- /dev/null +++ b/assets/devdoc_SecurityConfig.md.z0iGl4iN.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as r,V as a}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"Security Configuration","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/SecurityConfig.md","filePath":"devdoc/SecurityConfig.md","lastUpdated":1721825996000}'),o={name:"devdoc/SecurityConfig.md"},n=a("",21),d=[n];function s(i,u,c,l,m,h){return r(),t("div",null,d)}const g=e(o,[["render",s]]);export{f as __pageData,g as default}; diff --git a/assets/devdoc_SecurityUserAuthentication.md.q0qlsyXA.js b/assets/devdoc_SecurityUserAuthentication.md.q0qlsyXA.js new file mode 100644 index 00000000000..fda151ba7f0 --- /dev/null +++ b/assets/devdoc_SecurityUserAuthentication.md.q0qlsyXA.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as a,V as i}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"User Authentication","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/SecurityUserAuthentication.md","filePath":"devdoc/SecurityUserAuthentication.md","lastUpdated":1721825996000}'),s={name:"devdoc/SecurityUserAuthentication.md"},r=i('

User Authentication

This document covers user authentication, the process of verifying the identity of the user. Authorization is a separate topic covering whether a user should be allowed to perform a specific operation of access a specific resource.

Each supported security manager is covered.

Generally, when authentication is needed, the security manager client should call the ISecManagerauthenticateUser method. The method also allows the caller to detect if the user being authenticated is a superuser. Use of that feature is beyond the scope of this document. In practice, this method is rarely if ever called. User authentication is generally performed as part of authorization. This is covered in more detail below.

Security Manager User Authentication

This section covers how each supported security manager handles user authentication. As stated above, the method authenticateUser is defined for this purpose. However, other methods also perform user authentication. The sections that follow describe in general how each security manager performs user authentication, whether from directly calling the authenticateUser method, or as an ancillary action taken when another method is called.

LDAP

The LDAP security manager uses the configured Active Directory to authenticate users. Once authenticated, the user is added to the permissions cache, if enabled, to prevent repeated trips to the AD whenever an authentication check is required.

If caching is enabled, a lookup is done to see if the user is already cached. If so, the cached user authentication status is returned. Note that the cached status remains until either the cache time to live expires or is cleared either manually or through some other programmatic action.

If caching is not enabled, a request is sent to the AD to validate the user credentials.

In either case, if digital signatures are configured, the user is also digitally signed using the username. Digitally signing the user allows for quick authentication by validating the signature against the username. During initial authentication, if the digital signature exists, it is verified to provide a fast way to authenticate the user. If the signature is not verified, the user is marked as not authenticated.

Authentication status is stored in the security user object so that further checks are not necessary when the same user object is used in multiple calls to the security manager.

HTPasswd

Authentication in the htpasswd manager does not support singularly authenticating the user without also authorizing resource access. See the special case for authentication with authorization below.

Regardless, the htpasswd manager authenticates users using the .htpasswd file that is installed on the cluster. It does so by finding the user in the file and verifying that the input hashed password matches the stored hashed password in file.

Single User

The single user security manager allows the definition of a single username with a password. The values are set in the environment configuration and are read during the initialization of the manager. All authentication requests validate against the configured username and password. The process is a simple comparison. Note that the password stored in the environment is hashed.

User Authentication During Authorization

Since resource access authorization requires an authenticated user, the authorization process also authenticates the user before checking authorization. There are a couple of advantages to this

  • Two separate calls are not required to check authorization (one to verify the user and one to check authorization)
  • The caller can perform third party authorization for specific user access

The authenticate method, or any of its overloads or derivatives, accepts a resource or resource list and a user. These methods authenticate the user first before checking access to the specified resource.

ECL Watch uses user authentication during authorization during its log in process. Instead of first authenticating the user, it calls an authenticate method passing both the user and the necessary resources for which the user must have access in order to log into ECL Watch.

',22),n=[r];function o(h,c,u,d,l,p){return a(),t("div",null,n)}const g=e(s,[["render",o]]);export{f as __pageData,g as default}; diff --git a/assets/devdoc_SecurityUserAuthentication.md.q0qlsyXA.lean.js b/assets/devdoc_SecurityUserAuthentication.md.q0qlsyXA.lean.js new file mode 100644 index 00000000000..fcce1eceb13 --- /dev/null +++ b/assets/devdoc_SecurityUserAuthentication.md.q0qlsyXA.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as a,V as i}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"User Authentication","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/SecurityUserAuthentication.md","filePath":"devdoc/SecurityUserAuthentication.md","lastUpdated":1721825996000}'),s={name:"devdoc/SecurityUserAuthentication.md"},r=i("",22),n=[r];function o(h,c,u,d,l,p){return a(),t("div",null,n)}const g=e(s,[["render",o]]);export{f as __pageData,g as default}; diff --git a/assets/devdoc_StyleGuide.md.Mjr9vf6y.js b/assets/devdoc_StyleGuide.md.Mjr9vf6y.js new file mode 100644 index 00000000000..7689330c776 --- /dev/null +++ b/assets/devdoc_StyleGuide.md.Mjr9vf6y.js @@ -0,0 +1,51 @@ +import{_ as s,c as e,o as i,V as a}from"./chunks/framework.gBlNPWt_.js";const u=JSON.parse('{"title":"Coding conventions","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/StyleGuide.md","filePath":"devdoc/StyleGuide.md","lastUpdated":1721825996000}'),t={name:"devdoc/StyleGuide.md"},n=a(`

Coding conventions

Why coding conventions?

Everyone has their own ideas of what the best code formatting style is, but most would agree that code in a mixture of styles is the worst of all worlds. A consistent coding style makes unfamiliar code easier to understand and navigate.

In an ideal world, the HPCC sources would adhere to the coding standards described perfectly. In reality, there are many places that do not. These are being cleaned up as and when we find time.

C++ coding conventions

Unlike most software projects around, HPCC has some very specific constraints that makes most basic design decisions difficult, and often the results are odd to developers getting acquainted with its code base. For example, when HPCC was initially developed, most common-place libraries we have today (like STL and Boost) weren't available or stable enough at the time.

Also, at the beginning, both C++ and Java were being considered as the language of choice, but development started with C++. So a C++ library that copied most behaviour of the Java standard library (At the time, Java 1.4) was created (see jlib below) to make the transition, if ever taken, easier. The transition never happened, but the decisions were taken and the whole platform is designed on those terms.

Most importantly, the performance constraints in HPCC can make no-brainer decisions look impossible in HPCC. One example is the use of traditional smart pointers implementations (such as boost::shared_ptr or C++'s auto_ptr), that can lead to up to 20% performance hit if used instead of our internal shared pointer implementation.

The last important point to consider is that some libraries/systems were designed to replace older ones but haven't got replaced yet. There is a slow movement to deprecate old systems in favour of consolidating a few ones as the elected official ways to use HPCC (Thor, Roxie) but old systems still could be used for years in tests or legacy sub-systems.

In a nutshell, expect re-implementation of well-known containers and algorithms, expect duplicated functionality of sub-systems and expect to be required to use less-friendly libraries for the sake of performance, stability and longevity.

For the most part out coding style conventions match those described at http://geosoft.no/development/cppstyle.html, with a few exceptions or extensions as noted below.

Source files

We use the extension .cpp for C++ source files, and .h or .hpp for header files. Header files with the .hpp extension should be used for headers that are internal to a single library, while header files with the .h extension should be used for the interface that the library exposes. There will typically be one .h file per library, and one .hpp file per cpp file.

Source file names within a single shared library should share a common prefix to aid in identifying where they belong.

Header files with extension .ipp (i for internal) and .tpp (t for template) will be phased out in favour of the scheme described above.

Java-style

We adopted a Java-like inheritance model, with macro substitution for the basic Java keywords. This changes nothing on the code, but make it clearer for the reader on what's the recipient of the inheritance doing with it's base.

  • interface (struct): declares an interface (pure virtual class)
  • extends (public): One interface extending another, both are pure virtual
  • implements (public): Concrete class implementing an interface

There is no semantic check, which makes it difficult to enforce such scheme, which has led to code not using it intermixed with code using it. You should use it when possible, most importantly on code that already uses it.

We also tend to write methods inline, which matches well with C++ Templates requirements. We, however, do not enforce the one-class-per-file rule.

See the Interfaces section for more information on our implementation of interfaces.

Identifiers

Class and interface names are in CamelCase with a leading capital letter. Interface names should be prefixed capital I followed by another capital. Class names may be prefixed with a C if there is a corresponding I-prefixed interface name, e.g. when the interface is primarily used to create an opaque type, but need not be otherwise.

Variables, function and method names, and parameters use camelCase starting with a lower case letter. Parameters may be prefixed with underscore when the parameter is used to initialize a member variable of the same name. Common cases are constructors and setter methods.

Example:

cpp
class MySQLSuperClass
+{
+    bool haslocalcopy = false;
+    void mySQLFunctionIsCool(int _haslocalcopy, bool enablewrite)
+    {
+        if (enablewrite)
+            haslocalcopy = _haslocalcopy;
+    }
+};

Pointers

Use real pointers when you can, and smart pointers when you have to. Take extra care on understanding the needs of your pointers and their scope. Most programs can afford a few dangling pointers, but a high-performance clustering platform cannot.

Most importantly, use common sense and a lot of thought. Here are a few guidelines:

  • Use real pointers for return values, parameter passing.
  • For .md variables use real pointers if their lifetime is guaranteed to be longer than the function (and no exception is thrown from functions you call), shared pointers otherwise.
  • Use Shared pointers for member variables - unless there is a strong guarantee the object has a longer lifetime.
  • Create Shared<X> with either:
    • Owned<X>: if your new pointer will take ownership of the pointer
    • Linked<X>: if you are sharing the ownership (shared)

Warning: Direct manipulation of the ownership might cause Shared<> pointers to lose the pointers, so subsequent calls to it (like o2->doIt() after o3 gets ownership) will cause segmentation faults.

Refer to [Reference counted objects]{.title-ref} for more information on our smart pointer implementation, Shared<>.

Methods that return pointers to link counted objects, or that use them, should use a common naming standard:

  • Foo * queryFoo() Does not return a linked pointer since lifetime is guaranteed for a set period. Caller should link if it needs to retain it for longer.
  • Foo * getFoo() Returned value is linked and should be assigned to an owned, or returned directly.
  • void setFoo(Foo * x) Generally parameters to functions are assumed to be owned by the caller, the callee needs to link them if they are retained.
  • void setFoo(Foo * ownedX) Some calls do transfer ownership of parameters - the parameter should be named to indicate this. If the function only has a single signficant parameter then sometimes the name of the function indicates the ownership.

Indentation

We use 4 spaces to indent each level. TAB characters should not be used.

The { that starts a new scope and the corresponding } to close it are placed on a new line by themselves, and are not indented. This is sometimes known as the Allman or ANSI style.

Comments

We generally believe in the philosophy that well written code is self-documenting. Comments are also encouraged to describe why something is done, rather than how - which should be clear from the code.

javadoc-formatted comments for classes and interfaces are being added.

Classes

The virtual keyword should be included on the declaration of all virtual functions - including those in derived classes, and the override keyword should be used on all virtual functions in derived classes.

Namespaces

MORE: Update!!!

We do not use namespaces. We probably should, following the Google style guide's guidelines - see http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces

Other

We often pretend we are coding in Java and write all our class members inline.

C++11

Other coding conventions

ECL code

The ECL style guide is published separately.

Javascript, XML, XSL etc

We use the commonly accepted conventions for formatting these files.


Design Patterns

Why Design Patterns?

Consistent use of design patterns helps make the code easy to understand.

Interfaces

While C++ does not have explicit support for interfaces (in the java sense), an abstract class with no data members and all functions pure virtual can be used in the same way.

Interfaces are pure virtual classes. They are similar concepts to Java's interfaces and should be used on public APIs. If you need common code, use policies (see below).

An interface's name must start with an 'I' and the base class for its concrete implementations should start with a 'C' and have the same name, ex:

cpp
CFoo : implements IFoo { };

When an interface has multiple implementations, try to stay as close as possible to this rule. Ex:

cpp
CFooCool : implements IFoo { };
+CFooWarm : implements IFoo { };
+CFooALot : implements IFoo { };

Or, for partial implementation, use something like this:

cpp
CFoo : implements IFoo { };
+CFooCool : public CFoo { };
+CFooWarm : public CFoo { };

Extend current interfaces only on a 'is-a' approach, not to aggregate functionality. Avoid pollution of public interfaces by having only the public methods on the most-base interface in the header, and internal implementation in the source file. Prefer pImpl idiom (pointer-to-implementation) for functionality-only requirements and policy based design for interface requirements.

Example 1: You want to decouple part of the implementation from your class, and this part does not implements the interface your contract requires.:

cpp
interface IFoo
+{
+    virtual void foo()=0;
+};
+
+// Following is implemented in a separate private file...
+class CFoo : implements IFoo
+{
+    MyImpl *pImpl;
+public:
+    virtual void foo() override { pImpl->doSomething(); }
+};

Example2: You want to implement the common part of one (or more) interface(s) in a range of sub-classes.:

cpp
interface ICommon
+{
+    virtual void common()=0;
+};
+interface IFoo : extends ICommon
+{
+    virtual void foo()=0;
+};
+interface IBar : extends ICommon
+{
+    virtual void bar()=0;
+};
+
+template <class IFACE>
+class Base : implements IFACE
+{
+    virtual void common() override { ... };
+}; // Still virtual
+
+class CFoo : public Base<IFoo>
+{
+    void foo() override { 1+1; };
+};
+class CBar : public Base<IBar>
+{
+    void bar() override { 2+2; };
+};

NOTE: Interfaces deliberately do not contain virtual destructors. This is to help ensure that they are never destroyed by calling delete directly.

Reference counted objects

Shared<> is an in-house intrusive smart pointer implementation. It is close to boost's intrusive_ptr. It has two derived implementations: Linked and Owned, which are used to control whether the pointer is linked when a shared pointer is created from a real pointer or not, respectively. Ex:

cpp
Owned<Foo> myFoo = new Foo; // Take owenership of the pointers
+Linked<Foo> anotherFoo = = myFoo; // Shared ownership

Shared<> is thread-safe and uses atomic reference count handled by each object (rather than by the smart pointer itself, like boost's shared_ptr).

This means that, to use Shared<>, your class must implement the Link() and Release() methods - most commonly by extending the CInterfaceOf<> class, or the CInterface class (and using the IMPLEMENT_IINTERFACE macro in the public section of your class declaration).

This interface controls how you Link() and Release() the pointer. This is necessary because in some inner parts of HPCC, the use of a "really smart" smart pointer would add too many links and releases (on temporaries, local variables, members, etc) that could add to a significant performance hit.

The CInterface implementation also include a virtual function beforeDispose() which is called before the object is deleted. This allows resources to be cleanly freed up, with the full class hierarchy (including virtual functions) available even when freeing items in base classes. It is often used for caches that do not cause the objects to be retained.

STL

MORE: This needs documenting

Structure of the HPCC source tree

MORE!

Requiring more work: * namespaces * STL * c++11 * Review all documentation * Better examples for shared

`,84),l=[n];function o(h,r,p,d,c,k){return i(),e("div",null,l)}const E=s(t,[["render",o]]);export{u as __pageData,E as default}; diff --git a/assets/devdoc_StyleGuide.md.Mjr9vf6y.lean.js b/assets/devdoc_StyleGuide.md.Mjr9vf6y.lean.js new file mode 100644 index 00000000000..bcab98e795b --- /dev/null +++ b/assets/devdoc_StyleGuide.md.Mjr9vf6y.lean.js @@ -0,0 +1 @@ +import{_ as s,c as e,o as i,V as a}from"./chunks/framework.gBlNPWt_.js";const u=JSON.parse('{"title":"Coding conventions","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/StyleGuide.md","filePath":"devdoc/StyleGuide.md","lastUpdated":1721825996000}'),t={name:"devdoc/StyleGuide.md"},n=a("",84),l=[n];function o(h,r,p,d,c,k){return i(),e("div",null,l)}const E=s(t,[["render",o]]);export{u as __pageData,E as default}; diff --git a/assets/devdoc_UserBuildAssets.md.5pBqOi7J.js b/assets/devdoc_UserBuildAssets.md.5pBqOi7J.js new file mode 100644 index 00000000000..d27ba4c574e --- /dev/null +++ b/assets/devdoc_UserBuildAssets.md.5pBqOi7J.js @@ -0,0 +1,34 @@ +import{_ as e,c as s,o as a,V as t}from"./chunks/framework.gBlNPWt_.js";const n="/HPCC-Platform/assets/repository-tag-tab.0n7rea9Z.png",o="/HPCC-Platform/assets/actions-secrets-and-variables.XwHB-cZ0.png",i="/HPCC-Platform/assets/HPCC-12345-build-in-progress.1NPj4pB5.png",y=JSON.parse('{"title":"Build Assets for individual developer","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/UserBuildAssets.md","filePath":"devdoc/UserBuildAssets.md","lastUpdated":1721825996000}'),r={name:"devdoc/UserBuildAssets.md"},l=t('

Build Assets for individual developer

Build Assets

The modern tool used for generating all our official assets is the Github Actions build-asset workflow on the hpcc-systems/HPCC-Platform repository, located here. Developers and contributors can utilize this same workflow on their own forked repository. This allows developers to quickly create assets for testing changes and test for errors before the peer review process.

Build assets will generate every available project under the HPCC-Platform namespace. There currently is not an option to control which packages in the build matrix get generated. But most packages get built in parallel, and released after the individual matrix job is completed, so there is no waiting on packages you don't need. Exceptions to this are for packages that require other builds to complete, such as the ECLIDE.

Upon completion of each step and matrix job in the workflow, the assets will be output to the repositories tags tab. An example for the hpcc-systems user repository is hpcc-systems/HPCC-Platform/tags.

Tag tab screenshot

Dependent variables

The build assets workflow requires several repository secrets be available on a developers machine in order to run properly. You can access these secrets and variables by going to the settings tab in your forked repository, and then clicking on the Secrets and Variables - Actions drop down under Security on the lefthand side of the settings screen.

Actions secrets and variables

Create a secret by clicking the green New Repository Secret button. The following secrets are needed;

  • LNB_ACTOR - Your Github username
  • LNB_TOKEN - Classic Github token for your user with LN repo access
  • DOCKER_USERNAME - Your docker.io username
  • DOCKER_PASSWORD - Your docker.io password
  • SIGNING_CERTIFICATE - pks12 self signed cert encoded to base64 for windows signing
  • SIGNING_CERTIFICATE_PASSPHRASE - passphrase for pks12 cert
  • SIGNING_SECRET - ssh-keygen private key for signing linux builds
  • SIGN_MODULES_KEYID - email used to generate key
  • SIGN_MODULES_PASSPHRASE - passphrase for private key

Generating the windows signing certificate

To generate the self signed certificate for windows packages, you will need to do the following steps.

  1. Generate a root certificate authority

openssl req -x509 -sha256 -days 365 -nodes -newkey rsa:2048 -subj "/CN=example.com/C=US/L=Boca Raton" -keyout rootCA.key -out rootCA.crt

  1. Create the server secret key

openssl genrsa -out server.key 2048

  1. generate a csr.conf file
cat > csr.conf <<EOF
+[ req ]
+default_bits = 2048
+prompt = no
+default_md = sha256
+req_extensions = req_ext
+distinguished_name = dn
+
+[ dn ]
+C = US
+ST = Florida
+L = Boca Raton
+O = LexisNexis Risk
+OU = HPCCSystems Development
+CN = example.com
+
+[ req_ext ]
+subjectAltName = @alt_names
+
+[ alt_names ]
+DNS.1 = example.com
+IP.1 = 127.0.0.1
+
+EOF
  1. Generate Cert Signing Request

openssl req -new -key server.key -out server.csr -config csr.conf

  1. Create cert.conf file
cat > cert.conf <<EOF
+
+authorityKeyIdentifier=keyid,issuer
+basicConstraints=CA:FALSE
+keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
+subjectAltName = @alt_names
+
+[ alt_names ]
+DNS.1 = example.com
+
+EOF
  1. Generate SSL cert with self signed CA

openssl x509 -req -in server.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out server.crt -days 365 -sha256 -extfile cert.conf

  1. Use server.crt to generate PKCS12 needed for windows tools

openssl pkcs12 -inkey server.key -in server.crt -export -name "hpcc_sign_cert" -out hpcc_sign_cert.pfx

You will be asked to "enter export password", this will be what goes in the variable SIGNING_CERTIFICATE_PASSPHRASE in Github Actions.

  1. Convert to base64

On linux: base64 hpcc_sign_cert.pfx > hpcc_sign_cert.base64

On MacOS: base64 -i hpcc_sign_cert.pfx -o hpcc_sign_cert.base64

From here you can cat the output of hpcc_sign_cert.base64 and copy the output into the variable SIGNING_CERTIFICATE in Github Actions.

Generating a signing key for linux builds

For linux builds we're going to generate a private key using GnuPG (gpg).

Start the process by entering a terminal and run the command;gpg --full-generate-key

You will be given several options in this process.

For type of key, select RSA and RSA default.

For keysize, enter 4096.

For expiration date, select 0 = key does not expire.

Input your real name.

Input your company email address.

For comment, input something like Github actions key for signing linux builds.

Then it will ask you to enter a passphrase for the key, and confirm the passphrase. Do not leave this blank.

A key should be output and entered into your gpg keychain. Now we need to export the key for use in the github actions secret.

To extract your key run gpg --output private.pgp --armor --export-secret-key <email-address-used>.

Now open private.pgp, copy all, and go to github actions secrets. Paste the output into the secret "SIGNING_SECRET"

Starting a build

The build-asset workflow is kicked off by a tag being pushed to the developers HPCC-Platform repository. Before we push the tag to our HPCC-Platform repository, we will want to have other tags in place if we want LN and ECLIDE builds to function correctly. Suggested tag patterns are community_HPCC-12345-rc1 or HPCC-12345-rc1.

If you choose not to tag the LN and ECLIDE builds, the community builds will generate but errors will be thrown for any build utilizing the LN repository. ECLIDE will not even attempt a build unless you are also successfully building LN due to the dependency scheme we use. The 'Baremetal' builds are designed to generate our clienttools targets for windows-2022 and macos-12 distributions. These jobs contain both the COMMUNITY and LN builds. If the LN build is not tagged, the COMMUNITY section of the job will run, and the assets will be uploaded, but the job will fail when it tries to build LN.

If you choose to precede your Jira number with community_ then you must tag LN with internal_ and ECLIDE with eclide_. Otherwise just use the Jira tag in all three repositories.

Once the LN and ECLIDE repository tags have been created and pushed with the same base branch that your work is based on for the HPCC-Platform, then you are free to push the HPCC-Platform tag which will initiate the build process.

The summary of the build-asset workflow can then be viewed for progress, and individual jobs can be selected to check build outputs. Build Summary HPCC-12345

Asset output

Assets from the workflow will be released into the corresponding tag location, either in the HPCC-Platform repository for all community based builds, or the LN repository for any builds containing proprietary plugins. Simply browse to the releases or tag tab of your repository and select the tag name you just built. The assets will show up there as the build completes. An example of this on the hpcc-systems repository is hpcc-systems/HPCC-Platform/releases.

',54),p=[l];function c(d,u,h,g,b,f){return a(),s("div",null,p)}const w=e(r,[["render",c]]);export{y as __pageData,w as default}; diff --git a/assets/devdoc_UserBuildAssets.md.5pBqOi7J.lean.js b/assets/devdoc_UserBuildAssets.md.5pBqOi7J.lean.js new file mode 100644 index 00000000000..cef1c874440 --- /dev/null +++ b/assets/devdoc_UserBuildAssets.md.5pBqOi7J.lean.js @@ -0,0 +1 @@ +import{_ as e,c as s,o as a,V as t}from"./chunks/framework.gBlNPWt_.js";const n="/HPCC-Platform/assets/repository-tag-tab.0n7rea9Z.png",o="/HPCC-Platform/assets/actions-secrets-and-variables.XwHB-cZ0.png",i="/HPCC-Platform/assets/HPCC-12345-build-in-progress.1NPj4pB5.png",y=JSON.parse('{"title":"Build Assets for individual developer","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/UserBuildAssets.md","filePath":"devdoc/UserBuildAssets.md","lastUpdated":1721825996000}'),r={name:"devdoc/UserBuildAssets.md"},l=t("",54),p=[l];function c(d,u,h,g,b,f){return a(),s("div",null,p)}const w=e(r,[["render",c]]);export{y as __pageData,w as default}; diff --git a/assets/devdoc_VersionSupport.md.deGoLLFC.js b/assets/devdoc_VersionSupport.md.deGoLLFC.js new file mode 100644 index 00000000000..8ab085aef44 --- /dev/null +++ b/assets/devdoc_VersionSupport.md.deGoLLFC.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as i,V as a}from"./chunks/framework.gBlNPWt_.js";const g=JSON.parse('{"title":"Current versions","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/VersionSupport.md","filePath":"devdoc/VersionSupport.md","lastUpdated":1721825996000}'),s={name:"devdoc/VersionSupport.md"},o=a('

Current versions

nameversion
current9.6.x
previous9.4.x
critical9.2.x
security9.0.x

Supported versions

We release a new version of the platform every 3 months. If there are major changes in functionality, or significant backward compatibility issues then it will be tagged as a new major version, otherwise a new minor version. We normally maintain 4 versions of the system, which means that each new release will typically be supported for a year. Once a new major or minor version has been tagged gold it should not have any changes that change the behavior of queries.

Which versions should changes be applied to? The following gives some examples of the types of changes and which version they would be most relevant to target.

"master":

  • New features.
  • Bug fixes that will change the semantics of existing queries or processes.
  • Refactoring.
  • Performance improvements (unless simple and safe)

"<current>":

  • Bug fixes that only change behavior where it previously crashes or had undefined behavior (If well defined but wrong need to have very strong justification to change.)
  • Fixes for race conditions (the behavior was previously indeterminate so less of an argument against it changing)
  • Data corruption fixes - on a case by case basis if they change existing query results.
  • Missing functionality that prevents features from working.
  • Changes for tech-preview work that only effect those who are using it.
  • Regressions.
  • Improvements to logging and error messages (possibly in "previous" if simple and added to help diagnose problems).
  • Occasional simple refactoring that makes up-merging simpler..
  • Changes to improve backward compatibility of new features. (E.g. adding an ignored syntax to the compiler.)
  • Performance improvements - if simple and safe

"<previous>":

  • Simple bug fixes that do not change behavior
  • Simple changes for missing functionality
  • Regressions with simple fixes (but care is needed if it caused a change in behavior)
  • Serious regressions
  • Complex security fixes

"<critical>" fixes only:

  • Simple security fixes
  • Complex security fixes if sufficiently serious

"<security>" fixes only:

  • Serious security fixes

Occasionally earlier branches will be chosen, (e.g. security fixes to even older versions) but they should always be carefully discussed (and documented).

Patches and images

We aim to produce new point releases once a week. The point releases will contain

a) Any changes to the code base for that branch. b) Any security fixes for libraries that are project dependencies. We will upgrade to the latest point release for the library that fixes the security issue. c) For the cloud any security fixes in the base image or the packages installed in that image.

If there are no changes in any of those areas for a particular version then a new point release will not be created.

If you are deploying a system to the cloud you have one of two options

a) Use the images that are automatically built and published as part of the build pipeline. This image is currently based on ubuntu 22.04 and contains the majority of packages users will require.

b) Use your own hardened base image, and install the containerized package that we publish into that image.

Package versions.

We currently generate the following versions of the package and images:

  • debug
  • release with symbols
  • release without symbols.

It is recommended that you deploy the "release with symbols" version to all bare-metal and non-production cloud deployments. The extra symbols allow the system to generate stack backtraces which make it much easier to diagnose problems if they occur. The "release without symbols" version is recommended for Kubernetes production deployments. Deploying a system without symbols reduces the size of the images. This reduces the time it takes Kubernetes to copy the image before provisioning a new node.

',27),r=[o];function n(l,h,c,u,d,p){return i(),t("div",null,r)}const f=e(s,[["render",n]]);export{g as __pageData,f as default}; diff --git a/assets/devdoc_VersionSupport.md.deGoLLFC.lean.js b/assets/devdoc_VersionSupport.md.deGoLLFC.lean.js new file mode 100644 index 00000000000..63cb6447045 --- /dev/null +++ b/assets/devdoc_VersionSupport.md.deGoLLFC.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as i,V as a}from"./chunks/framework.gBlNPWt_.js";const g=JSON.parse('{"title":"Current versions","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/VersionSupport.md","filePath":"devdoc/VersionSupport.md","lastUpdated":1721825996000}'),s={name:"devdoc/VersionSupport.md"},o=a("",27),r=[o];function n(l,h,c,u,d,p){return i(),t("div",null,r)}const f=e(s,[["render",n]]);export{g as __pageData,f as default}; diff --git a/assets/devdoc_Workunits.md.jFcYNljW.js b/assets/devdoc_Workunits.md.jFcYNljW.js new file mode 100644 index 00000000000..3d534dae5ea --- /dev/null +++ b/assets/devdoc_Workunits.md.jFcYNljW.js @@ -0,0 +1,403 @@ +import{_ as s,c as i,o as a,V as t}from"./chunks/framework.gBlNPWt_.js";const y=JSON.parse('{"title":"Understanding workunits","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/Workunits.md","filePath":"devdoc/Workunits.md","lastUpdated":1721825996000}'),n={name:"devdoc/Workunits.md"},h=t(`

Understanding workunits

Introduction

A workunit contains all the information that the system requires about a query - including the parameters it takes, how to execute it, and how to format the results. Understanding the contents of a workunit is a key step to understanding how the HPCC system fits together. This document begins with an overview of the different elements in a workunit. That is then followed by a walk-through executing a simple query, with a more detailed description of some of the workunit components to show how they all tie together.

Before looking at the contents of a workunit it is important to understand one of the design goals behind the HPCC system. The HPCC system logically splits the code that is needed to execute a query in two. On the one hand there are the algorithms that are used to perform different dataset operations (e.g. sorting, deduping). The same algorithms are used by all the queries that execute on the system. On the other hand there is the meta-data that describes the columns present in the datasets, which columns you need to sort by, and the order of operations required by the query. These are typically different for each query. This "meta-data" includes generated compare functions, classes that describe the record formats, serialized data and graphs.

A workunit only contains data and code relating to the meta data for the query, i.e. "what to do", while the different engines (hthor, roxie, and thor) implement the algorithms - "how to do it". If you look at a workunit for an ECL query that sorts a dataset you will not find code to perform the sort itself in the workunit - you will not even find a call to a sort library function - that logic is contained in the engine that executes the query.

One consequence of this split, which can be initially confusing, is that execution continually passes back and forth between the generated code and the engines. By the end of this document you should have a better understanding of how the generated code is structured and the part it plays in executing a query.

Note the term "Query" is used as a generic term to cover read-only queries (typically run in roxie) and ETL (Extract, Transform, and Load) style queries that create lots of persistent datafiles (typically run in Thor). Also, the term "workunit" is used ambiguously. The dll created from a query is called a workunit (which is static), but "workunit" is also used to describe a single execution of a query (which includes the parameters and results). It should be clear from the context which of these is meant.

Throughout this document "dll" is a generic term used to refer to a dynamically loaded library. These correspond to shared objects in Linux (extension '.so'), dynamic libraries in Max OS X ('.dylib'), and dynamic link libraries in windows ('.dll').

Contents of a workunit

A workunit is generated by the ecl compiler, and consists of a single dll. That dll contains several different elements:

  • Various C++ helper classes, and exported factory methods are used to create instances of those classes.
  • An XML resource containing different pieces of information about a workunit, including workflow and graphs.
  • Other user-defined resources included in the manifest.

A workunit dll contains everything that the engines need to execute the query. When a workunit is executed, key elements of the xml information are cloned from the workunit dll and copied into a database. This is then augmented with other information as the query is executed - e.g. input parameters, results, statistics, etc.. The contents of the workunit are accessed through an "IWorkUnit" interface (defined in common/workunit/workunit.hpp) that hides the implementation details.

(Workunit information is currently stored in the Dali database - one of the components within the HPCC platform. Work is in-progress to allow the bulk of this workunit data to be stored in Cassandra or another third-party database instead.)

How is the workunit used?

The workunit information is used by most of the components in the system. The following is a quick outline:

  • eclcc

    Creates a workunit dll from an ecl query.

  • eclccserver

    Executes eclcc to create a workunit dll, and then clones some of the information into dali to create an active instance, ready to execute.

  • esp

    Uses information in the workunit dll to publish workunits. This includes details of the parameters that the query takes, how they should be formatted, and the results it returns.

  • eclscheduler

    Monitors workunits that are waiting for events, and updates them when those events occur.

  • eclagent/Roxie

    Process the different workflow actions, and workflow code.

  • hThor/Roxie/Thor

    Execute graphs within the workflow items.

  • Dali

    This database is used to store the state of the workunit state.

Example

The following ECL will be used as an example for the rest of the discussion. It is a very simple search that takes a string parameter 'searchName', which is the name of the person to search for, and returns the matching records from an index. It also outputs the word 'Done!' as a separate result.

ecl
STRING searchName := 'Smith' : STORED('searchName');
+nameIndex := INDEX({ STRING40 name, STRING80 address }, 'names');
+results := nameIndex(KEYED(name = searchName));
+OUTPUT(results);
+OUTPUT('Done!');

Extracts from the XML and C++ that are generated from this example will be included in the following discussion.

Workunit Main Elements

This section outlines the different sections in a workunit. This is followed by a walk-through of the stages that take place when a workunit is executed, together with a more detailed explanation of the workunit contents.

Workflow

The workflow is the highest level of control within a workunit. It is used for two related purposes:

  • Scheduling The HPCC system allows ECL code to be executed when certain events occur - e.g. every hour or when files are uploaded to a landing zone. Each piece of ECL code that is triggered by an external event creates a separate workflow action. This allows each of those events to be processed independently.
  • Splitting up queries. There are situations where it is useful to break parts of an ECL query into independent sections. The simplest example is the PERSIST workflow operation, which allows results to be shared between different work units. Each workflow operation creates one (or sometimes more) independent workflow items, which are then connected together.

Each piece of independent ECL is given a unique workflow id (wfid). Often workflow items need to be executed in a particular order, e.g. ensuring a persist exists before using it, which is managed with dependencies between different workflow items.

Our example above generates the following XML entry in the workunit:

xml
<Workflow>
+    <Item .... wfid="1"/>
+    <Item .... wfid="2">
+    <Dependency wfid="1"/>
+    <Schedule/>
+    </Item>
+</Workflow>

This contains two workflow items. The first workflow item (wfid=1) ensures that the stored value has a default value if it has not been supplied. The second item (with wfid=2) is the main code for the query. This has a dependency on the first workflow item because the stored variable needs to be intialised before it is executed.

MyProcess

The generated code contains a class instance that is used for executing the code associated with the workflow items. It is generated at the end of the main C++ module. E.g.:

cpp
struct MyEclProcess : public EclProcess {
+    virtual int perform(IGlobalCodeContext * gctx, unsigned wfid) {
+        ....
+        switch (wfid) {
+            case 1U:
+                ... code for workflow item 1 ...
+            case 2U:
+                ... code for workflow item 2 ...
+            break;
+        }
+        return 2U;
+    }
+};

The main element is a switch statement inside the perform() function that allows the workflow engines to execute the code associated with a particular workflow item.

There is also an associated factory function that is exported from the dll, and is used by the engines to create instances of the class:

cpp
extern "C" ECL_API IEclProcess* createProcess()
+{
+    return new MyEclProcess;
+}

Graph

Most of the work executing a query involves processing dataset operations, which are implemented as a graph of activities. Each graph is represented in the workunit as an xml graph structure (currently it uses the xgmml format). The graph xml includes details of which types of activities are required to be executed, how they are linked together, and any other dependencies.

The graph in our example is particularly simple:

xml
<Graph name="graph1" type="activities">
+    <xgmml>
+    <graph wfid="2">
+    <node id="1">
+    <att>
+        <graph>
+        <att name="rootGraph" value="1"/>
+        <edge id="2_0" source="2" target="3"/>
+        <node id="2" label="Index Read&#10;&apos;names&apos;">
+        ... attributes for activity 2 ...
+        </node>
+        <node id="3" label="Output&#10;Result #1">
+        ... attributes for activity 3 ...
+        </node>
+        </graph>
+    </att>
+    </node>
+    </graph>
+    </xgmml>
+</Graph>

This graph contains a single subgraph (node id=2) that contains two activities - an index read activity and an output result activity. These activities are linked by a single edge (id "2_0"). The details of the contents are covered in the section on executing graphs below.

Generated Activity Helpers

Each activity has a corresponding class instance in the generated code, and a factory function for creating instances of that class:

cpp
struct cAc2 : public CThorIndexReadArg {
+    ... Implementation of the helper for activity #2 ...
+};
+extern "C" ECL_API IHThorArg * fAc2() { return new cAc2; }
+
+struct cAc3 : public CThorWorkUnitWriteArg {
+    ... Implementation of the helper for activity #3 ...
+};
+extern "C" ECL_API IHThorArg * fAc3() { return new cAc3; }

The helper class for an activity implements the interface that is required for that particular kind. (The interfaces are defined in rtl/include/eclhelper.hpp - further details below.)

Other

The are several other items, detailed below, that are logically associated with a workunit. The information may be stored in the workunit dll or in various other location e.g. Dali, Sasha or Cassandra. It is all accessed through the IWorkUnit interface in common/workunit/workunit.hpp that hides the implementation details. For instance information generated at runtime cannot by definition be included in the workunit dll.

Options

Options that are supplied to eclcc via the -f command line option, or the #option statement are included in the <Debug> section of the workunit xml:

xml
<Debug>
+    <addtimingtoworkunit>0</addtimingtoworkunit>
+    <noterecordsizeingraph>1</noterecordsizeingraph>
+    <showmetaingraph>1</showmetaingraph>
+    <showrecordcountingraph>1</showrecordcountingraph>
+    <spanmultiplecpp>0</spanmultiplecpp>
+    <targetclustertype>hthor</targetclustertype>
+</Debug>

Note, the names of workunit options are case insensitive, and converted to lower case.

Input Parameters

Many queries contain input parameters that modify their behaviour. These correspond to STORED definitions in the ECL. Our example contains a single string "searchName", so the workunit contains a single input parameter:

xml
<Variables>
+    <Variable name="searchname">
+    <SchemaRaw xsi:type="SOAP-ENC:base64">
+        searchname&#xe000;&#xe004;&#241;&#255;&#255;&#255;&#xe001;ascii&#xe000;&#xe001;ascii&#xe000;&#xe000;&#xe018;&#xe000;&#xe000;&#xe000;&#xe000;   
+    </SchemaRaw>
+    </Variable>
+</Variables>

The implementation details of the schema information is encapsulated by the IConstWUResult interface in workunit.hpp.

Results

The workunit xml also contains details of each result that the query generates, including a serialized description of the output record format:

xml
<Results>
+    <Result isScalar="0"
+            name="Result 1"
+            recordSizeEntry="mf1"
+            rowLimit="-1"
+            sequence="0">
+    <SchemaRaw xsi:type="SOAP-ENC:base64">
+    name&#xe000;&#xe004;(&#xe000;&#xe000;&#xe000;&#xe001;ascii&#xe000;&#xe001;ascii&#xe000;address&#xe000;&#xe004;P&#xe000;&#xe000;&#xe000;&#xe001;ascii&#xe000;&#xe001;ascii&#xe000;&#xe000;&#xe018;%&#xe000;&#xe000;&#xe000;{ string40 name, string80 address };&#10;   </SchemaRaw>
+    </Result>
+    <Result name="Result 2" sequence="1">
+    <SchemaRaw xsi:type="SOAP-ENC:base64">
+    Result_2&#xe000;&#xe004;&#241;&#255;&#255;&#255;&#xe001;ascii&#xe000;&#xe001;ascii&#xe000;&#xe000;&#xe018;&#xe000;&#xe000;&#xe000;&#xe000;   </SchemaRaw>
+    </Result>
+</Results>

in our example there are two - the dataset of results and the text string "Done!". The values of the results for a query are associated with the workunit. (They are currently saved in dali, but this may change in version 6.0.)

Timings and Statistics

Any timings generated when compiling the query are included in the workunit dll:

xml
<Statistics>
+    <Statistic c="eclcc"
+            count="1"
+            creator="eclcc"
+            kind="SizePeakMemory"
+            s="compile"
+            scope=">compile"
+            ts="1428933081084000"
+            unit="sz"
+            value="27885568"/>
+</Statistics>

Other statistics and timings created when running the query are stored in the runtime copy of the workunit. (Statistics for graph elements are stored in a different format from global statistics, but the IWorkUnit interface ensures the implementation details are hidden.)

Manifests

It is possible to include other user-defined resources in the workunit dll - e.g. web pages, or dashboard layouts. I have to confess I do not understand them... ??Tony please provide some more information....!

Stages of Execution

Once a workunit has been compiled to a dll it is ready to be executed. Execution can be triggered in different ways, E.g.:

  • The ECL for a query is submitted to esp
    • A workunit entry, containing the ECL, is created in dali and added to an eclccserver queue.
    • An eclccserver instance removes the workunit form the queue, and compiles the ECL to a workunit dll.
    • The dali workunit entry is updated with the information from the workunit dll.
    • The dali workunit is added to the agent execution queue associated with the target cluster.
    • The associated engine (actually agentexec for hThor and Thor) pulls a query form the queue and executes it.
  • A query is submitted and published with a name. Another request is then submitted to execute this previously compiled query.
    • A workunit entry, containing the ECL, is created in dali and added to an eclccserver queue.
    • An eclccserver instance removes the workunit form the queue, and compiles the ECL to a workunit dll.
    • There is a 'query set' for each combination of query name and the target cluster. The new workunit dll is added to the appropriate query set, and marked as the current active implementation.
    • Later, a query that references a named query is submitted to esp.
    • The name and target cluster are mapped via the query set to the active implementation, and a workunit instance is created from the active workunit dll.
    • The workunit is added to a roxie or eclagentexec queue ready to be executed.
    • The associated engine pulls a query form the queue and executes it.
  • A query is compiled as a stand alone executable. The executable is then run.
    • eclcc is executed on the command line without the -shared command line option.
    • The resulting executable is run. The engine used to execute the query depends on the -platform parameter supplied to eclcc.

Most queries create persistent workunits in dali and then update those workunits with results as they are calculated, however for some roxie queries (e.g. in a production system) the execution workunits are transient.

The following walk-through details the main stages executing a query, and the effect each of the query elements has.

Queues

The system uses several inter-process queues to communicate between the different components in the system. These queues are implemented by dali. Components can subscribe to one or more queues, and receive notifications when entries are avaialable.

Some example queues are:

  • <cluster>.eclserver - workunits to be compiled
  • <cluster>.roxie - workunits to execute on roxie
  • <cluster>.thor - graphs to execute on thor
  • <cluster>.eclscheduler - workunits that need to wait for events
  • <cluster>.agent - workunits to be executed on hthor or thor.
  • dfuserver_queue - dfu workunits for sprays/file copies etc.

Workflow

When a workunit is ready to be run, the workflow controls the flow of execution. The workflow engine (defined in common/workunit/workflow.cpp) is responsible for determining which workflow item should be executed next.

The workflow for Thor and hThor jobs is coordinated by eclagent, while roxie includes the workflow engine in its process. The eclscheduler also uses the workflow engine to process events and mark workflow items ready for execution.

eclagent, or roxie calls the createProcess() function from the workunit dll to create an instance of the generated workflow helper class, and passes it to the workflow engine. The workflow engine walks the workflow items to find any items that are ready to be executed (have the state "reqd" - i.e. required). If a required workflow item has dependencies on other child workflow items then those children are executed first. Once all dependencies have executed successfully the parent workflow item is executed. The example has the following workflow entries:

xml
<Workflow>
+    <Item mode="normal"
+        state="null"
+        type="normal"
+        wfid="1"/>
+    <Item mode="normal"
+        state="reqd"
+        type="normal"
+        wfid="2">
+        <Dependency wfid="1"/>
+        <Schedule/>
+    </Item>
+</Workflow>

Item 2 has a state of "reqd", so it should be evaluated now. Item 2 has a dependency on item 1, so that must be evaluated first. This is achieved by calling MyEclProcess::perform() on the object that was previously created from the workunit dll, passing in wfid = 1. That will execute the following code:

cpp
switch (wfid) {
+    case 1U:
+        if (!gctx->isResult("searchname",4294967295U)) {
+            ctx->setResultString("searchname",4294967295U,5U,"Smith");
+        }
+        break;
+    break;
+}

This checks if a value has been provided for the input parameter, and if not assigns a default value of "Smith". The function returns control to the workflow engine. With the dependencies for wfid 2 now satisfied, the generated code for that workflow id is now executed:

cpp
switch (wfid) {
+    case 2U: {
+        ctx->executeGraph("graph1",false,0,NULL);
+        ctx->setResultString(0,1U,5U,"Done!");
+    }
+    break;
+}

Most of the work for this workflow item involves executing graph1 (by calling back into eclagent/roxie). However, the code also directly sets another result. This is fairly typical - the code inside MyProcess::perform often combines evaluating scalar results, executing graphs, and calling functions that cannot (currently) be called inside a graph (e.g. those involving superfile transactions).

Once all of the required workflow items are executed, the workunit is marked as completed. Alternatively, if there are workflow items that are waiting to be triggered by an event, the workunit will be passed to the scheduler, which will keep monitoring for events.

Note that most items in the xml workflow begin in the state WFStateNull. This means that it is valid to execute them, but they haven't been executed yet. Typically, only a few items begin with the state WFStateReqd.

There are various specialised types of workflow items - e.g. sequential, wait, independent, but they all follow the same basic approach of executing dependencies and then executing that particular item.

Most of the interesting work in an ECL query is done within a graph. The call ctx->executeGraph will either execute the graph locally (in the case of hthor and roxie), or add the workunit onto a queue (for Thor). Whichever happens, control will pass to that engine.

Specialised Workflow Items

Each item mode/type can affect the dependency structure of the workflow:

  • Sequential/Ordered

    The workflow structure for sequential and ordered is the same. An item is made to contain all of the actions in the statement. This is achieved by making each action a dependency of this item. The dependencies, and consequently the actions, must be executed in order. An extra item is always inserted before each dependency. This means that if other statements reference the same dependency, it will only be performed once.

  • Persist

    When the persist workflow service is used, two items are created. One item contains the graphs that perform the expression defined in ECL. It also stores the wfid for the second item. The second item is used to determine whether the persist is up to date.

  • Condition (IF)

    The IF function has either 2 or 3 arguments: the expression, the trueresult, and sometimes the falseresult. For each argument, a workflow item is created. These items are stored as dependencies to the condition, in the order stated above.

  • Contingency (SUCCESS/FAILURE)

    When a contingency clause is defined for an attribute, the attribute item stores the wfid of the contingency. If both success and failure are used, then the item will store the wfid of each contingency. The contingency is composed of items, just like the larger query.

  • Recovery

    When a workflow item fails, if it has a recovery clause, the item will be re-executed. The clause contains actions defined by the programmer to remedy the problem. This clause is stored differently to SUCCESS/FAILURE, in that the recovery clause is a dependency of the failed item. In order to stop the recovery clause from being executed like the other dependencies, it is marked with WFStateSkip.

  • Independent

    This specifier is used when a programmer wants to common up code for the query. It prevents the same piece of code from being executed twice in different contexts. To achieve this, an extra item is inserted between the expression and whichever items depend on it. This means that although the attribute can be referenced several times, it will only be executed once.

Graph Execution

All the engines (roxie, hThor, Thor) execute graphs in a very similar way. The main differences are that hThor and Thor execute a sub graph at a time, while roxie executes a complete graph as one. Roxie is also optimized to minimize the overhead of executing a query - since the same query tends to be run multiple times. This means that roxie creates a graph of factory objects and those are then used to create the activities. The core details are the same for each of them though.

Details of the graph structure

First, a recap of the structure of the graph together with the full xml for the graph definition in our example:

xml
<Graph name="graph1" type="activities">
+    <xgmml>
+        <graph wfid="2">
+            <node id="1">
+                <att>
+                    <graph>
+                        <att name="rootGraph" value="1"/>
+                        <edge id="2_0" source="2" target="3"/>
+                        <node id="2" label="Index Read&#10;&apos;names&apos;">
+                            <att name="definition" value="workuniteg1.ecl(3,1)"/>
+                            <att name="name" value="results"/>
+                            <att name="_kind" value="77"/>
+                            <att name="ecl" value="INDEX({ string40 name, string80 address }, &apos;names&apos;, fileposition(false));&#10;FILTER(KEYED(name = STORED(&apos;searchname&apos;)));&#10;"/>
+                            <att name="recordSize" value="120"/>
+                            <att name="predictedCount" value="0..?[disk]"/>
+                            <att name="_fileName" value="names"/>
+                        </node>
+                        <node id="3" label="Output&#10;Result #1">
+                            <att name="definition" value="workuniteg1.ecl(4,1)"/>
+                            <att name="_kind" value="16"/>
+                            <att name="ecl" value="OUTPUT(..., workunit);&#10;"/>
+                            <att name="recordSize" value="120"/>
+                        </node>
+                    </graph>
+                </att>
+            </node>
+        </graph>
+    </xgmml>
+</Graph>

Each graph (e.g. graph1) consists of 1 or more subgraphs (in the example above, node id=1). Each of those subgraphs contains 1 or more activities (node id=2, node id=3).

The xml for each activity might contain the following information:

  • A unique id (e.g. id="2").
  • The "kind" of the activity, e.g. <att name="_kind" value="77"/>. The value is an item from the enum ThorActivityKind in eclhelper.hpp.
  • The ECL that created the activity. E.g. <att name="ecl" value="...">
  • The identifier of the ecl definition. E.g. <att name="name" value="results"/>
  • Location (e.g. file, line number, column) of the original ECL. E.g. <att name="definition" value="workuniteg1.ecl(3,1)"/>
  • Meta information the code generator has deduced about the activity output. Examples include the record size, expected number of rows, sort order etc. E.g. <att name="recordSize" value="120"/>
  • Hints, which are used for fine control of options for a particular activity (e.g,, the number of threads to use while sorting).
  • Record counts and stats once the job has executed. (These are logically associated with the activities in the graph, but stored separately.)

Graphs also contain edges that can take one of 3 forms:

Edges within graphs

: These are used to indicate how the activities are connected. The source activity is used as the input to the target activity. These edges have the following format:

    <edge id="<source-activity-id>_<output-count>" source="<source-activity-id>" target="<target-activity-id">
+
+There is only one edge in our example workunit: \\<edge id="2\\_0"
+source="2" target="3"/\\>.
+

Edges between graphs

: These are used to indicate direct dependencies between activities. For instance there will be an edge connecting the activity that writes a spill file to the activity that reads it. These edges have the following format:

    <edge id="<source-activity-id>_<target-activity-id>" source="<source-subgraph-id>" target="<target-subgraph-id>"
+       <att name="_sourceActivity" value="<source-activity-id>"/>
+       <att name="_targetActivity" value="<target-activity-id>"/>
+    </edge>
+
+Roxie often optimizes spilled datasets and treats these edges as
+equivalent to the edges between activities.
+

Other dependencies.

: These are similar to the edges between graphs, but they are used for values that are used within an activity. For instance one part of the graph may calculate the maximum value of a column, and another activity may filter out all records that do not match that maximum value. The format is the same as the edges between graphs except that the edge contains the following attribute:

    <att name="_dependsOn" value="1"/>
+

Each activity in a graph also has a corresponding helper class instance in the generated code. (The name of the class is "cAc" followed by the activity number, and the exported factory method is "fAc" followed by the activity number.) Each helper class implements a specialised interface (derived from IHThorArg) - the particular interface is determined by the value of the "_kind" attribute for the activity.

The contents of file rtl/include/eclhelper.hpp is key to understanding how the generated code relates to the activities. Each kind of activity requires a helper class that implements a specific interface. The helpers allow the engine to tailor the generalised activity implementation to the the particular instance - e.g. for a filter activity whether a row should be included or excluded. The appendix at the end of this document contains some further information about this file.

The classes in the generated workunits are normally derived from base implementations of those interfaces (which are implemented in rtl/include/eclhelper_base.hpp). This reduces the size of the generated code by providing default implementations for various functions.

For instance the helper for the index read (activity 2) is defined as:

cpp
struct cAc2 : public CThorIndexReadArg {
+    virtual unsigned getFormatCrc() {
+        return 470622073U;
+    }
+    virtual bool getIndexLayout(size32_t & __lenResult, void * & __result) { getLayout5(__lenResult, __result, ctx); return true; }
+    virtual IOutputMetaData * queryDiskRecordSize() { return &mx1; }
+    virtual IOutputMetaData * queryOutputMeta() { return &mx1; }
+    virtual void onCreate(ICodeContext * _ctx, IHThorArg *, MemoryBuffer * in) {
+        ctx = _ctx;
+        ctx->getResultString(v2,v1.refstr(),"searchname",4294967295U);
+    }
+    rtlDataAttr v1;
+    unsigned v2;
+    virtual const char * getFileName() {
+        return "names";
+    }
+    virtual void createSegmentMonitors(IIndexReadContext *irc) {
+        Owned<IStringSet> set3;
+        set3.setown(createRtlStringSet(40));
+        char v4[40];
+        rtlStrToStr(40U,v4,v2,v1.getstr());
+        if (rtlCompareStrStr(v2,v1.getstr(),40U,v4) == 0) {
+            set3->addRange(v4,v4);
+        }
+        irc->append(createKeySegmentMonitor(false, set3.getClear(), 0, 40));
+        irc->append(createWildKeySegmentMonitor(40, 80));
+    }
+    virtual size32_t transform(ARowBuilder & crSelf, const void * _left) {
+        crSelf.getSelf();
+        unsigned char * left = (unsigned char *)_left;
+        memcpy(crSelf.row() + 0U,left + 0U,120U);
+        return 120U;
+    }
+};

Some of the methods to highlight are:

a) onCreate() - common to all activities. It is called by the engine when the helper is first created, and allows the helper to cache information that does not change - in this case the name that is being searched for. b) getFileName() - determines the name of the index being read. c) createSegmentMonitors() - defines which columns to filter, and which values to match against. d) transform() - create the record to return from the activity. It controls which columns should be included from the index row in the output. (In this case all.)

Executing the graph

To execute a graph, the engine walks the activities in the graph xml and creates, in memory, a graph of implementation activities.

For each activity, the name of the helper factory is calculated from the activity number (e.g. fAc2 for activity 2). That function is imported from the loaded dll, and then called to create an instance of the generated helper class - in this case cAc2.

The engine then creates an instance of the class for implementing the activity, and passes the previously created helper object to the constructor. The engine uses the _kind attribute in the graph to determine which activity class should be used. E.g. In the example above activity 2 has a _kind of 77, which corresponds to TAKindexread. For an index-read activity roxie will create an instance of CRoxieServerIndexReadActivity. (The generated helper that is passed to the activity instance will implement IHThorIndexReadArg). The activity implementations may also extract other information from the xml for the activity - e.g. hints. Once all the activities are created the edge information is used to link inputs activities to output activities and add other dependencies.

Note: Any subgraph that is marked with <att name="rootGraph" value="1"/> is a root subgraph. An activity within a subgraph that has no outputs is called a 'sink' (and an activity without any inputs is called a 'source').

Executing a graph involves executing all the root subgraphs that it contains. All dependencies of the activities within the subgraph must be executed before a subgraph is executed. To execute a subgraph, the engine executes each of the sink activities on separate threads, and then waits for each of those threads to complete. Each sink activity lazily pulls input rows on demand from activities further up the graph, processes them and returns when complete.

(If you examine the code you will find that this is a simplification. The implementation for processing dependencies is more fine grained to ensure IF datasets, OUPUT(,UPDATE) and other ECL constructs are executed correctly.)

In our example the execution flows as follows:

  1. Only a single root subgraph, so need to execute that.
  2. The engine will execute activity 3 - the workunit-write activity (TAKworkunitwrite).
  3. The workunit-write activity will start its input.
  4. The index-read activity will call the helper functions to obtain the filename, resolve the index, and create the filter.
  5. The workunit-write activity requests a row from its input.
  6. The index-read finds the first row, and calls the helper's transform() method to create an output row.
  7. The workunit-write activity persists the row to a buffer (using the serializer provided by the IOutputMetaData interface implemented by the class mx1).
  8. Back to step 5, workunit-write reading a row from its input, until end of file is returned (notified as two consecutive NULL rows.
  9. Workunit-write commits the results and finishes.

The execution generally switches back and forth between the code in the engines, and the members of the generated helper classes.

There are some other details of query execution that are worth highlighting:

Child Queries

: Some activities perform complicated operations on child datasets of the input rows. E.g. remove all duplicate people who are marked as living at this address. This will create a "child query" in the graph - i.e. a nested graph within a subgraph, which may be executed each time a new input row is processed by the containing activity. (The graph of activities for each child query is created at the same time as the parent activity. The activity instances are reinitialised and re-executed for each input row processed by the parent activity to minimise the create-time overhead.)

Other helper functions

: The generated code contains other functions that are used to describe the meta information for the rows processed within the graph. E.g. the following class describes the output from the index read activity:

cpp
struct mi1 : public CFixedOutputMetaData {
+    inline mi1() : CFixedOutputMetaData(120) {}
+    virtual const RtlTypeInfo * queryTypeInfo() const { return &ty1; }
+} mx1;
This represents a fixed size row that occupies 120 bytes. The object
+returned by the queryTypeInfo() function provides information about
+the types of the fields:
+
cpp
const RtlStringTypeInfo ty2(0x4,40);
+const RtlFieldStrInfo rf1("name",NULL,&ty2);
+const RtlStringTypeInfo ty3(0x4,80);
+const RtlFieldStrInfo rf2("address",NULL,&ty3);
+const RtlFieldInfo * const tl4[] = { &rf1,&rf2, 0 };
+const RtlRecordTypeInfo ty1(0xd,120,tl4);
I.e. a string column, length 40 called "name", followed by a
+string column, length 80 called "address". The interface
+IOutputMetaData in eclhelper.hpp is key to understanding how the
+rows are processed.
+

Inline dataset operations

: The rule mentioned at the start - that the generated code does not contain any knowledge of how to perform a particular dataset operation - does have one notable exception. Some operations on child datasets are very simple to implement, and more efficient if they are implemented using inline C++ code. (The generated code is smaller, and they avoid the overhead of setting up a child graph.) Examples include filtering and aggregating column values from a child dataset.

The full code in the different engines is more complicated than the simplified process outlined above, especially when it comes to executing dependencies, but the broad outline is the same.

Appendix

More information on the work done in the code generator to create the workunit can be found in ecl/eclcc/DOCUMENTATION.rst.

The C++ code can be generated as a single C++ file or multiple files. The system defaults to multiple C++ files, so that they can be compiled in parallel (and to avoid problems some compilers have with very large files). When multipe C++ files are generated the metadata classes and workflow classes are generated in the main module, and the activities are generated in the files suffixed with a number. It may be easier to understand the generated code if it is in one place. In which case, compile your query with the option -fspanMultipleCpp=0. Use -fsaveCppTempFiles to ensure the C++ files are not deleted (the C++ files will appear as helpers in the workunit details).

Key types and interfaces from eclhelper.hpp

IEclProcess

: The interface that is used by the workflow engine to execute the different workflow items in the generated code.

ThorActivityKind

: This enumeration contains one entry for each activity supported by the engines.

ICodeContext

: This interface is implemented by the engine, and provides a mechanism for the generated code to call back into the engine. For example resolveChildQuery() is used to obtain a reference to a child query that can then be executed later.

IOutputMetaData

: This interface is used to describe any meta data associated with the data being processed by the queries.

IHThorArg

: The base interface for defining information about an activity. Each activity has an associated interface that is derived from this interface. E.g. each instance of the sort activity will have a helper class implementing IHThorSortArg in the generated query. There is normally a corresponding base class for each interface in eclhelper_base.hpp that is used by the generated code e.g. CThorSortArg.

ARowBuilder

: This abstract base class is used by the transform functions to reserve memory for the rows that are created.

IEngineRowAllocator

: Used by the generated code to allocate rows and rowsets. Can also be used to release rows (or call the global function rtlReleaseRow()).

IGlobalCodeContext

: Provides access to functions that cannot be called inside a graph - i.e. can only be called from the global workflow code. Most functions are related to the internal implementation of particular workflow item types (e.g. persists).

Glossary

activity

: An activity is the basic unit of dataset processing implemented by the engines. Each activity corresponds to a node in the thor execution graph. Instances of the activities are connnected together to create the graph.

dll

: A dynamically loaded library. These correspond to shared objects in Linux (extension '.so'), dynamic libraries in Max OS X ('.dylib'), and dynamic link libraries in windows ('.dll').

superfile

: A composite file which allows a collection of files to be treated as a single compound file.

?What else should go here?

Full text of the workunit XML

The XML for a workunit can be viewed on the XML tag in eclwatch, or generated by compiling the ECL using the -wu option with eclcc. Alternatively eclcc -b -S can be used to generate the XML and the C++ at the same time (the output filenames are derived from the input name).

xml
<W_LOCAL buildVersion="internal_5.3.0-closedown0"
+    cloneable="1"
+    codeVersion="157"
+    eclVersion="5.3.0"
+    hash="2344844820"
+    state="completed"
+    xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance">
+    <Debug>
+        <addtimingtoworkunit>0</addtimingtoworkunit>
+        <debugnlp>1</debugnlp>
+        <expandpersistinputdependencies>1</expandpersistinputdependencies>
+        <forcegenerate>1</forcegenerate>
+        <noterecordsizeingraph>1</noterecordsizeingraph>
+        <regressiontest>1</regressiontest>
+        <showmetaingraph>1</showmetaingraph>
+        <showrecordcountingraph>1</showrecordcountingraph>
+        <spanmultiplecpp>0</spanmultiplecpp>
+        <targetclustertype>hthor</targetclustertype>
+    </Debug>
+    <Graphs>
+        <Graph name="graph1" type="activities">
+            <xgmml>
+                <graph wfid="2">
+                    <node id="1">
+                        <att>
+                            <graph>
+                                <att name="rootGraph" value="1" />
+                                <edge id="2_0" source="2" target="3" />
+                                <node id="2" label="Index Read&#10;&apos;names&apos;">
+                                    <att name="definition" value="workuniteg1.ecl(3,1)" />
+                                    <att name="name" value="results" />
+                                    <att name="_kind" value="77" />
+                                    <att name="ecl"
+                                        value="INDEX({ string40 name, string80 address }, &apos;names&apos;, fileposition(false));&#10;FILTER(KEYED(name = STORED(&apos;searchname&apos;)));&#10;" />
+                                    <att name="recordSize" value="120" />
+                                    <att name="predictedCount" value="0..?[disk]" />
+                                    <att name="_fileName" value="names" />
+                                </node>
+                                <node id="3" label="Output&#10;Result #1">
+                                    <att name="definition" value="workuniteg1.ecl(4,1)" />
+                                    <att name="_kind" value="16" />
+                                    <att name="ecl" value="OUTPUT(..., workunit);&#10;" />
+                                    <att name="recordSize" value="120" />
+                                </node>
+                            </graph>
+                        </att>
+                    </node>
+                </graph>
+            </xgmml>
+        </Graph>
+    </Graphs>
+    <Query fetchEntire="1">
+        <Associated>
+            <File desc="workuniteg1.ecl.cpp"
+                filename="c:\\regressout\\workuniteg1.ecl.cpp"
+                ip="10.121.159.73"
+                type="cpp" />
+        </Associated>
+    </Query>
+    <Results>
+        <Result isScalar="0"
+            name="Result 1"
+            recordSizeEntry="mf1"
+            rowLimit="-1"
+            sequence="0">
+            <SchemaRaw xsi:type="SOAP-ENC:base64">
+                name&#xe000;&#xe004;(&#xe000;&#xe000;&#xe000;&#xe001;ascii&#xe000;&#xe001;ascii&#xe000;address&#xe000;&#xe004;P&#xe000;&#xe000;&#xe000;&#xe001;ascii&#xe000;&#xe001;ascii&#xe000;&#xe000;&#xe018;%&#xe000;&#xe000;&#xe000;{
+                string40 name, string80 address };&#10; </SchemaRaw>
+        </Result>
+        <Result name="Result 2" sequence="1">
+            <SchemaRaw xsi:type="SOAP-ENC:base64">
+                Result_2&#xe000;&#xe004;&#241;&#255;&#255;&#255;&#xe001;ascii&#xe000;&#xe001;ascii&#xe000;&#xe000;&#xe018;&#xe000;&#xe000;&#xe000;&#xe000;
+            </SchemaRaw>
+        </Result>
+    </Results>
+    <Statistics>
+        <Statistic c="eclcc"
+            count="1"
+            creator="eclcc"
+            kind="SizePeakMemory"
+            s="compile"
+            scope=">compile"
+            ts="1428933081084000"
+            unit="sz"
+            value="27885568" />
+    </Statistics>
+    <Variables>
+        <Variable name="searchname">
+            <SchemaRaw xsi:type="SOAP-ENC:base64">
+                searchname&#xe000;&#xe004;&#241;&#255;&#255;&#255;&#xe001;ascii&#xe000;&#xe001;ascii&#xe000;&#xe000;&#xe018;&#xe000;&#xe000;&#xe000;&#xe000;
+            </SchemaRaw>
+        </Variable>
+    </Variables>
+    <Workflow>
+        <Item mode="normal"
+            state="null"
+            type="normal"
+            wfid="1" />
+        <Item mode="normal"
+            state="reqd"
+            type="normal"
+            wfid="2">
+            <Dependency wfid="1" />
+            <Schedule />
+        </Item>
+    </Workflow>
+</W_LOCAL>

Full contents of the generated C++ (as a single file)

cpp
/* Template for generating thor/hthor/roxie output */
+#include "eclinclude4.hpp"
+#include "eclrtl.hpp"
+#include "rtlkey.hpp"
+
+extern RTL_API void rtlStrToStr(size32_t lenTgt,void * tgt,size32_t lenSrc,const void * src);
+extern RTL_API int rtlCompareStrStr(size32_t lenL,const char * l,size32_t lenR,const char * r);
+
+
+const RtlStringTypeInfo ty2(0x4,40);
+const RtlFieldStrInfo rf1("name",NULL,&ty2);
+const RtlStringTypeInfo ty3(0x4,80);
+const RtlFieldStrInfo rf2("address",NULL,&ty3);
+const RtlFieldInfo * const tl4[] = { &rf1,&rf2, 0 };
+const RtlRecordTypeInfo ty1(0xd,120,tl4);
+void getLayout5(size32_t & __lenResult, void * & __result, IResourceContext * ctx) {
+    rtlStrToDataX(__lenResult,__result,87U,"\\000R\\000\\000\\000\\001x\\000\\000\\000\\002\\000\\000\\000\\003\\004\\000\\000\\000name\\004(\\000\\000\\000\\001ascii\\000\\001ascii\\000\\000\\000\\000\\000\\000\\003\\007\\000\\000\\000address\\004P\\000\\000\\000\\001ascii\\000\\001ascii\\000\\000\\000\\000\\000\\000\\002\\000\\000\\000");
+}
+struct mi1 : public CFixedOutputMetaData {
+    inline mi1() : CFixedOutputMetaData(120) {}
+    virtual const RtlTypeInfo * queryTypeInfo() const { return &ty1; }
+} mx1;
+extern "C" ECL_API IOutputMetaData * mf1() { mx1.Link(); return &mx1; }
+
+struct cAc2 : public CThorIndexReadArg {
+    virtual unsigned getFormatCrc() {
+        return 470622073U;
+    }
+    virtual bool getIndexLayout(size32_t & __lenResult, void * & __result) { getLayout5(__lenResult, __result, ctx); return true; }
+    virtual IOutputMetaData * queryDiskRecordSize() { return &mx1; }
+    virtual IOutputMetaData * queryOutputMeta() { return &mx1; }
+    virtual void onCreate(ICodeContext * _ctx, IHThorArg *, MemoryBuffer * in) {
+        ctx = _ctx;
+        ctx->getResultString(v2,v1.refstr(),"searchname",4294967295U);
+    }
+    rtlDataAttr v1;
+    unsigned v2;
+    virtual const char * getFileName() {
+        return "names";
+    }
+    virtual void createSegmentMonitors(IIndexReadContext *irc) {
+        Owned<IStringSet> set3;
+        set3.setown(createRtlStringSet(40));
+        char v4[40];
+        rtlStrToStr(40U,v4,v2,v1.getstr());
+        if (rtlCompareStrStr(v2,v1.getstr(),40U,v4) == 0) {
+            set3->addRange(v4,v4);
+        }
+        irc->append(createKeySegmentMonitor(false, set3.getClear(), 0, 40));
+        irc->append(createWildKeySegmentMonitor(40, 80));
+    }
+    virtual size32_t transform(ARowBuilder & crSelf, const void * _left) {
+        crSelf.getSelf();
+        unsigned char * left = (unsigned char *)_left;
+        memcpy(crSelf.row() + 0U,left + 0U,120U);
+        return 120U;
+    }
+};
+extern "C" ECL_API IHThorArg * fAc2() { return new cAc2; }
+struct cAc3 : public CThorWorkUnitWriteArg {
+    virtual int getSequence() { return 0; }
+    virtual IOutputMetaData * queryOutputMeta() { return &mx1; }
+    virtual void serializeXml(const byte * self, IXmlWriter & out) {
+        mx1.toXML(self, out);
+    }
+};
+extern "C" ECL_API IHThorArg * fAc3() { return new cAc3; }
+
+
+struct MyEclProcess : public EclProcess {
+    virtual unsigned getActivityVersion() const { return ACTIVITY_INTERFACE_VERSION; }
+    virtual int perform(IGlobalCodeContext * gctx, unsigned wfid) {
+        ICodeContext * ctx;
+        ctx = gctx->queryCodeContext();
+        switch (wfid) {
+            case 1U:
+                if (!gctx->isResult("searchname",4294967295U)) {
+                    ctx->setResultString("searchname",4294967295U,5U,"Smith");
+                }
+                break;
+            case 2U: {
+                ctx->executeGraph("graph1",false,0,NULL);
+                ctx->setResultString(0,1U,5U,"Done!");
+            }
+            break;
+        }
+        return 2U;
+    }
+};
+
+
+extern "C" ECL_API IEclProcess* createProcess()
+{
+
+    return new MyEclProcess;
+}
`,170),e=[h];function l(k,p,r,E,d,g){return a(),i("div",null,e)}const c=s(n,[["render",l]]);export{y as __pageData,c as default}; diff --git a/assets/devdoc_Workunits.md.jFcYNljW.lean.js b/assets/devdoc_Workunits.md.jFcYNljW.lean.js new file mode 100644 index 00000000000..0c9abadf36e --- /dev/null +++ b/assets/devdoc_Workunits.md.jFcYNljW.lean.js @@ -0,0 +1 @@ +import{_ as s,c as i,o as a,V as t}from"./chunks/framework.gBlNPWt_.js";const y=JSON.parse('{"title":"Understanding workunits","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/Workunits.md","filePath":"devdoc/Workunits.md","lastUpdated":1721825996000}'),n={name:"devdoc/Workunits.md"},h=t("",170),e=[h];function l(k,p,r,E,d,g){return a(),i("div",null,e)}const c=s(n,[["render",l]]);export{y as __pageData,c as default}; diff --git a/assets/devdoc_newActivity.md.SP0TgdQz.js b/assets/devdoc_newActivity.md.SP0TgdQz.js new file mode 100644 index 00000000000..3177fee4f77 --- /dev/null +++ b/assets/devdoc_newActivity.md.SP0TgdQz.js @@ -0,0 +1,11 @@ +import{_ as e,c as t,o as i,V as a}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"Quantile 1 - What is it?","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/newActivity.md","filePath":"devdoc/newActivity.md","lastUpdated":1721825996000}'),o={name:"devdoc/newActivity.md"},s=a(`

Quantile 1 - What is it?

This series of blog posts started life as a series of walk-throughs and brainstorming sessions at a team offsite. This series will look at adding a new activity to the system. The idea is to give an walk through of the work involved, to highlight the different areas that need changing, and hopefully encourage others to add their own activities. In parallel with the description in this blog there is a series of commits to the github repository that correspond to the different stages in adding the activity. Once the blog is completed, the text will also be checked into the source control tree for future reference.

The new activity is going to be a QUANTILE activity, which can be used to find the records that split a dataset into equal sized blocks. Two common uses are to find the median of a set of data (split into 2) or percentiles (split into 100). It can also be used to split a dataset for distribution across the nodes in a system. One hope is that the classes used to implement quantile in Thor can also be used to improve the performance of the global sort operation.

It may seem fatuous, but the first task in adding any activity to the system is to work out what that activity is going to do! You can approach this in an iterative manner - starting with a minimal set of functionality and adding options as you think of them - or start with a more complete initial design. We have used both approaches in the past to add capabilities to the HPCC system, but on this occasion we will be starting from a more complete design - the conclusion of our initial design discussion:

"What are the inputs, options and capabilities that might be useful in a QUANTILE activity?"

The discussion produced the following items:

  • Which dataset is being processed?

    This is always required and should be the first argument to the activity.

  • How many parts to split the dataset into?

    This is always required, so it should be the next argument to the activity.

  • Which fields are being used to order (and split) the dataset?

    Again this is always required, so the list of fields should follow the number of partitions.

  • Which fields are returned?

    Normally the input row, but often it would be useful for the output to include details of which quantile a row corresponds to. To allow this an optional transform could be passed the input row as LEFT and the quantile number as COUNTER.

  • How about first and last rows in the dataset?

    Sometimes it is also useful to know the first and last rows. Add flags to allow them to be optionally returned.

  • How do you cope with too few input rows (including an empty input)?

    After some discussion we decided that QUANTILE should always return the number of parts requested. If there were fewer items in the input they would be duplicated as appropriate. We should provide a DEDUP flag for the situations when that is not desired. If there is an empty dataset as input then the default (blank) row will be created.

  • Should all rows have the same weighting?

    Generally you want the same weighting for each row. However, if you are using QUANTILE to split your dataset, and the cost of the next operation depends on some feature of the row (e.g., the frequency of the firstname) then you may want to weight the rows differently.

  • What if we are only interested in the 5th and 95th centiles?

    We could optionally allow a set of values to be selected from the results.

There were also some implementation details concluded from the discussions:

  • How accurate should the results be?

    The simplest implementation of QUANTILE (sort and then select the correct rows) will always produce accurate results. However, there may be some implementations that can produce an approximate answer more quickly. Therefore we could add a SKEW attribute to allow early termination.

  • Does the implementation need to be stable?

    In other words, if there are rows with identical values for the ordering fields, but other fields not part of the ordering with different values, does it matter which of those rows are returned? Does the relative order within those matching rows matter?

    The general principle in the HPCC system is that sort operations should be stable, and that where possible activities return consistent, reproducible results. However, that often has a cost - either in performance or memory consumption. The design discussion highlighted the fact that if all the fields from the row are included in the sort order then the relative order does not matter because the duplicate rows will be indistinguishable. (This is also true for sorts, and following the discussion an optimization was added to 5.2 to take advantage of this.) For the QUANTILE activity we will add an ECL flag, but the code generator should also aim to spot this automatically.

  • Returning counts of the numbers in each quantile might be interesting.

    This has little value when the results are exact, but may be more useful when a SKEW is specified to allow an approximate answer, or if a dataset might have a vast numbers of duplicates. It is possibly something to add to a future version of the activity. For an approximate answer, calculating the counts is likely to add an additional cost to the implementation, so the target engine should be informed if this is required.

  • Is the output always sorted by the partition fields?

    If this naturally falls out of the implementations then it would be worth including it in the specification. Initially we will assume not, but will revisit after it has been implemented.

After all the discussions we arrived at the following syntax:

QUANTILE(<dataset>, <number-of-ranges>, { sort-order } [, <transform>(LEFT, COUNTER)]
+        [,FIRST][,LAST][,SKEW(<n>)][,UNSTABLE][,SCORE(<score>)][,RANGE(set)][,DEDUP][,LOCAL]
+
+FIRST - Match the first row in the input dataset (as quantile 0)
+LAST -  Match the last row in the input dataset (as quantile <n>)
+SKEW -  The maximum deviation from the correct results allowed.  Defaults to 0.
+UNSTABLE - Is the order of the original input values unimportant?
+SCORE - What weighting should be applied for each row.  Defaults to 1.
+RANGE - Which quantiles should actually be returned.  (Defaults to ALL).
+DEDUP - Avoid returning a match for an input row more than once.
+

We also summarised a few implementation details:

  • The activity needs to be available in GLOBAL, LOCAL and GROUPED variants.
  • The code generator should derive UNSTABLE if no non-sort fields are returned.
  • Flags to indicate if a score/range is required.
  • Flag to indicate if a transform is required.

Finally, deciding on the name of the activity took almost as long as designing it!

The end result of this process was summarised in a JIRA issue: https://track.hpccsystems.com/browse/HPCC-12267, which contains details of the desired syntax and semantics. It also contains some details of the next blog topic - test cases.

Incidentally, a question that arose from of the design discussion was "What ECL can we use if we want to annotate a dataset with partition points?". Ideally the user needs a join activity which walks through a table of rows, and matches against the first row that contains key values less than or equal to the values in the search row. There are other situations where that operation would also be useful. Our conclusion was that the system does not have a simple way to achieve that, and that it was a deficiency in the current system, so another JIRA was created (see https://track.hpccsystems.com/browse/HPCC-13016). This is often how the design discussions proceed, with discussions in one area leading to new ideas in another. Similarly we concluded it would be useful to distribute rows in a dataset based on a partition (see https://track.hpccsystems.com/browse/HPCC-13260).

Quantile 2 - Test cases

When adding new features to the system, or changing the code generator, the first step is often to write some ECL test cases. They have proved very useful for several reasons:

  • Developing the test cases can help clarify issues, and other details that the implementation needs to take into account. (E.g., what happens if the input dataset is empty?)
  • They provide something concrete to aim towards when implementing the feature.
  • They provide a set of milestones to show progress.
  • They can be used to check the implementation on the different engines.

As part of the design discussion we also started to create a list of useful test cases (they follow below in the order they were discussed). The tests perform varying functions. Some of the tests are checking that the core functionality works correctly, while others check unusual situations and that strange boundary cases are covered. The tests are not exhaustive, but they are a good starting point and new tests can be added as the implementation progresses.

The following is the list of tests that should be created as part of implementing this activity:

  1. Compare with values extracted from a SORT. Useful to check the implementation, but also to ensure we clearly define which results we are expecting.
  2. QUANTILE with a number-of-ranges = 1, 0, and a very large number. Should also test the number of ranges can be dynamic as well as a constant.
  3. Empty dataset as input.
  4. All input entries are duplicates.
  5. Dataset smaller than number of ranges.
  6. Input sorted and reverse sorted.
  7. Normal data with small number of entries.
  8. Duplicates in the input dataset that cause empty ranges.
  9. Random distribution of numbers without duplicates.
  10. Local and grouped cases.
  11. SKEW that fails.
  12. Test scoring functions.
  13. Testing different skews that work on the same dataset.
  14. An example that uses all the keywords.
  15. Examples that do and do not have extra fields not included in the sort order. (Check that the unstable flag is correctly deduced.)
  16. Globally partitioned already (e.g., globally sorted). All partition points on a single node.
  17. Apply quantile to a dataset, and also to the same dataset that has been reordered/distributed. Check the resulting quantiles are the same.
  18. Calculate just the 5 and 95 centiles from a dataset.
  19. Check a non constant number of splits (and also in a child query where it depends on the parent row).
  20. A transform that does something interesting to the sort order. (Check any order is tracked correctly.)
  21. Check the counts are correct for grouped and local operations.
  22. Call in a child query with options that depend on the parent row (e.g., num partitions).
  23. Split points that fall in the middle of two items.
  24. No input rows and DEDUP attribute specified.

Ideally any test cases for features should be included in the runtime regression suite, which is found in the testing/regress directory in the github repository. Tests that check invalid syntax should go in the compiler regression suite (ecl/regress). Commit https://github.com/ghalliday/HPCC-Platform/commit/d75e6b40e3503f851265670a27889d8adc73f645 contains the test cases so far. Note, the test examples in that commit do not yet cover all the cases above. Before the final pull request for the feature is merged the list above should be revisited and the test suite extended to include any missing tests.

In practice it may be easier to write the test cases in parallel with implementing the parser -since that allows you to check their syntax. Some of the examples in the commit were created before work was started on the parser, others during, and some while implementing the feature itself.

Quantile 3 - The parser

The first stage in implementing QUANTILE will be to add it to the parser. This can sometimes highlight issues with the syntax and cause revisions to the design. In this case there were two technical issues integrating the syntax into the grammar. (If you are not interested in shift/reduce conflicts you may want to skip a few paragraphs and jump to the walkthrough of the changes.)

Originally, the optional transform was specified inside an attribute, e.g., something like OUTPUT(transform). However, this was not very consistent with the way that other transforms were implemented, so the syntax was updated so it became an optional transform following the partition field list.

When the syntax was added to the grammar we hit another problem: Currently, a single production (sortList) in the grammar is used for matching sort orders. As well as accepting fields from a dataset the sort order production has been extended to accept any named attribute that can follow a sort order (e.g., LOCAL). This is because (with one token lookahead) it is ambiguous where the sort order finishes and the list of attributes begins. Trying to include transforms in those productions revealed other problems:

  • If a production has a sortList followed by a transform (or attribute) then it introduces a shift/reduce error on ','. To avoid the ambiguity all trailing attributes or values need to be included in the sortList.
  • Including a transform production in the sortList elements causes problems with other transform disambiguation (e.g., DATASET[x] and AGGREGATE).
  • We could require an attribute around the transform e.g., OUTPUT(transform), but that does not really fit in with other activities in the language.
  • We could change the parameter order, e.g., move the transform earlier, but that would make the syntax counter-intuitive.
  • We could require { } around the list - but this is inconsistent with some of the other sort orders.

In order to make some progress I elected to choose the last option and require the sort order to be included in curly braces. There are already a couple of activities - subsort and a form of atmost that similarly require them (and if redesigning ECL from scratch I would be tempted to require them everywhere). The final syntax is something that will need further discussion as part of the review of the pull request though, and may need to be revisited.

Having decided how to solve the ambiguities in the grammar, the following is a walkthrough of the changes that were made as part of commit https://github.com/ghalliday/HPCC-Platform/commit/3d623d1c6cd151a0a5608aa20ae4739a008f6e44:

  • no_quantile in hqlexpr.hpp

    The ECL query is represented by a graph of "expression" nodes - each has a "kind" that comes from the enumeration _node_operator. The first requirement is to add a new enumeration to represent the new activity - in this case we elected to reuse an unused placeholder. (These placeholders correspond to some old operators that are no longer supported. They have not been removed because the other elements in the enumeration need to keep the same values since they are used for calculating derived persistent values e.g., the hashes for persists.)

  • New attribute names in hqlatoms.

    The quantile activity introduces some new attribute names that have not been used before. All names are represented in an atom table, so the code in hqlatoms.hpp/cpp is updated to define the new atoms.

  • Properties of no_quantile

    There are various places that need to be updated to allow the system to know about the properties of the new operator:

    • hqlattr

      This contains code to calculate derived attributes. The first entry in the case statement is currently unused (the function should be removed). The second, inside calcRowInformation(), is used to predict how many rows are generated by this activity. This information is percolated through the graph and is used for optimizations, and input counts can be used to select the best implementation for a particular activity.

    • hqlexpr

      Most changes are relatively simple including the text for the operator, whether it is constant, and the number of dataset arguments it has. One key function is getChildDatasetType() that indicates the kind of dataset arguments the operator has, which in turn controls how LEFT/RIGHT are interpreted. In this case some of the activity arguments (e.g., the number of quantiles) implicitly use fields within the parent dataset, and the transform uses LEFT, so the operator returns childdataset_datasetleft.

    • hqlir

      This entry is used for generating an intermediate representation of the graph. This can be useful for debugging issues. (Running eclcc with the logging options "--logfile xxx" and "--logdetail 999" will include details of the expression tree at each point in the code generation process in the log file. Also defining -ftraceIR will output the graphs in the IR format.)

    • hqlfold

      This is the constant folder. At the moment the only change is to ensure that fields that are assigned constants within the transform are processed correctly. Future work could add code to optimize quantile applied to an empty dataset, or selecting 1 division.

    • hqlmeta

      Similar to the functions in hqlattr that calculate derived attributes, these functions are used to calculate how the rows coming out of an activity are sorted, grouped and distributed. It is vital to only preserve information that is guaranteed to be true - otherwise invalid optimizations might be performed on the rest of the expression tree.

    • reservedwords.cpp

      A new entry indicating which category the keyword belongs to.

Finally we have the changes to the parser to recognise the new syntax:

  • hqllex.l

    This file contains the lexer that breaks the ecl file into tokens. There are two new tokens - QUANTILE and SCORE.

  • Hqlgram.y

    This file contains the grammar that matches the language. There are two productions - one that matches the version of QUANTILE with a transform and one without. (Two productions are used instead of an optional transform to avoid shift/reduce errors.)

  • hqlgram2.cpp

    This contains the bulk of the code that is executed by the productions in the grammar. Changes here include new entries added to a case statement to get the text for the new tokens, and a new entry in the simplify() call. This helps reduce the number of valid tokens that could follow when reporting a syntax error.

Looking back over those changes, one reflection is that there are lots of different places that need to be changed. How does a programmer know which functions need to change, and what happens if some are missed? In this example, the locations were found by searching for an activity with a similar syntax e.g., no_soapcall_ds or no_normalize.

It is too easy to miss something, especially for somebody new to the code - although if you do then you will trigger a runtime internal error. It would be much better if the code was refactored so that the bulk of the changes were in one place. (See JIRA https://track.hpccsystems.com/browse/HPCC-13434 that has been added to track improvement of the situation.)

With these changes implemented the examples from the previous pull request now syntax check. The next stage in the process involves thinking through the details of how the activity will be implemented.

Quantile 4 - The engine interface.

The next stage in adding a new activity to the system is to define the interface between the generated code and the engines. The important file for this stage is rtl/include/eclhelper.hpp, which contains the interfaces between the engines and the generated code. These interfaces define the information required by the engines to customize each of the different activities. The changes that define the interface for quantile are found in commit https://github.com/ghalliday/HPCC-Platform/commit/06534d8e9962637fe9a5188d1cc4ab32c3925010.

Adding a quantile activity involves the following changes:

  • ThorActivityKind - TAKquantile

    Each activity that the engines support has an entry in this enumeration. This value is stored in the graph as the _kind attribute of the node.

  • ActivityInterfaceEnum - TAIquantilearg_1

    This enumeration in combination with the selectInterface() member of IHThorArg provides a mechanism for helper interfaces to be extended while preserving backwards compatibility with older workunits. The mechanism is rarely used (but valuable when it is), and adding a new activity only requires a single new entry.

  • IHThorArg

    This is the base interface that all activity interfaces are derived from. This interface does not need to change, but it is worth noting because each activity defines a specialized version of it. The names of the specialised interfaces follow a pattern; in this case the new interface is IHThorQuantileArg.

  • IHThorQuantileArg

    The following is an outline of the new member functions, with comments on their use:

    • getFlags()

      Many of the interfaces have a getFlags() function. It provides a concise way of returning several Boolean options in a single call - provided those options do not change during the execution of the activity. The flags are normally defined with explicit values in an enumeration before the interface. The labels often follow the pattern T<First-letter-of-activity>F<lowercase-name>, i.e. TQFxxx ~= Thor-Quantile-Flag-XXX.

    • getNumDivisions()

      Returns how many parts to split the dataset into.

    • getSkew()

      Corresponds to the SKEW() attribute.

    • queryCompare()

      Returns an implementation of the interface used to compare two rows.

    • createDefault(rowBuilder)

      A function used to create a default row - used if there are no input rows.

    • transform(rowBuilder, _left, _counter)

      The function to create the output record from the input record and the partition number (passed as counter).

    • getScore(_left)

      What weighting should be given to this row?

    • getRange(isAll, tlen, tgt)

      Corresponds to the RANGE attribute.

Note that the different engines all use the same specialised interface - it contains a superset of the functions required by the different targets. Occasionally some of the engines do not need to use some of the functions (e.g., to serialize information between nodes) so the code generator may output empty implementations.

For each interface defined in eclhelper.hpp there is a base implementation class defined in eclhelper_base.hpp. The classes generated for each activity in a query by the code generator are derived from one of these base classes. Therefore we need to create a corresponding new class CThorQuantileArg. It often provides default implementations for some of the helper functions to help reduce the size of the generated code (e.g., getScore returning 1).

Often the process of designing the helper interface is dynamic. As the implementation is created, new options or possibilities for optimizations appear. These require extensions and changes to the helper interface in order to be implemented by the engines. Once the initial interface has been agreed, work on the code generator and the engines can proceeded in parallel. (It is equally possible to design this interface before any work on the parser begins, allowing more work to overlap.)

There are some more details on the contents of thorhelper.hpp in the documentation ecl/eclcc/WORKUNIT.rst within the HPCC repository.

Quantile 5 - The code generator

Adding a new activity to the code generator is (surprisingly!) a relatively simple operation. The process is more complicated if the activity also requires an implementation that generates inline C++, but that only applies to a small subset of very simple activities, e.g., filter, aggregate. Changes to the code generator also tend to be more substantial if you add a new type, but that is also not the case for the quantile activity.

For quantile, the only change required is to add a function that generates an implementation of the helper class. The code for all the different activities follows a very similar pattern - generate input activities, generate the helper for this activity, and link the input activities to this new activity. It is often easiest to copy the boiler-plate code from a similar activity (e.g., sort) and then adapt it. (Yes, some of this code could also be refactored... any volunteers?) There are a large number of helper functions available to help generate transforms and other member functions, which also simplifies the process.

The new code is found in commit https://github.com/ghalliday/HPCC-Platform/commit/47f850d827f1655fd6a78fb9c07f1e911b708175.

Most of the code should be self explanatory, but one item is worth highlighting. The code generator builds up a structure in memory that represents the C++ code that is being generated. The BuildCtx class is used to represent a location within that generated code where new code can be inserted. The instance variable contains several BuildCtx members that are used to represent locations to generate code within the helper class (classctx, nestedctx, createctx and startctx). They are used for different purposes:

  • classctx

    Used to generate any member functions that can be called as soon as the helper object has been created, e.g., getFlags().

  • nestedctx

    Used to generate nested member classes and objects - e.g., comparison classes.

  • startctx

    Any function that may return a value that depends on the context/parent activity. For example if QUANTILE is used inside the TRANSFORM of a PROJECT, the number of partition points may depend on a field in the LEFT row of the PROJECT. Therefore the getNumDivisions() member function needs to be generated inside instance->startctx. These functions can only be called by the engine after onCreate() and onStart() have been called to set up the current context.

  • createctx

    Really, this is a historical artefact from many years ago. It was originally used for functions that could be dependent on a global expression, but not a parent row. Almost all such restrictions have since been removed, and those that remain should probably be replaced with either classctx or startctx.

The only other change is to extend the switch statement in common/thorcommon/thorcommon.cpp to add a text description of the activity.

Quantile 6 - Roxie

With the code generator outputting all the information we need, we can now implement the activity in one of the engines. (As I mentioned previously, in practice this is often done in parallel with adding it to the code generator.) Roxie and hThor are the best engines to start with because most of their activities run on a single node - so the implementations tend to be less complicated. It is also relatively easy to debug them, by compiling to create a stand-alone executable, and then running that executable inside a debugger. The following description walks-through the roxie changes:

The changes have been split into two commits to make the code changes easier to follow. The first commit (https://github.com/ghalliday/HPCC-Platform/commit/30da006df9ae01c9aa784e91129457883e9bb8f3) adds the simplest implementation of the activity:

Code is added to ccdquery to process the new TAKquantile activity kind, and create a factory object of the correct type. The implementation of the factory class is relatively simple - it primarily contains a method for creating an instance of the activity class. Some factories create instances of the helper and cache any information that never changes (in this case the value returned by getFlags(), which is a very marginal optimization).

The classes that implement the existing sort algorithms are extended to return the sorted array in a single call. This allows the quicksort variants to be implemented more efficiently.

The class CRoxieServerQuantileActivity contains the code for implementing the quantile activity. It has the following methods:

  • Constructor

    Extracts any information from the helper that does not vary, and initializes all member variables.

  • start()

    This function is called before the graph is executed. It evaluates any helper methods that might vary from execution to execution (e.g., getRange(), numDivisions()), but which do not depend on the current row.

  • reset()

    Called when a graph has finished executing - after an activity has finished processing all its records. It is used to clean up any variables, and restore the activity ready for processing again (e.g., if it is inside a child query).

  • needsAllocator()

    Returns true if this activity creates new rows.

  • nextInGroup()

    The main function in the activity. This function is called by whichever activity is next in the graph to request a new row from the quantile activity. The functions should be designed so they return the next row as quickly as possible, and delay any processing until it is needed. In this case the input is not read and sorted until the first row is requested.

    Note, the call to the helper.transform() returns the size of the resulting row, and returns zero if the row should be skipped. The call to finaliseRowClear() after a successful row creation is there to indicate that the row can no longer be modified, and ensures that any child rows will be correctly freed when the row is freed.

    The function also contains extra logic to ensure that groups are implemented correctly. The end of a group is marked by returning a single NULL row, the end of the dataset by two contiguous NULL rows. It is important to ensure that a group that has all its output rows skipped doesn't return two NULLs in a row - hence the checks for anyThisGroup.

With those changes in place, the second commit https://github.com/ghalliday/HPCC-Platform/commit/aeaa209092ea1af9660c6908062c1b0b9acff36b adds support for the RANGE, FIRST, and LAST attributes. It also optimizes the cases where the input is already sorted, and the version of QUANTILE which does not include a transform. (If you are looking at the change in github then it is useful to ignore whitespace changes by appending ?w=1 to the URL). The main changes are

  • Extra helper methods called in start() to obtain the range.
  • Optimize the situation where the input is known to be sorted by reading the input rows directly into the "sorted" array.
  • Extra checks to see if this quantile should be included in the output (FIRST,LAST,RANGE,DEDUP)
  • An optimization to link the incoming row if the transform does not modify it, by testing the TQFneedtransform flag.

Quantile 7 - Possible roxie improvements

TBD...

hthor - trivial,sharing code and deprecated.

Discussion, of possible improvements.

Hoares' algorithm.

Ln2(n) < 4k?

SKEW and Hoares

Ordered RANGE. Calc offsets from the quantile (see testing/regress/ecl/xxxxx?)

SCORE

Quantile 8 - Thor

TBD

Basic activity structure

Locally sorting and allowing the inputs to spill.

The partitioning approach

Classes

Skew

Optimizations

`,78),n=[s];function r(l,h,d,c,p,u){return i(),t("div",null,n)}const g=e(o,[["render",r]]);export{f as __pageData,g as default}; diff --git a/assets/devdoc_newActivity.md.SP0TgdQz.lean.js b/assets/devdoc_newActivity.md.SP0TgdQz.lean.js new file mode 100644 index 00000000000..55beffad009 --- /dev/null +++ b/assets/devdoc_newActivity.md.SP0TgdQz.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as i,V as a}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"Quantile 1 - What is it?","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/newActivity.md","filePath":"devdoc/newActivity.md","lastUpdated":1721825996000}'),o={name:"devdoc/newActivity.md"},s=a("",78),n=[s];function r(l,h,d,c,p,u){return i(),t("div",null,n)}const g=e(o,[["render",r]]);export{f as __pageData,g as default}; diff --git a/assets/devdoc_roxie.md.ucbDhtEB.js b/assets/devdoc_roxie.md.ucbDhtEB.js new file mode 100644 index 00000000000..2181403eb98 --- /dev/null +++ b/assets/devdoc_roxie.md.ucbDhtEB.js @@ -0,0 +1,8 @@ +import{_ as e,c as t,o as a,V as o}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"Everything you ever wanted to know about Roxie","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/roxie.md","filePath":"devdoc/roxie.md","lastUpdated":1721825996000}'),i={name:"devdoc/roxie.md"},n=o(`

Everything you ever wanted to know about Roxie

Why did I create it?

Because I could. Many of the pieces needed for Roxie were already created for use in other systems – ECL language, code generator, index creation in Thor, etc. Indexes could be used by Moxie, but that relied on monolithic single-part indexes, was single-threaded (forked a process per query) and had limited ability to do any queries beyond simple index lookups. ECL had already proved itself as a way to express more complex queries concisely, and the concept of doing the processing next to the data had been proved in hOle and Thor, so Roxie – using the same concept for online queries using indexes – was a natural extension of that, reusing the existing index creation and code generation, but adding a new run-time engine geared towards pre-deployed queries and sending index lookup requests to the node holding the index data.

The code generator creates a graph (DAG) representing the query, with one node per activity and links representing the inputs and dependencies. There is also a helper class for each activity.

Roxie loads this graph for all published queries, creating a factory for each activity and recording how they are linked. When a query is executed, the factories create the activity instances and link them together. All activities without output activities (known as ‘sinks’) are then executed (often on parallel threads), and will typically result in a value being written to a workunit, to the socket that the query was received in, or to a global “context” area where subsequent parts of the query might read it.

Data is pulled through the activity graph, by any activity that wants a row from its input requesting it. Evaluation is therefore lazy, with data only calculated as needed. However, to reduce latency in some cases activities will prepare results ahead of when they are requested – for example an index read activity will send the request to the agent(s) as soon as it is started rather than waiting for the data to be requested by its downstream activity. This may result in wasted work, and in some cases may result in data coming back from an agent after the requesting query has completed after discovering it didn’t need it after all – this results in the dreaded “NO msg collator found – using default” tracing (not an error but may be indicative of a query that could use some tuning).

Before requesting rows from an input, it should be started, and when no more rows are required it should be stopped. It should be reset before destruction or reuse (for example for the next row in a child query).

Balancing the desire to reduce latency with the desire to avoid wasted work can be tricky. Conditional activities (IF etc) will not start their unused inputs, so that queries can be written that do different index reads depending on the input. There is also the concept of a “delayed start” activity – I would need to look at the code to remind myself of how those are used.

Where are the Dragons?

Splitter activities are a bit painful – they may result in arbitrary buffering of the data consumed by one output until another output is ready to request a row. It’s particularly complex when some of the outputs don’t start at all – the splitter needs to keep track of how many of the inputs have been started and stopped (an input that is not going to be used must be stopped, so that splitters know not to keep data for them). Tracking these start/stop/reset calls accurately is very important otherwise you can end up with weird bugs including potential crashes when activities are destroyed. Therefore we report errors if the counts don’t tally properly at the end of a query – but working out where a call was missed is often not trivial. Usually it’s because of an exception thrown from an unexpected place, e.g. midway through starting.

Note that row requests from the activities above a splitter may execute on whichever thread downstream from the splitter happens to need that particular row first.

The splitter code for tracking whether any of the downstream activities still need a row is a bit hairy/inefficient, IIRC. There may be scope to optimize (but I would recommend adding some good unit test cases first!)

How does “I beat you to it” work?

When there are multiple agents fulfilling data on a channel, work is shared among them via a hash of the packet header, which is used to determine which agent should work on that packet. However, if it doesn’t start working on it within a short period (either because the node is down, or because it is too busy on other in-flight requests), then another node may take over. The IBYTI messages are used to indicate that a node has started to work on a packet and therefore there is no need for a secondary to take over.

The priority of agents as determined by the packet hash is also used to determine how to proceed if an IBYTI is received after starting to work on a request. If the IBYTI is from a lower priority buddy (sub-channel) then it is ignored, if it’s from a higher priority one then the processing will be abandoned.

When multicast is enabled, the IBYTI is sent on the same multicast channel as the original packet (and care is needed to ignore ones sent by yourself). Otherwise it is sent to all buddy IPs.

Nodes keep track of how often they have had to step in for a supposedly higher priority node, and reduce their wait time before stepping in each time this happens, so if a node has crashed then the buddy nodes will end up taking over without every packet being delayed.

(QUESTION – does this result in the load on the first node after the failed node getting double the load?)

Newer code for cloud systems (where the topology may change dynamically) send the information about the buddy nodes in the packet header rather than assuming all nodes already have a consistent version of that information. This ensures that all agents are using the same assumptions about buddy nodes and their ordering.

All about index compression

An index is basically a big sorted table of the keyed fields, divided into pages, with an index of the last row from each page used to be able to locate pages quickly. The bottom level pages (‘leaves’) may also contain payload fields that do not form part of the lookup but can be returned with it.

Typical usage within LN Risk tends to lean towards one of two cases:

  • Many keyed fields with a single “ID” field in the payload
  • A single “ID” field in the key with many “PII” fields in the payload.

There may be some other cases of note too though – e.g. an error code lookup file which heavily, used, or Boolean search logic keys using smart-stepping to implement boolean search conditions.

It is necessary to store the index pages on disk compressed – they are very compressible – but decompression can be expensive. For this reason traditionally we have maintained a cache of decompressed pages in addition to the cache of compressed pages that can be found in the Linux page cache. However, it would be much preferred if we could avoid decompressing as much as possible, ideally to the point where no significant cache of the decompressed pages was needed.

Presently we need to decompress to search, so we’ve been looking at options to compress the pages in such a way that searching can be done using the compressed form. The current design being played with here uses a form of DFA to perform searching/matching on the keyed fields – the DFA data is a compact representation of the data in the keyed fields but is also efficient to use as for searching. For the payload part, we are looking at several options (potentially using more than one of them depending on the exact data) including:

  • Do not compress (may be appropriate for ID case, for example)
  • Compress individual rows, using a shared dictionary (perhaps trained on first n rows of the index)
  • Compress blocks of rows (in particular, rows that have the same key value)

A fast (to decompress) compression algorithm that handles small blocks of data efficiently is needed. Zstd may be one possible candidate.

Preliminary work to enable the above changes involved some code restructuring to make it possible to plug in different compression formats more easily, and to vary the compression format per page.

What is the topology server for?

It’s used in the cloud to ensure that all nodes can know the IP addresses of all agents currently processing requests for a given channel. These addresses can change over time due to pod restarts or scaling events. Nodes report to the topology server periodically, and it responds to them with the current topology state. There may be multiple topology servers running (for redundancy purposes). If so all reports should go to all, and it should not matter which one’s answer is used. (QUESTION – how is the send to all done?)

Lazy File IO

All IFileIO objects used to read files from Roxie are instantiated as IRoxieLazyFileIO objects, which means:

  • The underlying file handles can be closed in the background, in order to handle the case where file handles are a limited resource. The maximum (and minimum) number of open files can be configured separately for local versus remote files (sometimes remote connections are a scarcer resource than local, if there are limits at the remote end).

  • The actual file connected to can be switched out in the background, to handle the case where a file read from a remote location becomes unavailable, and to switch to reading from a local location after a background file copy operation completes.

New IBYTI mode

Original IBYTI implementation allocated a thread (from the pool) to each incoming query packet, but some will block for a period to allow an IBYTI to arrive to avoid unnecessary work. It was done this way for historical reasons - mainly that the addition of the delay was after the initial IBYTI implementation, so that in the very earliest versions there was no priority given to any particular subchannel and all would start processing at the same time if they had capacity to do so.

This implementation does not seem particularly smart - in particular it's typing up worker threads even though they are not actually working, and may result in the throughput of the Roxie agent being reduced. For that reason an alternative implementation (controlled by the NEW_IBYTI flag) was created during the cloud transition which tracks what incoming packets are waiting for IBYTI expiry via a separate queue, and they are only allocated to a worker thread once the IBYTI delay times out.

So far the NEW_IBYTI flag has only been set on containerized systems (simply to avoid rocking the boat on the bare-metal systems), but we may turn on in bare metal too going forward (and if so, the old version of the code can be removed sooner or later).

Testing Roxie code

Sometimes when developing/debugging Roxie features, it's simplest to run a standalone executable. Using server mode may be useful if wanting to debug server/agent traffic messaging.

For example, to test IBYTI behaviour on a single node, use

./a.out --server --port=9999 --traceLevel=1 --logFullQueries=1 --expert.addDummyNode --roxieMulticastEnabled=0 --traceRoxiePackets=1
+

Having first compiled a suitable bit of ECL into a.out. I have found a snippet like this quite handy:

rtl := SERVICE
+ unsigned4 sleep(unsigned4 _delay) : eclrtl,action,library='eclrtl',entrypoint='rtlSleep';
+END;
+
+d := dataset([{rtl.sleep(5000)}], {unsigned a});
+allnodes(d)+d;
+

Cache prewarm

Roxie (optionally) maintains a list of the most recently accessed file pages (in a circular buffer), and flushes this information periodically to text files that will persist from one run of Roxie to the next. On startup, these files are processed and the relevant pages preloaded into the linux page cache to ensure that the "hot" pages are already available and maximum performance is available immediately once the Roxie is brought online, rather than requiring a replay of a "typical" query set to heat the cache as used to be done. In particular this should allow a node to be "warm" before being added to the cluster when autoscaling.

There are some questions outstanding about how this operates that may require empirical testing to answer. Firstly, how does this interact with volumes mounted via k8s pvc's, and in particular with cloud billing systems that charge per read. Will the reads that are done to warm the cache be done in large chunks, or will they happen one linux page at a time? The code at the Roxie level operates by memory-mapping the file then touching a byte within each linux page that we want to be "warm", but does the linux paging subsystem fetch larger blocks? Do huge pages play a part here?

Secondly, the prewarm is actually done by a child process (ccdcache), but the parent process is blocked while it happens. It would probably make sense to at allow at least some of the other startup operations of the parent process to proceed in parallel. There are two reasons why the cache prewarm is done using a child process. Firstly is to allow there to be a standalone way to prewarm prior to launching a Roxie, which might be useful for automation in some bare-metal systems. Secondly, because there is a possibility of segfaults resulting from the prewarm if the file has changed size since the cache warming was done, it is easier to contain, capture, and recover from such faults in a child process than it would be inside Roxie. However, it would probably be possible to avoid these segfaults (by checking more carefully against file size before trying to warm a page, for example) and then link the code into Roxie while still keeping the code common with the standalone executable version.

Thirdly, need to check that the prewarm is complete before adding a new agent to the topology. This is especially relevant if we make any change to do the prewarm asynchronously.

Fourthly, there are potential race conditions when reading/writing the file containing cache information, since this file may be written by any agent operating on the same channel, at any time.

Fifthly, how is the amount of information tracked decided? It should be at least related to the amount of memory available to the linux page cache, but that's not a completely trivial thing to calculate. Should we restrict to the most recent N when outputting, where N is calculated from, for example /proc/meminfo's Active(file) value? Unfortunately on containerized sytems that reflects the host, but perhaps /sys/fs/cgroup/memory.stat can be used instead?

When deciding how much to track, we can pick an upper limit from the pod's memory limit. This could be read from /sys/fs/cgroup/memory.max though we currently read from the config file instead. We should probably (a) subtract the roxiemem size from that and (b) think about a value that will work on bare-metal and fusion too. However, because we don't dedup the entries in the circular buffer used for tracking hot pages until the info is flushed, the appropriate size is not really the same as the memory size.

We track all reads by page, and before writing also add all pages in the jhtree cache with info about the node type. Note that a hit in the jhtree page cache won't be noted as a read OTHER than via this last-minute add.

Blacklisting sockets

This isn't really specific to Roxie, but was originally added for federated Roxie systems...

When a Roxie query (or hthor/thor) makes a SOAPCALL, there is an option to specify a list of target gateway IPs, and failover to the next in the list if the first does not respond in a timely fashion. In order to avoid this "timely fashion" check adding an overhead to every query made when a listed gateway is unavailable, we maintain a "blacklist" of nodes that have been seen to fail, and do not attempt to connect to them. There is a "deblacklister" thread that checks periodically whether it is now possible to connect to a previously-blacklisted gateway, and removes it from the list if so.

There are a number of potential questions and issues with this code:

  1. It would appear that a blacklist is applied even when there is only one gateway listed. In this case, the blacklist may be doing more harm than good? I'm not sure that is true - it is still causing rapid failures in cases that are never going to work...
  2. Even when there is only one gateway listed, a blacklist MIGHT still be useful (you don't really want EVERY query to block trying to connect, if the gateway is down - may prefer a fast failure). Also applies when there are multiple records, all being passed to a gateway, and with an ONFAIL.
  3. Is the blacklist shared between all queries? I'm pretty sure it is NOT shared across Roxie nodes... Looks like it is a global object, shared between all queries and activities. However, connections that were started in parallel will all be reported as failed rather than blacklisted, which can make it look like it is maintained per-activity.
  4. Is it only a failed connect that leads to blacklisting, or does a slow/error response also cause a gateway endpoint to be blacklisted? It's only a failed connect.
  5. When deblacklisting, can we check any condition other than "Successfully connected"? If not, blacklisting for any reason other than "Did not connect" feels like a recipe for problems. We only check for a connection (and correspondingly only blacklist for a failed connection).
  6. Are we ever using the functionality where there are more than one gateway listed? Most of the time a load-balancer is a preferable solution...
  7. The "deblacklister" thread seems to add an escalating delay between attempts. Is this delay ever reset? Is it configurable? Is it appropriate?
  8. There's a thread (in the blacklister's pool) for each blacklisted endpoint. These threads will never go away if the endpoint does not recover...
  9. There's a delay of up to 10 seconds in terminating caused by the deblacklister's connect having to timeout before it notices that we are stopping. Can we close the socket as well as interrupting the semaphore?
  10. Does the "reconnect" attempt from the deblacklister cause any pain for the server it is connecting to? Lots of connect attempts without any data could look like a DoS attack...
  11. Retries/timeout seems to translate to Owned<ISocketConnectWait> scw = nonBlockingConnect(ep, timeoutMS == WAIT_FOREVER ? 60000 : timeoutMS*(retries+1)); I am not sure that is correct (a single attempt to connect with a long timeout doesn't feel like it is the same as multiple attempts with shorter timeouts, for example if there is a load balancer in the mix).
  12. Perhaps an option to not use blacklister would solve the immediate issue?
  13. The blacklister uses an array of endpoints - If there were a lot blacklisted, a hash table would be better
  14. Hints to control behaviour of deblacklister would behave unpredictably if multiple activities connected to the same endpoint with different hints unless we make the blacklist lookup match the hint values too.
  15. Deblacklister should use nonBlockingConnect too.

Should the scope of the blacklist be different? Possible scopes are:

  1. Shared across all queries/activities (current behaviour)
  2. Specific to an activity, but shared across queries (i.e. owned by the activity factory)
  3. Specific to all activities in a deployed query (i.e. owned by the query factory)
  4. Specific to a particular activity instance (i.e. owned by the activity object)
  5. Specific to a particular query instance (i.e. owned by the query object)

Options 2 and 4 above would allow all aspects of the blacklisting behaviour to be specified by options on the SOAPCALL. We could control whether or not the blacklister is to be used at all via a SOAPCALL option with any of the above...

perftrace options

The HPCC Platform includes a rudimentary performance tracing feature using periodic stack capture to generate flame graphs. Roxie supports this in 3 ways:

  1. If expert/@profileStartup is set in roxie config, a flame graph is generated for operations during Roxie startup phase.
  2. If @perf is set on an incoming query, a flame graph is generated for the lifetime of that query's execution, and returned along with the query results
  3. If expert/perftrace is set in roxie config, one-shot roxie queries (e.g. eclagent mode) generate a flame graph (currently just to a text file).

The perf trace operates as follows:

  1. A child process is launched that runs the doperf script. This samples the current stack(s) every 0.2s (configurable) to a series of text files.
  2. When tracing is done, these text files are "folded" via a perl script that notes every unique stack and how many times it was seen, one line per unique stack
  3. This folded stack list is filtered to suppress some stacks that are not very interesting
  4. The filtered folded stack list is passed to another perl script that generates an svg file.

The basic info captured at step 1 (or maybe 2) could also be analysed to give other insights, such as:

  1. A list of "time in function, time in children of function".
  2. An expanded list of callers to __GI___lll_lock_wait and __GI___lll_lock_wake, to help spot contended critsecs.

Unfortunately some info present in the original stack text files is lost in the folded summary - in particular related to the TID that the stack is on. Can we spot lifetimes of threads and/or should we treat "stacks" on different threads as different? Thread pools might render this difficult though. There is an option in stack-collapse-elfutils.pl to include the TID when considering whether stacks match, so perhaps we should just (optionally) use that.

Some notes on LocalAgent mode

In localAgent mode, the global queueManager object (normally a RoxieUdpSocketQueueManager) is replaced by a RoxieLocalQueueManager. Outbound packets are added directly to target queue, inbound are packed into DataBuffers.

There is also "local optimizations" mode where any index operation reading a one-part file (does the same apply to one-part disk files?) just reads it directly on the server (regardless of localAgent setting). Typically still injected into receiver code though as otherwise handling exception cases, limits etc would all be duplicated/messy. Rows created in localOptimization mode are created directly in the caller's row manager, and are injected in serialized format.

Why are inbound not created directly in the desired destination's allocator and then marked as serialized? Some lifespan issues... are they insurmountable? We do pack into dataBuffers rather than MemoryBuffers, which avoids a need to copy the data before the receiver can use it. Large rows get split and will require copying again, but we could set dataBufferSize to be bigger in localAgent mode to mitigate this somewhat.

What is the lifespan issue? In-flight queries may be abandoned when a server-side query fails, times out, or no longer needs the data. Using DataBuffer does not have this issue as they are attached to the query's memory manager/allocation once read. Or we could bypass the agent queue altogether, but rather more refactoring needed for that (might almost be easier to extent the "local optimization" mode to use multiple threads at that point)

abortPending, replyPending, and abortPendingData methods are unimplemented, which may lead to some inefficiencies?

Some notes on UDP packet sending mechanism

Requests from server to agents are send via UDP (and have a size limit of 64k as a result). Historically they were sent using multicast to go to all agents on a channel at the same time, but since most cloud providers do not support multicast, there has long been an option to avoid multicast and send explicitly to the agent IPs. In bare metal systems these IPs are known via the topology file, and do not change. In cloud systems the topology server provides the IPs of all agents for a channel.

In cloud systems, the list of IPs that a message was sent to is included in the message header, so that the IBYTI messages can be sent without requiring that all agents/servers have the same topology information at any given moment (they will stay in sync because of topology server, but may be temporarily out of sync when nodes are added/removed, until next time topology info is retrieved). This is controled by the SUBCHANNELS_IN_HEADER define.

Packets back from agents to server go via the udplib message-passing code. This can best be described by looking at the sending and receiving sides separately.

When sending, results are split into individual packets (DataBuffers), each designed to be under 1 MTU in size. Traditionally this meant they were 1k, but they can be set larger (8k is good). They do have to be a power of 2 because of how they are allocated from the roxiemem heap. The sender maintains a set of UdpReceiverEntry objects, one for each server that it is conversing with. Each UdpReceiverEntry maintains multiple queues of data packets waiting to be sent, one queue for each priority. The UdpReceiverEntry maintains a count of how many packets are contained across all its queues in packetsQueued, so that it knows if there is data to send.

The priority levels are: 0: Out Of Band 1: Fast lane 2: Standard

This is designed to allow control information to be sent without getting blocked by data, and high priority queries to avoid being blocked by data going to lower priority ones. The mechanism for deciding what packet to send next is a little odd though - rather than sending all higher-priorty packets before any lower-priority ones, it round robins across the queues sending up to N^2 from queue 0 then up to N from queue 1 then 1 from queue 2, where N is set by the UdpOutQsPriority option, or 1 if not set. This may be a mistake - probably any from queue 0 should be sent first, before round-robining the other queues in this fashion.

UdpReceiverEntry objects are also responsible for maintaining a list of packets that have been sent but receiver has not yet indicated that they have arrived.

If an agent has data ready for a given receiver, it will send a requestToSend to that receiver, and wait for a permitToSend response. Sequence numbers are used to handle situations where these messages get lost. A permitToSend that does not contain the expected sequence number is ignored.

`,85),s=[n];function r(l,h,d,c,p,u){return a(),t("div",null,s)}const g=e(i,[["render",r]]);export{f as __pageData,g as default}; diff --git a/assets/devdoc_roxie.md.ucbDhtEB.lean.js b/assets/devdoc_roxie.md.ucbDhtEB.lean.js new file mode 100644 index 00000000000..51644b8bfe3 --- /dev/null +++ b/assets/devdoc_roxie.md.ucbDhtEB.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as a,V as o}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"Everything you ever wanted to know about Roxie","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/roxie.md","filePath":"devdoc/roxie.md","lastUpdated":1721825996000}'),i={name:"devdoc/roxie.md"},n=o("",85),s=[n];function r(l,h,d,c,p,u){return a(),t("div",null,s)}const g=e(i,[["render",r]]);export{f as __pageData,g as default}; diff --git a/assets/devdoc_userdoc_AzureTipsTricks.md.aFaptA0d.js b/assets/devdoc_userdoc_AzureTipsTricks.md.aFaptA0d.js new file mode 100644 index 00000000000..264c3211afb --- /dev/null +++ b/assets/devdoc_userdoc_AzureTipsTricks.md.aFaptA0d.js @@ -0,0 +1 @@ +import{_ as e,c as r,o as s}from"./chunks/framework.gBlNPWt_.js";const _=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/userdoc/AzureTipsTricks.md","filePath":"devdoc/userdoc/AzureTipsTricks.md","lastUpdated":1721825996000}'),t={name:"devdoc/userdoc/AzureTipsTricks.md"};function c(o,a,d,i,p,n){return s(),r("div")}const m=e(t,[["render",c]]);export{_ as __pageData,m as default}; diff --git a/assets/devdoc_userdoc_AzureTipsTricks.md.aFaptA0d.lean.js b/assets/devdoc_userdoc_AzureTipsTricks.md.aFaptA0d.lean.js new file mode 100644 index 00000000000..264c3211afb --- /dev/null +++ b/assets/devdoc_userdoc_AzureTipsTricks.md.aFaptA0d.lean.js @@ -0,0 +1 @@ +import{_ as e,c as r,o as s}from"./chunks/framework.gBlNPWt_.js";const _=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/userdoc/AzureTipsTricks.md","filePath":"devdoc/userdoc/AzureTipsTricks.md","lastUpdated":1721825996000}'),t={name:"devdoc/userdoc/AzureTipsTricks.md"};function c(o,a,d,i,p,n){return s(),r("div")}const m=e(t,[["render",c]]);export{_ as __pageData,m as default}; diff --git a/assets/devdoc_userdoc_Blogs.md.-JKS2x0f.js b/assets/devdoc_userdoc_Blogs.md.-JKS2x0f.js new file mode 100644 index 00000000000..268b03d4ff0 --- /dev/null +++ b/assets/devdoc_userdoc_Blogs.md.-JKS2x0f.js @@ -0,0 +1 @@ +import{_ as e,c as o,o as t}from"./chunks/framework.gBlNPWt_.js";const i=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/userdoc/Blogs.md","filePath":"devdoc/userdoc/Blogs.md","lastUpdated":1721825996000}'),s={name:"devdoc/userdoc/Blogs.md"};function a(c,d,r,n,l,p){return t(),o("div")}const m=e(s,[["render",a]]);export{i as __pageData,m as default}; diff --git a/assets/devdoc_userdoc_Blogs.md.-JKS2x0f.lean.js b/assets/devdoc_userdoc_Blogs.md.-JKS2x0f.lean.js new file mode 100644 index 00000000000..268b03d4ff0 --- /dev/null +++ b/assets/devdoc_userdoc_Blogs.md.-JKS2x0f.lean.js @@ -0,0 +1 @@ +import{_ as e,c as o,o as t}from"./chunks/framework.gBlNPWt_.js";const i=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/userdoc/Blogs.md","filePath":"devdoc/userdoc/Blogs.md","lastUpdated":1721825996000}'),s={name:"devdoc/userdoc/Blogs.md"};function a(c,d,r,n,l,p){return t(),o("div")}const m=e(s,[["render",a]]);export{i as __pageData,m as default}; diff --git a/assets/devdoc_userdoc_README.md.D4Kid-3h.js b/assets/devdoc_userdoc_README.md.D4Kid-3h.js new file mode 100644 index 00000000000..5fe18183131 --- /dev/null +++ b/assets/devdoc_userdoc_README.md.D4Kid-3h.js @@ -0,0 +1 @@ +import{_ as e,c as t,o,V as r}from"./chunks/framework.gBlNPWt_.js";const p=JSON.parse('{"title":"User Documentation","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/userdoc/README.md","filePath":"devdoc/userdoc/README.md","lastUpdated":1721825996000}'),a={name:"devdoc/userdoc/README.md"},s=r('

User Documentation

Documentation in this directory is targeted to end-users of the HPCC Systems Platform. This is less-formal documentation intended to be produced and released more quickly than the published HPCC documentation. See HPCC documentation if you would like to contribute to our official docs.

Directory structure under devdoc

INFO

  • userdoc/troubleshoot: Information related to troubleshooting particular components
  • userdoc/azure: Useful information about Azure Cloud portal and cli
  • userdoc/roxie: Useful information for running roxie
  • userdoc/thor: COMING SOON: Useful information for running thor
  • userdoc/blogs: COMING SOON: Location and instructions for all Blogs

General documentation

HPCC Website documentation

',8),i=[s];function n(l,c,u,d,h,m){return o(),t("div",null,i)}const _=e(a,[["render",n]]);export{p as __pageData,_ as default}; diff --git a/assets/devdoc_userdoc_README.md.D4Kid-3h.lean.js b/assets/devdoc_userdoc_README.md.D4Kid-3h.lean.js new file mode 100644 index 00000000000..1566f421471 --- /dev/null +++ b/assets/devdoc_userdoc_README.md.D4Kid-3h.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o,V as r}from"./chunks/framework.gBlNPWt_.js";const p=JSON.parse('{"title":"User Documentation","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/userdoc/README.md","filePath":"devdoc/userdoc/README.md","lastUpdated":1721825996000}'),a={name:"devdoc/userdoc/README.md"},s=r("",8),i=[s];function n(l,c,u,d,h,m){return o(),t("div",null,i)}const _=e(a,[["render",n]]);export{p as __pageData,_ as default}; diff --git a/assets/devdoc_userdoc_WikiGuidelines.md.QHJ-8Jox.js b/assets/devdoc_userdoc_WikiGuidelines.md.QHJ-8Jox.js new file mode 100644 index 00000000000..94a2d6613a3 --- /dev/null +++ b/assets/devdoc_userdoc_WikiGuidelines.md.QHJ-8Jox.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as i}from"./chunks/framework.gBlNPWt_.js";const u=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/userdoc/WikiGuidelines.md","filePath":"devdoc/userdoc/WikiGuidelines.md","lastUpdated":1721825996000}'),d={name:"devdoc/userdoc/WikiGuidelines.md"};function s(o,a,c,r,n,l){return i(),t("div")}const _=e(d,[["render",s]]);export{u as __pageData,_ as default}; diff --git a/assets/devdoc_userdoc_WikiGuidelines.md.QHJ-8Jox.lean.js b/assets/devdoc_userdoc_WikiGuidelines.md.QHJ-8Jox.lean.js new file mode 100644 index 00000000000..94a2d6613a3 --- /dev/null +++ b/assets/devdoc_userdoc_WikiGuidelines.md.QHJ-8Jox.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as i}from"./chunks/framework.gBlNPWt_.js";const u=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/userdoc/WikiGuidelines.md","filePath":"devdoc/userdoc/WikiGuidelines.md","lastUpdated":1721825996000}'),d={name:"devdoc/userdoc/WikiGuidelines.md"};function s(o,a,c,r,n,l){return i(),t("div")}const _=e(d,[["render",s]]);export{u as __pageData,_ as default}; diff --git a/assets/devdoc_userdoc_azure_TipsAndTricks.md.LEeeHH_U.js b/assets/devdoc_userdoc_azure_TipsAndTricks.md.LEeeHH_U.js new file mode 100644 index 00000000000..221a9500778 --- /dev/null +++ b/assets/devdoc_userdoc_azure_TipsAndTricks.md.LEeeHH_U.js @@ -0,0 +1,54 @@ +import{_ as s,c as n,o as a,V as p}from"./chunks/framework.gBlNPWt_.js";const k=JSON.parse('{"title":"Azure Portal FAQs","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/userdoc/azure/TipsAndTricks.md","filePath":"devdoc/userdoc/azure/TipsAndTricks.md","lastUpdated":1721825996000}'),e={name:"devdoc/userdoc/azure/TipsAndTricks.md"},l=p(`

Azure Portal FAQs

  1. How do I see who created a resource group?

Answer:

1. go to the resource group service page (https://portal.azure.com/#view/HubsExtension/BrowseResourceGroups)
+2. "Add filter"
+3. Filter on "Admin"
+4. Set the Value to "ALL" or select the names you are interested in (the names in the list are the only ones that have the Admin tag set)  This works for all other fields that you can filter on
  1. How do I create a dashboard in Azure?

Answer:

Login to the Azure portal.
+
+Click on the \`hamburger icon\`, located at the top left corner of the page.
+
+Click on \`Dashboard\` to go to your dashboards.
+
+Click on \`Create\`, located at the top left corner.
+
+Click on the \`Custom\` tile.
+
+Edit the input box to name your dashboard.
+
+Click on \`Resource groups\` in the tile gallery.
+
+Click \`Add\`.
+
+Click and drag the \`lower right corner\` of the tile to resize it to your likings.
+
+Click \`Save\` to save your settings.
+
+You should now be taken to your new dashboard.
+
+Click on your new dashboard tile.
+
+Click on \`Add filter\`, located at the top center of the page.
+
+Click on the \`Filter\` input box to reveal the tags.
+
+Select the \`Admin\` tag.
+
+Click on the \`Value\` input box.
+
+Click on \`Select all\` to unselect all.
+
+Select your name.
+
+Click on \`Apply\`.
+
+Next, click on \`Manage view\`, located at the top left of the page.
+
+Select \`Save view\`.
+
+Enter a name for the view in the input box.
+
+Click \`Save\`
+
+ 
+
+ 
+
+Click on \`Manage View\`, located at the top left of the page
`,7),t=[l];function o(i,c,r,d,h,u){return a(),n("div",null,t)}const _=s(e,[["render",o]]);export{k as __pageData,_ as default}; diff --git a/assets/devdoc_userdoc_azure_TipsAndTricks.md.LEeeHH_U.lean.js b/assets/devdoc_userdoc_azure_TipsAndTricks.md.LEeeHH_U.lean.js new file mode 100644 index 00000000000..8f9c5d042d8 --- /dev/null +++ b/assets/devdoc_userdoc_azure_TipsAndTricks.md.LEeeHH_U.lean.js @@ -0,0 +1 @@ +import{_ as s,c as n,o as a,V as p}from"./chunks/framework.gBlNPWt_.js";const k=JSON.parse('{"title":"Azure Portal FAQs","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/userdoc/azure/TipsAndTricks.md","filePath":"devdoc/userdoc/azure/TipsAndTricks.md","lastUpdated":1721825996000}'),e={name:"devdoc/userdoc/azure/TipsAndTricks.md"},l=p("",7),t=[l];function o(i,c,r,d,h,u){return a(),n("div",null,t)}const _=s(e,[["render",o]]);export{k as __pageData,_ as default}; diff --git a/assets/devdoc_userdoc_roxie_FAQ.md.nQti9zzo.js b/assets/devdoc_userdoc_roxie_FAQ.md.nQti9zzo.js new file mode 100644 index 00000000000..b6801643949 --- /dev/null +++ b/assets/devdoc_userdoc_roxie_FAQ.md.nQti9zzo.js @@ -0,0 +1,24 @@ +import{_ as s,c as a,o as e,V as t}from"./chunks/framework.gBlNPWt_.js";const m=JSON.parse('{"title":"ROXIE FAQs","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/userdoc/roxie/FAQ.md","filePath":"devdoc/userdoc/roxie/FAQ.md","lastUpdated":1721825996000}'),n={name:"devdoc/userdoc/roxie/FAQ.md"},o=t(`

ROXIE FAQs

  1. How I can compile a query on a containerized or cloud-based system?

Answer:

Same way as bare metal. Command line, or with the IDE, or from ECL Watch. Just point to the HPCC Systems instance to compile.
+For Example:
+ecl deploy <target> <file>
  1. How do I copy queries from an on-prem cluster to Azure?

Answer:

The copy query command – use the Azure host name or IP address for the target.
+For example:
+ecl queries copy <source_query_path> <target_queryset>
  1. How can I get the IP address for the Azure target cluster?

Answer:

Use the "kubectl get svc" command. Use the external IP address listed for ECL Watch.
+kubectl get svc
  1. Do we have to have use the DNSName or do we need to use the IP address?

Answer:

If you can reach ECL Watch with the DNS Name then it should also work for the command line.
  1. How can I find the ECL Watch or Dali hostname?

Answer:

If you did not set up the containerized instance, then you need to ask your Systems Administrator or whomever set it up..
  1. How do I publish a package file?

Answer:

Same way as bare metal.
+To add a new package file: ecl packagemap add or
+To copy exisitng package file : ecl packagemap copy
  1. How do I check the logs?

Answer:

kubectl log <podname>
+in addition you can use -f (follow) option to tail the logs. Optionally you can also issue the <namespace> parameter.
+For example:
+kbectl log roxie-agent-1-3b12a587b –namespace MyNameSpace
+Optionally, you may have implemented a log-processing solution such as the Elastic Stack (elastic4hpcclogs).
  1. How do I get the data on to Azure?

Answer:

Use the copy query command and copy or add the Packagemap.
+With data copy start in the logs…copy from remote location specified if data doesn’t exist on the local system.
+The remote location is the remote Dali (use the --daliip=<daliIP> parameter to specify the remote Dali)
+You can also use ECL Watch.
  1. How can I start a cloud cluster? (akin to the old Virtual Box image)?

Answer:

Can use Docker Desktop, or Azure or any cloud provider and install the HPCC Systems Cloud native helm
+charts
  1. How can I show the ECL queries that are published to a given Roxie?

Answer:

Can use WUListQueries
+For example:
+https://[eclwatch]:18010/WsWorkunits/WUListQueries.json?ver_=1.86&ClusterName=roxie&CheckAllNodes=0
  1. I set up persistent storage on my containerized HPCC Systems, and now it won't start. Why?

Answer:

One possible reason may be that all of the required storage directories are not present. The directories for ~/
+hpccdata/dalistorage, hpcc-data, debug, queries, sasha, and dropzone are all required to exist or your cluster may not start.
  1. Are there any new methods available to work with queries?

Answer:

Yes. There is a new method available ServiceQuery.
+https://[eclwatch]:18010/WsResources/ServiceQuery?ver_=1.01&
+For example Roxie Queries:
+https://[eclwatch]:18010/WsResources/ServiceQuery?ver_=1.01&Type=roxie
+or WsECL (eclqueries)
+https://[eclwatch]:18010/WsResources/ServiceQuery?ver_=1.01&Type=eclqueries
`,37),p=[o];function l(i,r,c,d,h,g){return e(),a("div",null,p)}const v=s(n,[["render",l]]);export{m as __pageData,v as default}; diff --git a/assets/devdoc_userdoc_roxie_FAQ.md.nQti9zzo.lean.js b/assets/devdoc_userdoc_roxie_FAQ.md.nQti9zzo.lean.js new file mode 100644 index 00000000000..32e40f7e0bc --- /dev/null +++ b/assets/devdoc_userdoc_roxie_FAQ.md.nQti9zzo.lean.js @@ -0,0 +1 @@ +import{_ as s,c as a,o as e,V as t}from"./chunks/framework.gBlNPWt_.js";const m=JSON.parse('{"title":"ROXIE FAQs","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/userdoc/roxie/FAQ.md","filePath":"devdoc/userdoc/roxie/FAQ.md","lastUpdated":1721825996000}'),n={name:"devdoc/userdoc/roxie/FAQ.md"},o=t("",37),p=[o];function l(i,r,c,d,h,g){return e(),a("div",null,p)}const v=s(n,[["render",l]]);export{m as __pageData,v as default}; diff --git a/assets/devdoc_userdoc_troubleshoot_ClientsToolIssues.md.Oxqyv8dQ.js b/assets/devdoc_userdoc_troubleshoot_ClientsToolIssues.md.Oxqyv8dQ.js new file mode 100644 index 00000000000..639c5bab8e6 --- /dev/null +++ b/assets/devdoc_userdoc_troubleshoot_ClientsToolIssues.md.Oxqyv8dQ.js @@ -0,0 +1 @@ +import{_ as e,c as t,o,V as s}from"./chunks/framework.gBlNPWt_.js";const l="/HPCC-Platform/assets/ECLIDE-WindowsProtectionError.GSXgMRbZ.png",r="/HPCC-Platform/assets/ECLIDE-AppProperties.syqSIm3l.png",f=JSON.parse('{"title":"How to resolve issues installing the ECL IDE / Client tools","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/userdoc/troubleshoot/ClientsToolIssues.md","filePath":"devdoc/userdoc/troubleshoot/ClientsToolIssues.md","lastUpdated":1721825996000}'),a={name:"devdoc/userdoc/troubleshoot/ClientsToolIssues.md"},i=s('

How to resolve issues installing the ECL IDE / Client tools

Problem

Some users may experience an issue when trying to install the ECL IDE / client tools version 8.10 and later.
If you get an error saying something like, Windows Defender SmartScreen prevented access and there is no option other than "Don't Run":

  • Go to the folder where you downloaded the file.
  • Once there go to the app file properties click unblock

alt-text

alt-text

',6),n=[i];function c(d,h,u,p,_,m){return o(),t("div",null,n)}const E=e(a,[["render",c]]);export{f as __pageData,E as default}; diff --git a/assets/devdoc_userdoc_troubleshoot_ClientsToolIssues.md.Oxqyv8dQ.lean.js b/assets/devdoc_userdoc_troubleshoot_ClientsToolIssues.md.Oxqyv8dQ.lean.js new file mode 100644 index 00000000000..f0ccdea371a --- /dev/null +++ b/assets/devdoc_userdoc_troubleshoot_ClientsToolIssues.md.Oxqyv8dQ.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o,V as s}from"./chunks/framework.gBlNPWt_.js";const l="/HPCC-Platform/assets/ECLIDE-WindowsProtectionError.GSXgMRbZ.png",r="/HPCC-Platform/assets/ECLIDE-AppProperties.syqSIm3l.png",f=JSON.parse('{"title":"How to resolve issues installing the ECL IDE / Client tools","description":"","frontmatter":{},"headers":[],"relativePath":"devdoc/userdoc/troubleshoot/ClientsToolIssues.md","filePath":"devdoc/userdoc/troubleshoot/ClientsToolIssues.md","lastUpdated":1721825996000}'),a={name:"devdoc/userdoc/troubleshoot/ClientsToolIssues.md"},i=s("",6),n=[i];function c(d,h,u,p,_,m){return o(),t("div",null,n)}const E=e(a,[["render",c]]);export{f as __pageData,E as default}; diff --git a/assets/ecl_ecl-bundle_DOCUMENTATION.md.Z4TCf1dS.js b/assets/ecl_ecl-bundle_DOCUMENTATION.md.Z4TCf1dS.js new file mode 100644 index 00000000000..65f103a9e12 --- /dev/null +++ b/assets/ecl_ecl-bundle_DOCUMENTATION.md.Z4TCf1dS.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as l,V as n}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"Ecl-bundle source documentation","description":"","frontmatter":{},"headers":[],"relativePath":"ecl/ecl-bundle/DOCUMENTATION.md","filePath":"ecl/ecl-bundle/DOCUMENTATION.md","lastUpdated":1721825996000}'),a={name:"ecl/ecl-bundle/DOCUMENTATION.md"},i=n('

Ecl-bundle source documentation

Introduction

Purpose

The ecl-bundle executable (normally executed from the ecl executable by specifying 'ecl bundle XXX' is designed to manipulate ecl bundle files

  • these are self-contained parcels of ECL code, packaged into a compressed file like a zip or .tar.gz file, that can be downloaded, installed, removed etc from an ECL installation.

Design

The metadata for ECL bundles is described using an exported module called Bundle within the bundle's source tree - typically, this means that a file called Bundle.ecl will be added to the highest level of the bundle's directory tree. In order to extract the information from the Bundle module, eclcc is run in 'evaluate' mode (using the -Me option) which will parse the bundle module and output the required fields to stdout.

ecl-bundle also executes eclcc (using the --showpaths option) to determine where bundle files are to be located.

Directory structure

In order to make versioning easier, bundle files are not copied directly into the bundles directory. A bundle called "MyBundle" that announces itself as version "x.y.z" will be installed to the directory

$BUNDLEDIR/_versions/MyBundle/x.y.z

A "redirect" file called MyBundle.ecl is then created in $BUNDLEDIR, which redirects any IMPORT MyBundle statement to actually import the currently active version of the bundle in _versions/MyBundle/x.y.z

By rewriting this redirect file, it is possible to switch to using a different version of a bundle without having to uninstall and reinstall.

In a future release, we hope to make it possible to specify that bundle A requires version X of bundle B, while bundle C requires version Y of bundle B. That will require the redirect files to be 'local' to a bundle (and will require that bundle B uses a redirect file to ensure it picks up the local copy of B when making internal calls).

Key classes

An IBundleInfo represents a specific copy of a bundle, and is created by explicitly parsing a snippet of ECL that imports it, with the ECL include path set to include only the specified bundle.

An IBundleInfoSet represents all the installed versions of a particular named bundle.

An IBundleCollection represents all the bundles on the system.

Every individual subcommand is represented by a class derived (directly or indirectly) from EclCmdCommon. These classes are responsible for command-line parsing, usage text output, and (most importantly) execution of the desired outcomes.

',19),o=[i];function r(s,d,c,u,h,p){return l(),t("div",null,o)}const m=e(a,[["render",r]]);export{f as __pageData,m as default}; diff --git a/assets/ecl_ecl-bundle_DOCUMENTATION.md.Z4TCf1dS.lean.js b/assets/ecl_ecl-bundle_DOCUMENTATION.md.Z4TCf1dS.lean.js new file mode 100644 index 00000000000..202bf613a77 --- /dev/null +++ b/assets/ecl_ecl-bundle_DOCUMENTATION.md.Z4TCf1dS.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as l,V as n}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"Ecl-bundle source documentation","description":"","frontmatter":{},"headers":[],"relativePath":"ecl/ecl-bundle/DOCUMENTATION.md","filePath":"ecl/ecl-bundle/DOCUMENTATION.md","lastUpdated":1721825996000}'),a={name:"ecl/ecl-bundle/DOCUMENTATION.md"},i=n("",19),o=[i];function r(s,d,c,u,h,p){return l(),t("div",null,o)}const m=e(a,[["render",r]]);export{f as __pageData,m as default}; diff --git a/assets/ecllibrary_StyleGuide.md.gc1ZYvqu.js b/assets/ecllibrary_StyleGuide.md.gc1ZYvqu.js new file mode 100644 index 00000000000..f180d476a76 --- /dev/null +++ b/assets/ecllibrary_StyleGuide.md.gc1ZYvqu.js @@ -0,0 +1,20 @@ +import{_ as a,c as s,o as e,V as n}from"./chunks/framework.gBlNPWt_.js";const _=JSON.parse('{"title":"ECL standard library style guide","description":"","frontmatter":{},"headers":[],"relativePath":"ecllibrary/StyleGuide.md","filePath":"ecllibrary/StyleGuide.md","lastUpdated":1721825996000}'),t={name:"ecllibrary/StyleGuide.md"},l=n(`

ECL standard library style guide

The ECL code in the standard library should follow the following style guidelines:

  • All ECL keywords in upper case
  • ECL reserved types in upper case
  • Public attributes in camel case with leading upper case
  • Private attributes in lower case with underscore as a separator
  • Field names in lower case with underscore as a separator
  • Standard indent is 2 spaces (no tabs)
  • Maximum line length of 120 characters
  • Compound statements have contents indented, and END is aligned with the opening statement
  • Field names are not indented to make them line up within a record structure
  • Parameters are indented as necessary
  • Use javadoc style comments on all functions/attributes (see Writing Javadoc Comments)

For example:

ecl
my_record := RECORD
+    INTEGER4 id;
+    STRING firstname{MAXLENGTH(40)};
+    STRING lastname{MAXLENGTH(50)};
+END;
+
+/**
+  * Returns a dataset of people to treat with caution matching a particular lastname.  The
+  * names are maintained in a global database of undesirables.
+  *
+  * @param  search_lastname    A lastname used as a filter
+  * @return                    The list of people
+  * @see                       NoFlyList
+  * @see                       MorePeopleToAvoid
+  */
+
+EXPORT DodgyCharacters(STRING search_lastname) := FUNCTION
+    raw_ds := DATASET(my_record, 'undesirables', THOR);
+    RETURN raw_ds(last_name = search_lastname);
+END;

Some additional rules for attributes in the library:

  • Services should be SHARED and EXPORTed via intermediate attributes
  • All attributes must have at least one matching test. If you're not on the test list you're not coming in.
`,7),i=[l];function p(r,o,c,d,u,m){return e(),s("div",null,i)}const y=a(t,[["render",p]]);export{_ as __pageData,y as default}; diff --git a/assets/ecllibrary_StyleGuide.md.gc1ZYvqu.lean.js b/assets/ecllibrary_StyleGuide.md.gc1ZYvqu.lean.js new file mode 100644 index 00000000000..4e7e3da7011 --- /dev/null +++ b/assets/ecllibrary_StyleGuide.md.gc1ZYvqu.lean.js @@ -0,0 +1 @@ +import{_ as a,c as s,o as e,V as n}from"./chunks/framework.gBlNPWt_.js";const _=JSON.parse('{"title":"ECL standard library style guide","description":"","frontmatter":{},"headers":[],"relativePath":"ecllibrary/StyleGuide.md","filePath":"ecllibrary/StyleGuide.md","lastUpdated":1721825996000}'),t={name:"ecllibrary/StyleGuide.md"},l=n("",7),i=[l];function p(r,o,c,d,u,m){return e(),s("div",null,i)}const y=a(t,[["render",p]]);export{_ as __pageData,y as default}; diff --git a/assets/index.md.gO4OaP_B.js b/assets/index.md.gO4OaP_B.js new file mode 100644 index 00000000000..4c7ada0cd13 --- /dev/null +++ b/assets/index.md.gO4OaP_B.js @@ -0,0 +1 @@ +import{_ as t,c as e,o as s}from"./chunks/framework.gBlNPWt_.js";const p=JSON.parse('{"title":"","description":"","frontmatter":{"layout":"home","hero":{"name":"HPCC-Platform","text":"Developers Hub","tagline":"Notes and documentation for developers of the HPCC-Platform","image":{"light":{"src":"/devdoc/hpccsystems.png","alt":"HPCC Systems","link":"https://hpccsystems.com"},"dark":{"src":"/devdoc/hpccsystemsdark.png","alt":"HPCC Systems","link":"https://hpccsystems.com"}},"actions":[{"theme":"brand","text":"Get Started","link":"/devdoc/README"},{"theme":"alt","text":"View on GitHub","link":"https://github.com/hpcc-systems/HPCC-Platform"}]},"features":[{"title":"Clone and Build","details":"Building the HPCC Platform from source code, deploying to a test environment and submitting pull requests.","link":"/devdoc/Development"},{"title":"Issues","details":"Report and track issues relating to the HPCC Platform.","link":"https://track.hpccsystems.com"},{"title":"Discussions","details":"Discuss the HPCC Platform with other platform developers.","link":"https://github.com/hpcc-systems/HPCC-Platform/discussions"},{"title":"HPCC Systems Homepage","details":"Not a HPCC-Platform developer? Please visit the HPCC Systems homepage for end user information and support.","link":"https://hpccsystems.com"}]},"headers":[],"relativePath":"index.md","filePath":"index.md","lastUpdated":1721825996000}'),o={name:"index.md"};function a(i,n,r,c,l,m){return s(),e("div")}const h=t(o,[["render",a]]);export{p as __pageData,h as default}; diff --git a/assets/index.md.gO4OaP_B.lean.js b/assets/index.md.gO4OaP_B.lean.js new file mode 100644 index 00000000000..4c7ada0cd13 --- /dev/null +++ b/assets/index.md.gO4OaP_B.lean.js @@ -0,0 +1 @@ +import{_ as t,c as e,o as s}from"./chunks/framework.gBlNPWt_.js";const p=JSON.parse('{"title":"","description":"","frontmatter":{"layout":"home","hero":{"name":"HPCC-Platform","text":"Developers Hub","tagline":"Notes and documentation for developers of the HPCC-Platform","image":{"light":{"src":"/devdoc/hpccsystems.png","alt":"HPCC Systems","link":"https://hpccsystems.com"},"dark":{"src":"/devdoc/hpccsystemsdark.png","alt":"HPCC Systems","link":"https://hpccsystems.com"}},"actions":[{"theme":"brand","text":"Get Started","link":"/devdoc/README"},{"theme":"alt","text":"View on GitHub","link":"https://github.com/hpcc-systems/HPCC-Platform"}]},"features":[{"title":"Clone and Build","details":"Building the HPCC Platform from source code, deploying to a test environment and submitting pull requests.","link":"/devdoc/Development"},{"title":"Issues","details":"Report and track issues relating to the HPCC Platform.","link":"https://track.hpccsystems.com"},{"title":"Discussions","details":"Discuss the HPCC Platform with other platform developers.","link":"https://github.com/hpcc-systems/HPCC-Platform/discussions"},{"title":"HPCC Systems Homepage","details":"Not a HPCC-Platform developer? Please visit the HPCC Systems homepage for end user information and support.","link":"https://hpccsystems.com"}]},"headers":[],"relativePath":"index.md","filePath":"index.md","lastUpdated":1721825996000}'),o={name:"index.md"};function a(i,n,r,c,l,m){return s(),e("div")}const h=t(o,[["render",a]]);export{p as __pageData,h as default}; diff --git a/assets/inter-italic-cyrillic-ext.OVycGSDq.woff2 b/assets/inter-italic-cyrillic-ext.OVycGSDq.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..2a687296748f6b8bc8076cd11bde49cd27e4442b GIT binary patch literal 28332 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 HNCp4^t1hY; literal 0 HcmV?d00001 diff --git a/assets/inter-italic-cyrillic.-nLMcIwj.woff2 b/assets/inter-italic-cyrillic.-nLMcIwj.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..f64035158d7e4c01654e3f23dcd6e8299928a28c GIT binary patch literal 17824 zcmV(|K+(TGZYzoV0yihRy3;{L*Bm;yv3xXa1 z1Rw>42nQe=C3zL>7I?PUshK)MG!ta^L3b^d$O{#w!;WOi#caU z9epdi^~z}FXC1zMc;nw||B-~iJKZ7-`H(Kn3lKjZ5N5RqaHof~Lr#JsXW1jml58&> zQGi~|0cnL4zB|P8}0t5&U@c|JdMvNGlmzMw!d_sgsBSwh~ z<3uKERMdzOA;C{wc9u@*KWk~1ZrQFoOIvrA?XoRxnU;2`Lr2EaDbuDiOouv|*;F%- z#gKPJ>8fl=HUf*mFq&P*op#SRB5uMwrI5O=D|BV|t7*jYszMn6_y=vT5wbG)8zDBN(kmfH1b)al)=7_XtiK52#|ESncdAW0QK2HvRY`_;WiO>pz_-Xk zH!T)&YR`MAJa7J@pNA;Ve=XlUuY2Z4Y1bh`Bcza^p&9I}QLUiMj74g|4NyqjXq$hi zG1}j&v`P|}Bqr@5l@eV;qy>fq`v}SgHrfA#@*v!mV~$a+B6D&Jh-sRueXL+`%jr?vt30*Vq&Bs(79VL�!Z1%E;0yxE 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 literal 0 HcmV?d00001 diff --git a/assets/inter-italic-latin-ext.RnFly65-.woff2 b/assets/inter-italic-latin-ext.RnFly65-.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..9c1b9440ed419d4a71ba46b0db3951164f9e10df GIT binary patch literal 63552 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 literal 0 HcmV?d00001 diff --git a/assets/inter-italic-latin.27E69YJn.woff2 b/assets/inter-italic-latin.27E69YJn.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..01fcf20724f915f68a974ef2fb85f86f3039b1d8 GIT binary patch literal 46048 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 literal 0 HcmV?d00001 diff --git a/assets/inter-italic-vietnamese.xzQHe1q1.woff2 b/assets/inter-italic-vietnamese.xzQHe1q1.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e4f788ee02bb687bc1d5045483ff0d381e7654e0 GIT binary patch literal 8784 zcmV-WBCp+dPew8T0RR9103uKT5dZ)H07v)$03qf80|eaw00000000000000000000 z0000Qfg~G@2plRuNLE2of^r66KT}jeRDl`*f;ca13aUh<5HP?}0X7081B4(8f)W4( zAO(d82Ot{>Lo)-x#sL^4@1Z66za(%|6q8eQ+Z$6!gJ(^}bq0;sONFdCj08aplr^jN?-K#&i3_{-NOKRw33LCqts3fdRWZHY0E~MB`M9Kq3P73JDO>nrZmz>DYaut z$W&>Z5Q1t|%c{QrGvW>qa(4M0P9mpnR$H7Pu&;>Po03^G;;MVIS)*nTgdU*W^Am>Pm(3j$ zgtL}x(jNb#P?F!;gr2f^EZ*!;G#1;7(a>Lb@PomSO$MW2$o=N;Rn_%BJb(h}Mu3(n zcQSVjl`bn~HY05*rH=M4fPg^?z(u6wJ5jb=YX5>Om0pxyr{-2FmDb9f%%x5lhAmS! zTQiq{rG~);EK%=uo9QaDw=_!VULzEpP>k_tg6aFZ+X4X%qiOpD3VLWuU?6lt5wlRj zCfGRzmpY-rA>ptNA>f$MEiCjJ7Gh)}Wg@%~fs75{Q2>g7QGy*(GqAw=crIKQvSLwU z!Nk)ke{5&J;H8y-4i?oYUwa?~eX=h-20a)6s9>#54W-7|A|RN)7Az>}U_?Lx;Fs+r zI=Gt;P$<#Xo@u*8jQp!h{SGQ5NQ?@UDhLXdDr2>ujh#c4YBiiZd=5K9w;o|`|k^e~VC#s+tw3G=Z43sJybXx3Z#^dL|IH%6nI9L6ZhfwG28fewbC zDvV+jqZq{~Mlp&}*_(v*V~3)7d_YI&`%&RAJ@JgdI_`Ca1r%ckZj=zE4y{f_wZxhO zoI@{hW-TG`TfyNlj#;xSBp6-%w4HlPm=0TxbS~xd+JPIZq6LNN8h)eh;G8%pw>J{F zA#0}Sd-E6MzP|Yp#Sdl(=yED~R8qAp`MC=jhMjtv5s3yAxN3^PT+BxS_hF@8T}QD6 z4`B!Pw2z{|^PvjVt|Ga>Ov_G-3J0)oaME&P4ieBb=%%kwF&%ni_jx#O7$t)=aHvNH zNn;&$T>^BP8(Cdd{gKis0n8y<&7nH1aWQ%c0?Gx@s6wDxXK)B0Isp?z&?uLJ{g$%Y zgrbj~-voFAh+v@sC)7hL1Rw!36S^5ZpzbFLz{SZRWMyt{t*jT)hzQ3BxhtATYh)T{w(4Tjf{%{2`F1CkrC2^8tu zpTN!Ay;$82pl3UZ;cXFu3a8cK{Zc8lri3G?JQ0i{hX9QW8@CFw1znv>**QlWMI11iUTBLEF1{9WkJu!uAZpGYIQ$1CP6nvb%TaO1DX$y7P~) zyWqInU3iLuaIPrn09|N8t6bL%u>ctHXi$VE2JKgzd{4iFGFMybT^0nG_Iehgl-_*P+3J!-{mP5L2R;T^x;e3=UDjLycA%_^@cBg_jPEa?p%J zi(E+bXkrjxbh;gmFgZiMpaMq~>LNI1mmX$er#Vg(vD?}9IAO1oETZ=5Q>tH?0akP=tjDypd3Pij+=dDnlq!+Bq{p#(6BvgjVkc!tpy065(dsTX%acQ-YEr z%BhO;$aeHStd7I#dJ3#D@VA0tS8MjYyL~HLp$U-9S;d$3)$c*d`L))?q=% z7poD=+QJTGkiIw@&LJjWJEv(3Qi7`1Y+E=ge>a&Qb?o~wnFjOxAi<8dAnQODjQgk^Alh|^>Zu@&;E zRDE>xLbzaLU}%WlLv4Sd=(f6-`x$&WMRMz`FtCIzz!uc(10nx&UQq)(8H%k!I|l_z z!b&~Yq#2Wz{5Usnb!lkFYE#=t1Fu8cpc@ILd+BAH`87lsvi}t1D%x$Xpda}rh^bBa z)&_C^IvkP9lw~v`b9DuWTl6@1c={&Y2r?#J7J)Ffl*lheWe=Ipu_}X}Fn#}QOFLF8 zT`=Qt12#JX7Y0U^RsGE{x{G|h1uTfKpRbD}qWpu{Mq3&HB6EN-*|T|7;4ER;$HHLe zH%<1zzm(Zmsmgzqw<1F&**}QGIm4@z?FPB}R=TDtQxAR#&VhUy*hyW8Bg`l_QhWsD zR6?89CQs6@uqcIw9zE zJ?meyc6Vnu-I5t(dOei5jxGN!%5w2P%Tj0Dtu`9g6HCsK!esB08R8ZE1w@|^+)2H( zcmkb2???gYrA#0v4@N6)DgAsAx%#lyixotj>#+BExZkH5q|cm(!a)Ep3cQCjxn&5u z$PjC5({5S&eT!g?vMDj2!BqGu|M^v@*Z9(=_(Eoa!j}BUH^Cm`vm1loj&+bk4hePY z!cHCT8~kyioh*FtjYkKZW`XFLj3)}Ow{W{+C!!NQ4ciT>bojcZm1SVc8@?#}kwz8H z>yW`Sc)n$!$Olk3Sew-^Sor;-C;DgG1E%PGw?Klu?W$0B>e7SQYhpu%X=bWrGMspZ z&D=^g3p@aYaXke}cry~H=~mf%_F$VZwZyAvyM#4)_slBqQ2z}lP$2u*0|42_ph97l z%H=QTf;!ad`-}|z;cR|)b3o{?&1aY#VZ z1(Hzz%8gPxPlYu*1!LpD0AK}y4bcJE^7zP|Wzm5rcvqWD1|u;O$jqFblD!{~3{h{c z%+d~&?dINYCTEjlT0%T$cPnQm)q|aYVAo}54Yiyk$ zAUSM4;V;>ajE``R^OrnnatLQIV#0^llVyo9eXX%hp4txDX6lZ%W!Yln$bMF=x~;@} z8wpi)S6e`6XKyf{1Tm#J8}N;xt#w1rI}W(l;16F+V;krVDK2wt%fnoH&ID9QiOSr0 zzpuN$bA#D`=A_sSZ=|18Ls+z;C#C%AKrT6kNYD4Mg@AVspX|Q#c|&z$=e~P;7?VQu z=&1_VwG>ANE30@@z2n4i9^G}}DxcHuD5`jy0zzxW$??@;qIZA*pn=&qga-Wc2NHmP z?$Ac*{Mist#=qjhZ1A5SE~MbQAaEpoC(2+t22pWI-f7xg{b?2=chHAYEs8DK!1nt45b1BWl<(w zcqfHhZ+3FvYg{M1pturhnE4*{X9K{9E4v0&AC9n{@J@n~8jNc)WpHNanKe+bfOl@M z9||j(KIX;-mR=uK1&Tf_GU=M|083}OI6B^D>5Mf;=jN~q0~=3)v#tdSne8`CRCE3r zPzOLg9wRPWM#j1lWmD*)m<&1WVj0E@Uldm33}hIoG;`F;(JJWC|7?{7?b%e~wZv-V z0n~rRxBkE1fBye$>&U$sNO}Nx1q-L&2&^eDMlVzp6>hNJPmHy_Q zNV9W&KVl-E4OLyK4K!rajHz!Xjai~(~miYxrLd<+{3)Z{Kg`5JFqfX9jq1B1KW)~j>G2c z#3|#9agI1Tj)BX?jo_AWPjFvw`*<>59&d~f$FuM&1Z1v*U`EIz+$56pY2pzgmn2Ba zC%KLTWJ#_B4$6Q)1prcqsv4dS^EHeU1X3paMIbvodn`$$@ejW1nUD?k5BbirzMWC5xdsn673jK!yx*%n4Fv{44<+(e;BR z^AmaWvqi59jrOCMULIzXh)guT)*pBaI04!xqG2K#2uu;tGNF^5)*09d0n)XWq0VMf zxi!m-2QGa#vLxcIqpfdvo=fw7f5!=Nl8%0mYFZ7p>3csb8E3qpSKBLAtuiSQufp)h zm?4W9UQ_vBv56`n;FTY{xI_W!b~!X{v)zKELjV7~rZ3M8a zx`_oxy1P*;NYQ4~%A`a2kKfbDVxvYdN_fH-mqWlf*&>XMP|F`3t-T8OF1l&L_JWph zQK$+m-O~Gx4H-FLFIAMBTAu*r_X4?=yW=QC4J;hXD`UaT9DvBqGL_DP9YSO{;V6jx z_mjEIw;L^Da~*8``aI@*_t&vs4Y!+~ z+Vp#W-_Kt=Y!!aA)dOF8EcNR>dm$j%zTgF+q!xER*9u?TI88=Q<88xO)F2H+k9WD# zF8+!X9N;WeT=>C_{ZuB33~t3nlo+=Woq>UBgBHmHlU6PK)Pt6UBxK({@WF;x+w;TQ zw#)gn$k5vkj^F3Ol)%2Kt~SMwcj)?psFNl!V!~cb`Qwi)UT8GxvxtZ+Uhg{xKpPgE zS`XPTGUFr*dvL?a5+_Y3|0GtT;Sx$*85U@`XcBO~v4eXoJJ}=Uc_-vxuDyP(DFx+a zfJWW9VF>G@wjd2d7cQbMD^4L;nKfId&Rvahnt>GqOsH0~3PTZiRO*eOjpC<@2QTgiO-j1wq ztCB&hs-o^ok(8dOR(#ltX(Da}Mo!f3?_1r4clbK$P1>E?L3zI#3T2q1RvDI?*L*&j zNrbLaVjYC!9I&)WQ#cKguf)$H)>7sAG-a+7ZWgqRCKe_5bD7(f0u7C@wLrnbLJ_!h zQuz0P##R?6$H1v!J*eEKfqkTOEPy~NzFS*> z(4CcoV2F;AcpyV#DR6Y%i5S1!M5wYF55SppS(GGoStCW^o2`_A&W7L zv3lKmO`0j=#`%eraiMM_(*3pSX9nD)Ogr?QVWJI}LXXswCQ=P|&tR=8g7|3(vx;to zT3;x5FcmKr2`{p;!nqrdelW}quVO4XXtm&otnC~+t+NjI2J>3txOIFNX@>KvqhN~U z#-li14{x~;pMeFUPn8LPLaDlVpKg2O266Qo&+@`Z;I;qBa{k7|t3}B(ugi0SM_h-b z)njL`fmqa*+&Z?hhB4STpIt2)|-WM~iq-c#}A{;b-`?)SL9yobJQ`gA3BGh%n4wu12k~LV+E%WV_lnJq zj4~axmLR^>VjyNy3ufiLvq4;M;*l0JQ4k|7W|XYjayy6tR;YjjE1W}0s{#JzZzP{K z^P$xQSu#!lUmi?t$|m-o?pAZ_X9RT?iN!1x6%Y1&!7WOA)2#@ZHs*NT4^p^1n`nBr z+#J%9r)|yBe@lQqTP4;C`08WIo_CAicY*J^z$4!P1a3%dQG%R)7Ox4OAmVX1c~z1D z9#-RqAAKIGT?_JUds|wxL}@Pm!PwY`GxH0#9_fy{@YVn6K8ppZc9aLMsLePo7q4Rs zc2gz_^n-irz8Tpxz12S?jvq+W#%-Ve|IC?bVN?CdR`eJ$l1Nz6rF!6^+Y+H?bbZn0dmz z#bu)}e=&>YAFF!yaYh%~VBSSyaPNt4whFaLevE^2gP?<#do0#p*-!TQV~mWhTAm;E z`vZaD$=(+^IK@%m4mb@C*P!)_%yT^UIJ(#OSm_XVDC>LN>kxg-?eUUB3^sQbGmha` zRsD0|^h)J2(3Ta?0u5D2(+GBz-C>ONw)8V)a6tslG6X^>#bR-JKJ)q1 zL@ahs#Q;&lQ$hxE*Xuw^78pFJRz%(Ct%6Jy=LZ~;X)gR@U5|xg5i;PkC7~OJzR%+6 ztwwb^ZZAy74i2YG{+sEJBM@i`cn?2OW-olWs z$(PuX_IThXgTV>jNC_T=YO|=K6pnU&u3yY%iyrK5h*^@9ObH)`iN1D13A4wpTYu<6 zuCz!>ZyWSMsPICiP{_vP*@DNKXek(^!Q76~_&wo~f4ImgQUg+@kaZSWA-KS~_&a0z z%4)HUK*3sl>#6Tm`E^QJy8JPe>pm0`o>u4SQzM=T!z`Jcci$IY{N60Ce_ANYTlU>v zM-kuT$eTbYO5%0gVD)Tep~cZS+YZ`C^GhSC`7W3HaSd-wbv1xW*gv=U^@h=|ICyop z2Zs$FmP*TWAH8p(_HdUKzP8O>6t*tPJg#TUU$+ok)OTX%X3`yxuJG}*Dqfq-dTX@^ zPqo#$%Vs#U1qYzI6yfebLfn+I&g12sF}DADQ?vD0bG9~;|K1p@{bQ;P$=?lA<15Hu-$hg<79$kyn`ZF@p+J z122TX-7vT0kpeIUhkyE+otOO52s%0lH#WrA8s4rXkS8=YGi>=%;r z>|`)WUS>(>HtRgly#eps*_Z|+W zo%X=~efRv|!sB|_4kD4mfuTSo9K1(6JzgFg`yf}O8nsB0z@^enOei;$Z&Z}|xiy~l z`iTnm9ofES{!~VzJt^U>tK_GtG@rikbxt(SXJ+IWNWlXXN~z~ErKVNWuWB&oa^DFH zJWp^Nr8QYr48z#{D45Lo%Gc|(U1;S`U4I^XZ)lGxDQV4Woo(Ty_pQN0`}eue%nbJq zylFFMR6kV>cS^Xr@I;n{rsdcKr4e{}NVN~wgGcvLm3`N@PZ?+gs?f7jEVOU4&G zPyU?KtoAhXA7hKA9m~^**G|2L?MHZe#s-{ZgQRulkj>lvV zb)*$FLgRM5I%L5LyGx6d=PY!26}jDOV!G+tflW?d-Ey9nQ|qfOP3OrF|`Wh`rI;V615~;2U^jj z(djgvjsikl{9=N<+F&U=*b~8cm07d9Xm)1>mK8pr16(h4-#TBJTQ890t+@s3R@UDV zz2|7?#Ibv2Bz3T{tFRwCNc^l9FszNiG`y;`Qg0ezfUW6^u z5%-^kv}zaDrIY2`?x{JMv8o-?#-Z^xTDlAeZ2icC+}_#b%yt);Nia5=bTl3PQ<>?w zWS1;W;%irK%JZ48iXI6oR8ovexSbjFI|he+Sj8|-gM}(GHRr>{;2;Gb>a{0=9X+FS znV|!atS{f$A~oY*T4KOC5)OGzjwHsT&R9HQe3o0#L*YECgmvi~jxH-2zf*15_=fl3 zA4t}Zwm`dadt5=SoU#QzeStBd#ylxdB)T`w1`%CB3#RVVjyA#jWg0W}_Lc zc&t33^-9oS547IH)i{Xx=r<=)#eeZkT^iYvh*uvs6JS8l*KFFAFxU0S?3Ec^?;6A^ z{6|~2g8dceomy5|S>Ml4f1A6XULa$tC8e27@?F5UJn;2k`J^NB5(hVVuDLeB+ldn_ zk=2Fw1_Z!`=l~uv4G5;X3T9;RHz+_DEr1ZL`v`Oh`6zHW;iFM0F*1Ymso-+`Na|Qm zBo7>(bv~W~|MMBJ*z_3@VlsEg$>1|l6uU1UG3~wr3Nq{~WKZUnL_o-`*cd09mJ&B2 zCQbCS#aT7-0;TVjz6?u&x#~H*>BTGF)NtGD-;+-13VKkszf8AIy;(km4 G0002QVTgqQ literal 0 HcmV?d00001 diff --git a/assets/inter-roman-cyrillic-ext.8T9wMG5w.woff2 b/assets/inter-roman-cyrillic-ext.8T9wMG5w.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..28593ccb8a4d849a746f2b970678fe426cb136e8 GIT binary patch literal 26600 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 literal 0 HcmV?d00001 diff --git a/assets/inter-roman-cyrillic.jIZ9REo5.woff2 b/assets/inter-roman-cyrillic.jIZ9REo5.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..a20adc161f433a7c4e3d92306301b9228bcf9fb4 GIT binary patch literal 16780 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

KZ;k8ywA} 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 O1{o}0SRMEP0000Yp_Hru literal 0 HcmV?d00001 diff --git a/assets/inter-roman-greek.Cb5wWeGA.woff2 b/assets/inter-roman-greek.Cb5wWeGA.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..f790e047daa346583880da2be470431e35aa6054 GIT binary patch literal 21776 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 De!(H( literal 0 HcmV?d00001 diff --git a/assets/inter-roman-latin-ext.GZWE-KO4.woff2 b/assets/inter-roman-latin-ext.GZWE-KO4.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..715bd903b9b14d22a056f10e6d13b8d7e0acce57 GIT binary patch literal 59608 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

cpp-httplib

A C++11 single-file header-only cross platform HTTP/HTTPS library.

It's extremely easy to setup. Just include httplib.h file in your code!

Server Example

c++
#include <httplib.h>
+
+int main(void)
+{
+  using namespace httplib;
+
+  Server svr;
+
+  svr.Get("/hi", [](const Request& req, Response& res) {
+    res.set_content("Hello World!", "text/plain");
+  });
+
+  svr.Get(R"(/numbers/(\\d+))", [&](const Request& req, Response& res) {
+    auto numbers = req.matches[1];
+    res.set_content(numbers, "text/plain");
+  });
+
+  svr.Get("/body-header-param", [](const Request& req, Response& res) {
+    if (req.has_header("Content-Length")) {
+      auto val = req.get_header_value("Content-Length");
+    }
+    if (req.has_param("key")) {
+      auto val = req.get_param_value("key");
+    }
+    res.set_content(req.body, "text/plain");
+  });
+
+  svr.Get("/stop", [&](const Request& req, Response& res) {
+    svr.stop();
+  });
+
+  svr.listen("localhost", 1234);
+}

Post, Put, Delete and Options methods are also supported.

Bind a socket to multiple interfaces and any available port

cpp
int port = svr.bind_to_any_port("0.0.0.0");
+svr.listen_after_bind();

Static File Server

cpp
// Mount / to ./www directory
+auto ret = svr.set_mount_point("/", "./www");
+if (!ret) {
+  // The specified base directory doesn't exist...
+}
+
+// Mount /public to ./www directory
+ret = svr.set_mount_point("/public", "./www");
+
+// Mount /public to ./www1 and ./www2 directories
+ret = svr.set_mount_point("/public", "./www1"); // 1st order to search
+ret = svr.set_mount_point("/public", "./www2"); // 2nd order to search
+
+// Remove mount /
+ret = svr.remove_mount_point("/");
+
+// Remove mount /public
+ret = svr.remove_mount_point("/public");
cpp
// User defined file extension and MIME type mappings
+svr.set_file_extension_and_mimetype_mapping("cc", "text/x-c");
+svr.set_file_extension_and_mimetype_mapping("cpp", "text/x-c");
+svr.set_file_extension_and_mimetype_mapping("hh", "text/x-h");

The followings are built-in mappings:

ExtensionMIME Type
txttext/plain
html, htmtext/html
csstext/css
jpeg, jpgimage/jpg
pngimage/png
gifimage/gif
svgimage/svg+xml
icoimage/x-icon
jsonapplication/json
pdfapplication/pdf
jsapplication/javascript
wasmapplication/wasm
xmlapplication/xml
xhtmlapplication/xhtml+xml

NOTE: These the static file server methods are not thread safe.

Logging

cpp
svr.set_logger([](const auto& req, const auto& res) {
+  your_logger(req, res);
+});

Error handler

cpp
svr.set_error_handler([](const auto& req, auto& res) {
+  auto fmt = "<p>Error Status: <span style='color:red;'>%d</span></p>";
+  char buf[BUFSIZ];
+  snprintf(buf, sizeof(buf), fmt, res.status);
+  res.set_content(buf, "text/html");
+});

'multipart/form-data' POST data

cpp
svr.Post("/multipart", [&](const auto& req, auto& res) {
+  auto size = req.files.size();
+  auto ret = req.has_file("name1");
+  const auto& file = req.get_file_value("name1");
+  // file.filename;
+  // file.content_type;
+  // file.content;
+});

Receive content with Content receiver

cpp
svr.Post("/content_receiver",
+  [&](const Request &req, Response &res, const ContentReader &content_reader) {
+    if (req.is_multipart_form_data()) {
+      MultipartFormDataItems files;
+      content_reader(
+        [&](const MultipartFormData &file) {
+          files.push_back(file);
+          return true;
+        },
+        [&](const char *data, size_t data_length) {
+          files.back().content.append(data, data_length);
+          return true;
+        });
+    } else {
+      std::string body;
+      content_reader([&](const char *data, size_t data_length) {
+        body.append(data, data_length);
+        return true;
+      });
+      res.set_content(body, "text/plain");
+    }
+  });

Send content with Content provider

cpp
const size_t DATA_CHUNK_SIZE = 4;
+
+svr.Get("/stream", [&](const Request &req, Response &res) {
+  auto data = new std::string("abcdefg");
+
+  res.set_content_provider(
+    data->size(), // Content length
+    "text/plain", // Content type
+    [data](size_t offset, size_t length, DataSink &sink) {
+      const auto &d = *data;
+      sink.write(&d[offset], std::min(length, DATA_CHUNK_SIZE));
+      return true; // return 'false' if you want to cancel the process.
+    },
+    [data] { delete data; });
+});

Without content length:

cpp
svr.Get("/stream", [&](const Request &req, Response &res) {
+  res.set_content_provider(
+    "text/plain", // Content type
+    [&](size_t offset, size_t length, DataSink &sink) {
+      if (/* there is still data */) {
+        std::vector<char> data;
+        // prepare data...
+        sink.write(data.data(), data.size());
+      } else {
+        done(); // No more data
+      }
+      return true; // return 'false' if you want to cancel the process.
+    });
+});

Chunked transfer encoding

cpp
svr.Get("/chunked", [&](const Request& req, Response& res) {
+  res.set_chunked_content_provider(
+    [](size_t offset, DataSink &sink) {
+      sink.write("123", 3);
+      sink.write("345", 3);
+      sink.write("789", 3);
+      sink.done(); // No more data
+      return true; // return 'false' if you want to cancel the process.
+    }
+  );
+});

'Expect: 100-continue' handler

As default, the server sends 100 Continue response for Expect: 100-continue header.

cpp
// Send a '417 Expectation Failed' response.
+svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
+  return 417;
+});
cpp
// Send a final status without reading the message body.
+svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
+  return res.status = 401;
+});

Keep-Alive connection

cpp
svr.set_keep_alive_max_count(2); // Default is 5

Timeout

c++
svr.set_read_timeout(5, 0); // 5 seconds
+svr.set_write_timeout(5, 0); // 5 seconds
+svr.set_idle_interval(0, 100000); // 100 milliseconds

Set maximum payload length for reading request body

c++
svr.set_payload_max_length(1024 * 1024 * 512); // 512MB

Server-Sent Events

Please see Server example and Client example.

Default thread pool support

ThreadPool is used as a default task queue, and the default thread count is set to value from std::thread::hardware_concurrency().

You can change the thread count by setting CPPHTTPLIB_THREAD_POOL_COUNT.

Override the default thread pool with yours

cpp
class YourThreadPoolTaskQueue : public TaskQueue {
+public:
+  YourThreadPoolTaskQueue(size_t n) {
+    pool_.start_with_thread_count(n);
+  }
+
+  virtual void enqueue(std::function<void()> fn) override {
+    pool_.enqueue(fn);
+  }
+
+  virtual void shutdown() override {
+    pool_.shutdown_gracefully();
+  }
+
+private:
+  YourThreadPool pool_;
+};
+
+svr.new_task_queue = [] {
+  return new YourThreadPoolTaskQueue(12);
+};

Client Example

c++
#include <httplib.h>
+#include <iostream>
+
+int main(void)
+{
+  httplib::Client cli("localhost", 1234);
+
+  if (auto res = cli.Get("/hi")) {
+    if (res->status == 200) {
+      std::cout << res->body << std::endl;
+    }
+  } else {
+    auto err = res.error();
+    ...
+  }
+}

NOTE: Constructor with scheme-host-port string is now supported!

c++
httplib::Client cli("localhost");
+httplib::Client cli("localhost:8080");
+httplib::Client cli("http://localhost");
+httplib::Client cli("http://localhost:8080");
+httplib::Client cli("https://localhost");

GET with HTTP headers

c++
httplib::Headers headers = {
+  { "Accept-Encoding", "gzip, deflate" }
+};
+auto res = cli.Get("/hi", headers);

or

c++
cli.set_default_headers({
+  { "Accept-Encoding", "gzip, deflate" }
+});
+auto res = cli.Get("/hi");

POST

c++
res = cli.Post("/post", "text", "text/plain");
+res = cli.Post("/person", "name=john1&note=coder", "application/x-www-form-urlencoded");

POST with parameters

c++
httplib::Params params;
+params.emplace("name", "john");
+params.emplace("note", "coder");
+
+auto res = cli.Post("/post", params);

or

c++
httplib::Params params{
+  { "name", "john" },
+  { "note", "coder" }
+};
+
+auto res = cli.Post("/post", params);

POST with Multipart Form Data

c++
httplib::MultipartFormDataItems items = {
+  { "text1", "text default", "", "" },
+  { "text2", "aωb", "", "" },
+  { "file1", "h\\ne\\n\\nl\\nl\\no\\n", "hello.txt", "text/plain" },
+  { "file2", "{\\n  \\"world\\", true\\n}\\n", "world.json", "application/json" },
+  { "file3", "", "", "application/octet-stream" },
+};
+
+auto res = cli.Post("/multipart", items);

PUT

c++
res = cli.Put("/resource/foo", "text", "text/plain");

DELETE

c++
res = cli.Delete("/resource/foo");

OPTIONS

c++
res = cli.Options("*");
+res = cli.Options("/resource/foo");

Timeout

c++
cli.set_connection_timeout(0, 300000); // 300 milliseconds
+cli.set_read_timeout(5, 0); // 5 seconds
+cli.set_write_timeout(5, 0); // 5 seconds

Receive content with Content receiver

c++
std::string body;
+
+auto res = cli.Get("/large-data",
+  [&](const char *data, size_t data_length) {
+    body.append(data, data_length);
+    return true;
+  });
cpp
std::string body;
+
+auto res = cli.Get(
+  "/stream", Headers(),
+  [&](const Response &response) {
+    EXPECT_EQ(200, response.status);
+    return true; // return 'false' if you want to cancel the request.
+  },
+  [&](const char *data, size_t data_length) {
+    body.append(data, data_length);
+    return true; // return 'false' if you want to cancel the request.
+  });

Send content with Content provider

cpp
std::string body = ...;
+
+auto res = cli_.Post(
+  "/stream", body.size(),
+  [](size_t offset, size_t length, DataSink &sink) {
+    sink.write(body.data() + offset, length);
+    return true; // return 'false' if you want to cancel the request.
+  },
+  "text/plain");

With Progress Callback

cpp
httplib::Client client(url, port);
+
+// prints: 0 / 000 bytes => 50% complete
+auto res = cli.Get("/", [](uint64_t len, uint64_t total) {
+  printf("%lld / %lld bytes => %d%% complete\\n",
+    len, total,
+    (int)(len*100/total));
+  return true; // return 'false' if you want to cancel the request.
+}
+);

progress

Authentication

cpp
// Basic Authentication
+cli.set_basic_auth("user", "pass");
+
+// Digest Authentication
+cli.set_digest_auth("user", "pass");
+
+// Bearer Token Authentication
+cli.set_bearer_token_auth("token");

NOTE: OpenSSL is required for Digest Authentication.

Proxy server support

cpp
cli.set_proxy("host", port);
+
+// Basic Authentication
+cli.set_proxy_basic_auth("user", "pass");
+
+// Digest Authentication
+cli.set_proxy_digest_auth("user", "pass");
+
+// Bearer Token Authentication
+cli.set_proxy_bearer_token_auth("pass");

NOTE: OpenSSL is required for Digest Authentication.

Range

cpp
httplib::Client cli("httpbin.org");
+
+auto res = cli.Get("/range/32", {
+  httplib::make_range_header({{1, 10}}) // 'Range: bytes=1-10'
+});
+// res->status should be 206.
+// res->body should be "bcdefghijk".
cpp
httplib::make_range_header({{1, 10}, {20, -1}})      // 'Range: bytes=1-10, 20-'
+httplib::make_range_header({{100, 199}, {500, 599}}) // 'Range: bytes=100-199, 500-599'
+httplib::make_range_header({{0, 0}, {-1, 1}})        // 'Range: bytes=0-0, -1'

Keep-Alive connection

cpp
httplib::Client cli("localhost", 1234);
+
+cli.Get("/hello");         // with "Connection: close"
+
+cli.set_keep_alive(true);
+cli.Get("/world");
+
+cli.set_keep_alive(false);
+cli.Get("/last-request");  // with "Connection: close"

Redirect

cpp
httplib::Client cli("yahoo.com");
+
+auto res = cli.Get("/");
+res->status; // 301
+
+cli.set_follow_location(true);
+res = cli.Get("/");
+res->status; // 200

Use a specitic network interface

NOTE: This feature is not available on Windows, yet.

cpp
cli.set_interface("eth0"); // Interface name, IP address or host name

OpenSSL Support

SSL support is available with CPPHTTPLIB_OPENSSL_SUPPORT. libssl and libcrypto should be linked.

NOTE: cpp-httplib currently supports only version 1.1.1.

c++
#define CPPHTTPLIB_OPENSSL_SUPPORT
+
+SSLServer svr("./cert.pem", "./key.pem");
+
+SSLClient cli("localhost", 8080);
+cli.set_ca_cert_path("./ca-bundle.crt");
+cli.enable_server_certificate_verification(true);

Compression

The server can applie compression to the following MIME type contents:

  • all text types except text/event-stream
  • image/svg+xml
  • application/javascript
  • application/json
  • application/xml
  • application/xhtml+xml

Zlib Support

'gzip' compression is available with CPPHTTPLIB_ZLIB_SUPPORT. libz should be linked.

Brotli Support

Brotli compression is available with CPPHTTPLIB_BROTLI_SUPPORT. Necessary libraries should be linked. Please see https://github.com/google/brotli for more detail.

Compress request body on client

c++
cli.set_compress(true);
+res = cli.Post("/resource/foo", "...", "text/plain");

Compress response body on client

c++
cli.set_decompress(false);
+res = cli.Get("/resource/foo", {{"Accept-Encoding", "gzip, deflate, br"}});
+res->body; // Compressed data

Split httplib.h into .h and .cc

bash
> python3 split.py
+> ls out
+httplib.h  httplib.cc

NOTE

g++

g++ 4.8 and below cannot build this library since <regex> in the versions are broken.

Windows

Include httplib.h before Windows.h or include Windows.h by defining WIN32_LEAN_AND_MEAN beforehand.

cpp
#include <httplib.h>
+#include <Windows.h>
cpp
#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <httplib.h>

Note: Cygwin on Windows is not supported.

License

MIT license (© 2020 Yuji Hirose)

Special Thanks To

These folks made great contributions to polish this library to totally another level from a simple toy!

`,123),l=[h];function k(p,e,E,r,d,g){return a(),i("div",null,l)}const F=s(n,[["render",k]]);export{o as __pageData,F as default}; diff --git a/assets/system_httplib_README.md.ayRNZub-.lean.js b/assets/system_httplib_README.md.ayRNZub-.lean.js new file mode 100644 index 00000000000..d6bfa755c0f --- /dev/null +++ b/assets/system_httplib_README.md.ayRNZub-.lean.js @@ -0,0 +1 @@ +import{_ as s,c as i,o as a,V as t}from"./chunks/framework.gBlNPWt_.js";const o=JSON.parse('{"title":"cpp-httplib","description":"","frontmatter":{},"headers":[],"relativePath":"system/httplib/README.md","filePath":"system/httplib/README.md","lastUpdated":1721825996000}'),n={name:"system/httplib/README.md"},h=t("",123),l=[h];function k(p,e,E,r,d,g){return a(),i("div",null,l)}const F=s(n,[["render",k]]);export{o as __pageData,F as default}; diff --git a/assets/system_masking_include_readme.md.i5B-dOzb.js b/assets/system_masking_include_readme.md.i5B-dOzb.js new file mode 100644 index 00000000000..a3eef351213 --- /dev/null +++ b/assets/system_masking_include_readme.md.i5B-dOzb.js @@ -0,0 +1,93 @@ +import{_ as e,c as t,o as a,V as n}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"Data Masking Framework","description":"","frontmatter":{},"headers":[],"relativePath":"system/masking/include/readme.md","filePath":"system/masking/include/readme.md","lastUpdated":1721825996000}'),s={name:"system/masking/include/readme.md"},o=n(`

Data Masking Framework

This is a high level description of the data obfuscation framework defined in system/masking/include. The framework includes a platform component, the engine, that exposes obfuscation logic supplied by dynamically loaded plugin libraries. The framework can be used to obfuscate sensitive data, such as passwords and PII. Possible use cases including preventing trace output from revealing sensitive values and partially masking values in situations where a user needs to see "enough" of a value to confirm its correctness without seeing all of the value (e.g., when a user is asked to confirm the last four digits of an account number).

FileContents
datamasking.hDeclares the core framework interfaces. Most are used by the engine and plugins.
datamaskingengine.hppDefines the engine component from the core engine interface.
datamaskingplugin.hppDefines a set of classes derived from the core plugin interfaces that can be used to provide rudimentary obfuscation. It is expected that the some or all classes will be subclassed, if not replaced outright, in new plugins.
datamaskingshared.hppDefines utilities shared by engine and plugin implementations.

Glossary

Domain

A domain is a representation of the obfuscation requirements applicable to a set of data.

Consider that the data used to represent individuals likely differs between countries. With different data, requirements for obfuscation may reasonably be expected to vary. Assuming that requirements do change between countries, each country's requirements could logically constitute a separate domain. The capacity to define multiple domains does not create a requirement to do so.

Requirements can change over time. To support this, a domain can be seen as a collection of requirement snapshots where each snapshot defines the complete set of requirements for the domain at a point in time. Snapshots are referenced by unique version numbers, which should be sequential starting at 1.

Obfuscation is always applied based on a single snapshot of a domain's requirements.

Domains are represented in the framework interface as text identifiers. Each distinct domain is identified by at least one unique identifier.

Masker

A masker is a provider of obfuscation for a single snapshot of a domain's requirements. There are three masking operations defined in this framework. Each instance decides which of the three it will support, and how it will support them. The three operations are:

  • maskValue obfuscates individual values based on snapshot-defined meanings. For example, a value identified as a password might require complete obfuscation anywhere it appears, or an account number may require complete obfuscation in some cases and partial obfuscation in others.
  • maskContent obfuscates a variable number of values based on context provided by surrounding text. For example, the value of an HTTP authentication header or the text between <Password> and </Password> might require obfuscation. This operation can apply to both structured and unstructured text content.
  • maskMarkupValue obfuscates individual values based on their locationa within an in-memory representation of a structured document, such as XML or JSON. For example, the value of element Password might require obfuscation unconditionally, while the value of element Value might require obfuscation only if a sibling element named Name exists with value password. This operation relies on the caller's ability to supply context parsed from structured content.

A masker may be either stateless or stateful. With a stateless masker, identical input will produce identical output for every requested operation. A stateful masker, however, enables its user to affect operation outputs (i.e., identical input may not produce identical output for each operation).

Maskers are represented in the framework interface using IDataMasker, with IDataMaskerInspector providing access to less frequently used information about the domain.

Profile

A profile is a stateless masker. Each instance defines the requirements of one or more snapshots of a single domain.

Snapshots are versioned. Each profile declares a minimum, maximum, and default version. Masker operations apply to the default version. Other declared versions may be accessed using a stateful context, which the profile can create on demand.

Each instance must support at least one version of a domain's requirements. Whether an instance supports more than one version depends on the implementation and on user preference. A domain can be viewed as a collection of one or more profiles where each profile defines a unique set of requirement snapshots applicable to the same underlying data.

Refer to IDataMaskingProfile (extending IDataMasker) and IDataMaskingProfileInspector (extending IDataMaskerInspector) for additional information.

Context

A context is a stateful masker. Instantiated by and tightly coupled to a profile, it provides some user control over how masking operations are completed.

  • For a profile supporting multiple versions, the requirements of a non-default version may be applied.
  • Custom properties, defined by a profile, may be managed.
    • valuetype-set is a pre-defined property used to select the group of value types that may be masked by any operation.
    • rule-set is a pre-defined property used to select the group of rules that will be applied for maskContent requests.
    • Profile implementations may define additional properties as needed. For example, one might define mask-pattern to override the default obfuscation pattern to avoid replicating (and complicating) configurations just to change the appearance of obfuscated data.
  • Trace output produced by operation requests, including errors and warnings encountered during request processing, can be controlled per context. This does not affect the operation output, per-se, but provides compatibility with transactional trace output control.

Refer to IDataMaskingProfileContext (extending IDataMasker) and IDataMaskingProfileContextInspector (extending IDataMaskerInspector) for additional information.

Value Type

Each snapshot defines one or more value types. A value type is a representation of the requirements pertaining to a particular concept of a domain datum. Requirements include:

  • instructions for identifying occurrences based on contextual clues found in a body of text; and
  • instructions for applying obfuscation to an identified value.

A Social Security Number, or SSN, is a U.S.-centric datum that requires obfuscation and for which a value type may be defined. Element names associated with an SSN may include, but are not limited to, SSN and SOCS; the value type is expected to identify all such names used within the domain. SSN occurrences are frequently partially masked, with common formats being to mask only the first four or the last five digits of the nine digit number; the value type defines which formats are available besides the default of masking all characters.

Value types are represented in the framework using IDataMaskingProfileValueType. They are accessed through the IDataMaskerInspector interface.

Mask Style

A mask style describes how obfuscation is applied to a value. It is always defined in the context of a value type, and a value type may define multiple.

A value type is not required to define any mask styles. If none are defined, all value characters are obfuscated. If the requested mask style is not defined, the default obfuscation occurs; the value type will not attempt to guess which of the defined styles is appropriate.

Mask styles are represented in the framework using IDataMaskingProfileMaskStyle. They are accessed through the IDataMaskingProfileValueType interface.

Rule

A rule contains the information necessary to locate at least one occurrence of a value type datum to be obfuscated. It is always defined in the context of a value type, and a value type may define multiple.

maskValue requests do not use rules. maskContent requests rely on rules for locating affected values, with the relationship between a profile and its rules an implementation detail. maskMarkupValue requests, when implemented, may also use rules or may take an entirely different approach.

Rules are represented in the framework as an abastract concept that cannot be inspected individually. Inspection may be used to establish the presence of rules, but not to examine individual instances.

Plugin

The combination of a shared library and entry point function describes a plugin. The input to a plugin is a property tree describing one or more profiles. The output of an entry point function is an iterator of profiles. The profiles created by the function may all be associated with the same domain, but this is not required.

Plugin results are represented in the framework using IDataMaskingProfileIterator.

Engine

An engine is the platform's interface to obfuscation. It loads domains by loading one or more plugins. Plugins yield profiles, from which domains are inferred.

Once configured with at least one domain, a caller can obtain obfuscation in multiple ways:

  1. As an instance of a stateless masker, an engine can provide obfuscation. The default version of the default profile of the default domain will be used, and no custom context properties can be used. This is the simplest integration, appropriate for use when the host process implicitly trusts that the default configuration is sufficient.
  2. An engine may be used to obtain a stateless profile for a specific domain. If no version is requested, the default profile of the requested domain will be used. If a version is specified, the domain profile supporting the requested version is used. This usage supports host processes that are aware of domains and need to use specific instances.
  3. An engine may be used to obtain a context tied to a specific version of a domain. The default domain may be requested with an empty string. The default version may be requested by passing 0. With a context, a caller may manipulate further the environment for obfuscation requests.

Engines are represented in the framework using IDataMaskingEngine (extending IDataMasker) and IDataMaskingEngineInspector for additional information.

Environment

This section assumes a context is in use. Absent a context, only the default state of the default version of a profile can be used.

The framework reserves multiple custom property names, which are described in subsequent subsections. It also allows implementations to define additional properties using any non-reserved name.

Suppose an implementation defines a property to override the default obfuscation pattern. Let's call this property default-pattern. A caller could interrogate a masker to know if default-pattern is accepted. If accepted, setProperty("default-patterns", "#") can be used to register an override.

All custom properties, whether defined in the framework or in third party libraries, are managed using a generic context interface. This interface includes:

bool hasProperties() cosnt
+bool hasProperty(const char* name) const
+const char* queryProperty(const char* name) const
+bool setProperty(const char* name, const char* value)
+bool removeProperty(const char* name)

Value Type Sets

bool setProperty(const char* name, const char* value);

Overview

The abstraction includes the concept of sets of related value types. All value types should be assigned membership in at least one set. One set should be selected by default. The custom context property valuetype-set is used to select a different set.

The rationale for this is an expectation that certain data always requires obfuscation. A password, for example, would never not require obfuscation. Other data may only require obfuscacation in certain situations, such as when required by individual customer agreements. Callers should not be required to complete additional steps to act on data that must always be obfuscated, and should not need to know which data falls in which category.

The set name "*" is reserved to select all value types regardless of their defined set membership. This mechanism is intended to assist with compatibility checks, and should be used with care in other situations.

Runtime Compatibility

Use acceptsProperty to determine whether a snapshot recognizes a custom property name. Use usesProperty to learn if the snapshot includes references to the custom property name. Acceptance implies awareness of the set (and what it represents) even if selecting it will not change the outcome of any masking request. Usage implies that selecting the set will change the outcome of at least one masking operation.

The property valuetype-set is used to select a named set. A check of this property addresses whether the concept of a set is accepted or used by the snapshot.

For improved compatibility checks, implementations are encouraged to reserve additional property names that enable checks for individual set names. For each set name, foo, the included implementations report property valuetype-set:foo as used.

Implementation

The included implementations allow membership in either a default, unnamed set or in any number of named sets. Absent a contextual request for a named set, the unnamed set is selected by default. With a contextual set request, the members of the named set are selected in addition to members of the unnamed set. Members of the unnamed set are never not selected.

Unnamed set membership cannot be defined explicitly. Because this set is always selected, assigning a value type to both a named and the unnamed set is redundant - the type will be selected whether the named set is requested or not.

Example 1a: default value type

profile:
+  valueType:
+    - name: type1

Value type type1 belongs to the default, unnamed value type set. Property valuetype-set is accepted, but not used; an attempt to use it will change no results.

Example 1b: sample set memberships

profile:
+  valueType:
+    - name: type1
+    - name: type2
+      memberOf:
+        - name: set1
+        - name: set2
+    - name: type3
+      memberOf:
+        - name: set2
+        - name: set3
+    - name: type4
+      memberOf:
+        - name: set1
+    - name: type5
+      memberOf:
+        - name: set3

Extending the previous example, five types and four sets are in use. Property valuetype-set is both accepted and used, as it can now impact results. Properties valuetype-set:set1, valuetype-set:set2 and valuetype-set:set3 are also both accepted and used, but are intended only for use with compatibility checks.

This table shows which types are selected for each requested set:

unnamedset1set2set3*
type1YYYYY
type2NYYNY
type3NNYYY
type4NYNNY
type5NNNYY

Example 1c: sample accepted sets

profile:
+  valueType:
+    - name: type1
+    - name: type2
+      memberOf:
+        - name: set1
+        - name: set2
+    - name: type3
+      memberOf:
+        - name: set2
+        - name: set3
+    - name: type4
+      memberOf:
+        - name: set1
+    - name: type5
+      memberOf:
+        - name: set3
+  property:
+    - name: 'valuetype-set:set4'
+    - name: 'valuetype-set:set5'

Continuing the previous example, the profile has declared acceptance of two additional sets using properties valuetype-set:set4 and valuetype-set:set5. Selection of these sets will not change results, but a caller might accept acceptance of a set as sufficient to establish compatibility.

Rule Sets

bool setProperty(const char* name, const char* value);

Overview

The abstraction includes the concept of sets of related rules. All rules should be assigned membership in at least one set. One set should be selected by default. The custom context property rule-set is used to select a different set.

The rationale for this is similar to yet different than that of value type sets. Instead of one set of rules that are always selected, backward compatibility with a proprietary legacy implementation requires that the default set be replaced by an alternate collection.

The set name "*" is reserved to select all rules regardless of their defined set membership. This does not override constraints imposed by the current value type set. This mechanism is intended to assist with compatibility checks, and should be used with care in other situations.

Runtime Compatibility

Use acceptsProperty to determine whether a snapshot recognizes a custom property name. Use usesProperty to learn if the snapshot includes references to the custom property name. Acceptance implies awareness of the set (and what it represents) even if selecting it will select no rules. Usage implies that selecting the set will select at least one rule.

The property rule-set is used to select a named set. A check of this property addresses whether the concept of a set is accepted or used by the snapshot.

For improved compatibility checks, implementations are encouraged to reserve additional property names that enable checks for individual set names. For each set name, foo, the included implementations report property rule-set:foo as used.

Implementation

The included implementations allow membership in any number of named sets as well as a default, unnamed set. Absent a contextual request for a named set, the unnamed set is selected by default. With a contextual set request, only the members of the requested set are selected.

Unlike value type sets, the unnamed set is not always selected. Because of this difference, a rule may be assigned explicit membership in the unnamed set. This is optional for rules that belong only to the unnamed set and is required for rules meant to be selected as part of the unnamed set and one or more named sets.

Example 2: rule set memberships

profile:
+  valueType:
+    name: type1
+    rule:
+      - name: foo
+      - memberOf:
+          - name: ''
+      - memberOf:
+          - name: ''
+          - name: set1
+      - memberOf:
+          - name: set1
+  property:
+    name: 'rule-set:set2'

The rules described here are intentionally incomplete, showing only what is necessary for this example. Four rules are defined:

  1. The rule named foo implicitly belongs to the unnamed set.
  2. The second rule explicitly belongs to the unnamed set.
  3. The third rule explicitly belongs to the unnamed set and to set1.
  4. The fourth rule belongs to set1.

property rule-set is both accepted and used. Properties rule-set: and rule-set:set1 are both accepted and used, intended for use with compatibility checks. Property rule-set:set2 is accepted.

Operations

maskValue

bool maskValue(const char* valueType, const char* maskStyle, char* buffer, size_t offset, size_t length, bool conditionally) const;
+bool maskValueConditionally(const char* valueType, const char* maskStyle, char* buffer, size_t offset, size_t length) const;
+bool maskValueUnconditionally(const char* valueType, const char* maskStyle, char* buffer, size_t offset, size_t length) const;

Overview

Given a single domain datum, obfuscate the content "as needed". The framework anticipates two interpretations of "as needed", either conditional or unconditional:

  • Conditional means that values of a named type are only obfuscated if the named type is known by and selected in the snapshot. This usage assumes that the profile is the authoritative source for obfuscation requirements. The profile's consumer may ask for any value to be obfuscated, but the profile is not required to act.
  • Unconditional means that all value obfuscation requests will result in obfuscation. The named type is not required to be known by or selected in the snapshot. This usage assumes that the profile's consumer is the authoritative source for obfuscation requirements. If a consumer asks for a value to be obfuscated, the profile must act.

Each plugin will define its own interpretation of "as needed". The API distinction between conditional and unconditional is a hint intended to guide implementations capable of both, and should be ignored by implementations that are not.

The buffer, offset, and length parameters define what is assumed to be a single domain datum. That is, every character in the given character range is subject to obfuscation.

The valueType parameter determines if obfuscation is required, with conditionally controlling whether an unrecognized valueType is ignored (true) or forced to obfuscate (false).

The maskStyle parameter may be used to affect the nature of obfuscation to be applied. If the parameter names a defined mask style, that obfuscation format is applied. If the parameter does not name a define mask style, the value type's default obfuscation format is applied.

Runtime Compatibility

The canMaskValue method of IDataMasker can be used to establish whether a snapshot supports this operation. A result of true indicates that the plugin is capable of performing obfuscation in response to a request. Whether a combination of input parameters exists that results in obfuscation depends on the definition of the snapshot.

The hasValueType method of IDataMaskerInspector can be used to check if a given name is selected in the snapshot. The reserved name "*" may be used to detect unconditional obfuscation support when available in the snapshot. Names defined yet unselected in the snapshot are not directly detectable, but can be detected by comparing results of using multiple context configurations.

The name "*" is reserved by the abstraction for compatibility checks.

To confirm the availability of a specific mask style first requires confirmation of the value type to which the mask style is related. Use queryValueType to obtain the defined instance and, from the result, call queryMaskStyle to confirm the existence of the mask stye. For each of these, corresponding get... methods are defined to obtain new references to the objects. The methods are declared by IDataMaskerInspector.

Implementations

The included implementations are inherently conditional. Obfuscation depends on matching valueType with a selected value type instance in the snapshot, where an instance is selected if it belongs to the currently selected value type set.

Unconditional obfuscation is enabled in profiles that include a value type instance named "*". If an instance with this name is selected by the currently selected value type set, all values for undefined value types will be obfuscated. Values for defined but unselected value types will not be obfuscated.

  • A value of type "foo" will be obfuscated when type "foo" is selected by the currently selected value type set.
  • A value of type "foo" will be obfuscated when type "foo" is undefined and type "*" is selected by the currently selected value type set.
  • A value of type "foo" will not be obfuscated when type "foo" is defined but unselected by the currently selected value type set.

Example 3a: conditional obfuscation

valueType:
+  - name: type1
+  - name: type2
+    memberOf:
+      - name: set1

This snippet declares two value types, with two total sets. The table shows which values will be conditionally obfuscated based on a the combination of valueType and the currently selected value type set.

valueTypeSelected SetObfuscated
type1N/AYes
type1set1Yes
type2N/ANo
type2set1Yes
typeNN/ANo
typeNset1No

"typeN" in the table represents any named type, excluding the reserved "*", not explicitly defined in the profile.

Example 3b: unconditional obfuscation

valueType:
+  - name: type1
+  - name: type2
+    memberOf:
+      - name: set1
+  - name: *

Extending the previous example with a third value type, values of unknown type are now obfuscated. Values of known but unselected type remain unobfuscated.

valueTypeSelected SetObfuscated
type1N/AYes
type1set1Yes
type2N/ANo
type2set1Yes
typeNN/AYes
typeNset1Yes

The name reserved by the abstraction to detect support for unconditional obfuscation is the same name reserved by the implementation to define that support.

"typeN" in the table represents any named type, excluding the reserved "*", not explicitly defined in the profile.

maskContent

bool maskContent(const char* contentType, char* buffer, size_t offset, size_t length) const;

Overview

The buffer, offset, and length parameters define what is assumed to be a blob of content that may contain zero or more occurrences of domain data. The blob is expected to contain sufficient context allowing a snapshot's rules to locate any included occurrences.

The context parameter optionally influences which rules are selected in a snapshot, while the contentType parameter offers a hint as to the blob's data format. Each snapshot rule may be assigned a format to which it applies. Operation requests may be optimized by limiting the number of selected rules applied by describing the format. Selected rules not assigned a format will be applied in all requests.

There is no equivalent to the unconditional mode offered by maskValue. Content obfuscation is always conditional on matching rules.

Runtime Compatibility

The canMaskContent method of IDataMasker can be used to establish whether a snapshot supports this operation. A result of true indicates that the plugin is capable of performing obfuscation in response to a request. Whether a combination of input parameters exists that results in obfuscation depends on the definition of the snapshot.

The hasRule method of IDataMaskerInspector indicates if at least one rule is selected in the snapshot for a given content type. Rules not associated with any content type may be interrogated using an empty string.

It is not possible to discern any information about the selected rules, such as their associated value types or the cues used to apply them in a buffer.

Implementations

The groundwork exists for two types of rules, serial and parallel. Serial rules, as the name implies, are evaluated sequentially. Parallel rules, on the other hand, are evaluated concurrently. Concurrent evaluation is expected to be more efficient than sequential, but no concurrent implementation is provided.

The included sequential implementation should be viewed as a starting point. Each rule defines a start token and an optional end token. For each occurrence of the start token in the blob, a corresponding search for an end token (newline if omitted), is performed. When both parts are found, the content between is obfuscated using the associated value type's default mask style.

Traversing a potentially large blob of text once per rule is inefficient. A concurrent implementation is in development to improve performance and capabilities. A domain originated using the original implementation and migrated to the new implementation illustrates one domain implemented by multiple plugins and, by extension, multiple profiles.

maskMarkupValue

bool maskMarkupValue(const char* element, const char* attribute, char* buffer, size_t offset, size_t length, IDataMaskingDependencyCallback& callback) const;

Overview

Where maskValue obfuscates individual values based on what the caller believes the value to be, and maskContent obfuscates any number of values it identifies based on cues found in surrounding text, maskMarkupValue obfuscastes individual values based on cues not provided to it.

Given a value and a relative location within a structure document, an implementation must decide whether obfuscation is required, may be required, or is not required. If required, it can obfuscate immediately. If not required, and can return immediately. If it might be required, the implementation must request the context it needs from the caller in order to make a final determination.

A request to mask the content of an element named Value might depend on the content of a sibling element named Name. If value of Value probably does not require obfuscation when the value of Name is city, but almost certainly does require obfuscation when the value of Name is password. When processing the request for Value, an implementation must ask for the value of a sibline named Name to decide whether or not to obfuscate the data.

There is no equivalent to the unconditional mode offered by maskValue. Markup value obfuscation presumes that the caller is traversing a structured document without awareness of which values require obfuscation. If the caller knows which values require obfuscation, it should use maskValue on those specific values.

Runtime Compatibility

    virtual bool getMarkupValueInfo(const char* element, const char* attribute, const char* buffer, size_t offset, size_t length, IDataMaskingDependencyCallback& callback, DataMaskingMarkupValueInfo& info) const = 0;

The canMaskMarkupValue method of IDataMasker can be used to establish whether a snapshot supports this operation. A result of true indicates that the plugin is capable of performing obfuscation in response to a request. Whether a combination of input parameters exists that results in obfuscation depends on the definition of the snapshot.

The getMarkupValueInfo method of IDataMaskerInspector determines if the value of an element or attribute requires obfuscation. If required, the info parameter will describe on completion how to perform the obfuscation using maskValue instead of maskMarkupValue. This is done to optimize performance by eliminating the need to reevaluate rules to re-identify a match.

Assme a value type representing passwords is defined. If given an element value containing a URL with an embedded password, obfuscation is required. But most likely not for the entire value. In addition to identifying the value type associated with value, the offset and length of the embedded substring requiring obfuscation are supplied; use of maskValue with these three values and no mask style will apply the default obfuscation only to the embedded password. Alternatively, if a mask style is supplied, use of maskValue with this style and the original value offset and length should obfuscate only the embedded password.

A callback interface is required for all calls to getMarkupValueInfo. The interface is only used when additional document context is required to make a determination about the requested value. In these cases, the checks are proximity-dependent and cannot be performed as part of load-time compatibility checks, when no document is available.

Implementations

TBD

Load-time Compatibility

bool checkCompatibility(const IPTree& requirements) const;

Overview

Use of runtime compatibility checks may be unavoidable in some circumstances, but reliance on this in every script is inefficient. It may also devalue trace output by omitting messaging required to debug an issue, a problem that might only be found when said messaging is needed.

The requirements parameter represents a standardized set of runtime compatibility checks to be applied. By default, each check must pass to establish compatibility. Explicitly optional checks may be included to detect conditions the caller is prepared to work around.

This can test which values will or won't be affected by maskValue, and which obfuscation styles are available. It can test which rule sets will impact maskContent. It specifically excludes maskMarkupValue checks that depend on the proximity of one value to another.

Returning to the earlier SSN example, a caller might require that an SSN value type will be obfuscated. It might also want to use a particilar mask style, but may be prepared for its absence. A caller may prefer to mask the first four digits but may accept masking the last five instead, or may omit certain trace messages.

Implementation

compatibility:
+  - context:
+      - domain: optional text
+        version: optional number
+        property:
+          - name: required text
+            value: required text
+    accepts:
+      - name: required text
+        presence: one of "r", "o", or "p"
+    uses:
+      - name: required text
+        presence: one of "r", "o", or "p"
+    operation:
+      - name: one of "maskValue", "maskContent", or "maskMarkupValue"
+        presence: one of "r", "o", or "p"
+    valueType
+      - name: required text
+        presence: one of "r", "o", or "p"
+        maskStyle:
+          - name: required text
+            presence: one of "r", "o", or "p"
+        Set:
+          - name: required text
+            presence: one of "r", "o", or "p"
+    rule:
+      - contentType: required text
+        presence: one of "r", "o", or "p"

The requirements parameter of checkCompatibility must be either a compatibility element or the parent of a collection of compatibility elements. Each instance may contain at most one context element, three operation elements (one per operation), and a variable number of accepts, uses, valueType, or rule elements.

The context element describes the target of an evaluation. It may include a domain identifier, or assume the default domain. It may include a version number, or assume the default snapshot of the domain. It may specify any number of custom context properties to select various profile elements in the snapshot.

The accepts and uses elements identify custom context property names either accepted or used within a snapshot. Acceptance indicates some part of a snapshot has declared an understanding of the named property. Usage indicates the some part of a snapshot will react to the named value being set. Usage implies acceptance.

To improve compatibility check capabilities, a snapshot may synthesize properties that, if set, will have no effect. Specifically, a caller may be more interested to know if a particular set name (for either value type or rule set membership) is used than to know that an unidentified set name is used.

The Operations element identifies which operations must be supported. Its purpose is to enforce availability of an operation when no more explicit requirements are given (for example, one may require maskContent without also requiring the presence of rules for any given content type). When more explicit requirements are given, the attributes of this element are redundant. If all attributes are redundant, the element may be omitted.

The valueType element describes all optional and mandatory requirements for using maskValue with a single value type name and optional mask style name. Set membership requirements can also be evaluated, which is most useful when compatibility/context/property/@name is "*".

The rule element describes all optional and mandatory requirements for using maskContent with a single content type value. Unlike evaluable value type set membership, rule set membership requirements cannot be evaluated.

In all elements described as accepting @presence, the acceptable values are

  • r: the check is required to pass
  • o: the check is optional
  • p: the check is prohibited from passing
`,170),i=[o];function l(r,p,c,d,m,u){return a(),t("div",null,i)}const b=e(s,[["render",l]]);export{f as __pageData,b as default}; diff --git a/assets/system_masking_include_readme.md.i5B-dOzb.lean.js b/assets/system_masking_include_readme.md.i5B-dOzb.lean.js new file mode 100644 index 00000000000..9a2efd8a631 --- /dev/null +++ b/assets/system_masking_include_readme.md.i5B-dOzb.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as a,V as n}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"Data Masking Framework","description":"","frontmatter":{},"headers":[],"relativePath":"system/masking/include/readme.md","filePath":"system/masking/include/readme.md","lastUpdated":1721825996000}'),s={name:"system/masking/include/readme.md"},o=n("",170),i=[o];function l(r,p,c,d,m,u){return a(),t("div",null,i)}const b=e(s,[["render",l]]);export{f as __pageData,b as default}; diff --git a/assets/system_masking_plugins_datamasker_readme.md.iLx0XZB6.js b/assets/system_masking_plugins_datamasker_readme.md.iLx0XZB6.js new file mode 100644 index 00000000000..d755723371c --- /dev/null +++ b/assets/system_masking_plugins_datamasker_readme.md.iLx0XZB6.js @@ -0,0 +1,24 @@ +import{_ as e,c as t,o as i,V as n}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"system/masking/plugins/datamasker/readme.md","filePath":"system/masking/plugins/datamasker/readme.md","lastUpdated":1721825996000}'),a={name:"system/masking/plugins/datamasker/readme.md"},o=n(`

libdatamasker.so

This implements a plugin library producing instances of IDataMaskingProfileIterator to be used by an IDataMaskingEngine instance. The library is written in C++, and this section is organized according to the namespace and class names used.

namespace DataMasking

CContext

Standard implementation of IDataMaskingProfileContext and IDataMaskingProfileContextInspector tightly coupled to a specific version of a specific profile instance. It manages custom properties that are accepted by the associated profile, and rejects attempts to set those not accepted. The rejection is one way of letting the caller know that something it might deem essential is not handled by the profile.

Depends on CProfile, an abstract implementation of IDataMaskingProfile and IDataMaskingProfileInspector.

CMaskStyle

Implementation of IDataMaskingProfileMaskStyle providing customized masked output of values. Configuration options include:

  • @maximumVersion is an optional version number, needed only when the style does not apply to the maximum version of the value type in which it is defined.
  • @minimumVersion is an optional version number, needed only when the style does not apply to the minumum version of the value type in which it is defined.
  • @name is a required unique identifier for the style.
  • @overrideDefault is an optional Boolean flag controlling whether the style should replace the default style for the value type in which it is defined.
  • @pattern is an optional character sequence (one or more characters in length) that will be used to mask value content.

CPartialMaskStyle

Am extension of CMaskStyle, this is intended to partially mask values, such as account numbers or telephone numbers, without assuming knowledge of the values. Configuration is generally expressed as:

[ [ @action ] [ @location ] @count  [ @characters ] ]
+

Where:

  • @action what to do, either keep or mask (default); and
  • @location where to do it, either first or last (default); and
  • @count how many characters to examine, a positive integer value; and
  • @characters type of characters to be considered, either numbers, letters, alphanumeric, or all (default)

All four values may be omitted when only the inherited options are needed. If any of the four values are given, count is required and the other three are optional.

A value substring containing at most @count instances of the character class denoted by @characters is identified. Character classes are ASCII numeric characters (numbers), ASCII alphabetic characters (letters), ASCII alphabetic and numeric characters (alphanumeric), or any characters (all).

The value substring is at the start of the value when @location is first, and at the end of the value when @location is last.

The value substring is masked when @action is mask. All of the value with the exception of the substring is masked when @action is keep.

CRule

A base class for rule implementations, this defines standard rule properties without providing any content knowledge for use with maskContent.

Configuration includes:

  • @contentType is an optional, user-defined, label describing the content format to which the rule should be applied. maskContent requests that indicate a content type should apply all rules that match the type in addition to all rules that omit a type.
  • memberOf is an optional and repeating element identifying user-defined set labels. Only rules with membership in the requested set are considered; in the absence of an explictly requested set, a default set with an empty name is implied.
  • memberOf/@name is a required user-defined set label.

rule instances will frequently be specific to a content format. For example, a rule applied to XML markup value may include dependencies on XML syntax which are not applicable when masking JSON content. Rules specific to a markup format can be associated with that format using @contentType. Requests to mask content of a type will apply all rules without a @contentType value or with a matching value, and will exclude all rules with a non-matching value.

CSerialTokenRule

An extension of CRule, this identifies content substrings to be masked based on matching start and end tokens in the content buffer. For each occurrence of a configured start token that is balanced by a corresponding configured end token, the characters between the tokens are masked.

This class may be used with TSerialProfile.

Configuration includes:

  • @endToken is an optional character sequence expected to immediately follow a content substring to be masked. This might be an XML element end tag, or a terminating double quote for a JSON value. A newline, i.e., \\n, is assumed if omitted or empty, for masking values such as HTTP headers.
  • @matchCase is an optional Boolean flag controlling whether token matches are case sensitive (true) or insensitivie (false). Matches are case insensitive by default. The implementation is based on ASCII data; case sensitive comparisons of similarly encoded non-ASCI text can work, but case insensitive comparisons are not supported.
  • @startToken is a required character sequence expected to precede a content substring to be masked.

No content type knowledge is implied by this class. An instance with @contentType of xml does not inherently know how to find values in XML markup. The defined tokens must include characters such as < and > to match an element name and quotes to match attribute values.

TPlugin

An implementation of IDataMaskingProfileIterator used for transforming a configuration property tree into a collection of profiles to be returned by a library entry point function. Created profiles are of the same class, identified by the template parameter profile_t.

Configuration includes:

  • profile An optional and repeating element defining a profile. The content of this element depends on profile_t.
  • If and only if profile is not included as a child of the configuration node, the node itself will be treated as a configuration for profile_t. The name of the configuration node is not required to be profile.

TProfile

A concrete extension of CProfile, this manages value types and rules, providing a default implementation of maskValue but leaving other operations to subclasses.

Template parameters are:

  • valuetype_t identifies the concrete implementation of IDataMaskingProfileValueType to be instantiated during configuration.
  • rule_t identifies the concrete implementation of rules to be managed. Creation is assumed to be the responsibility of the value type.
  • context_t identifies the concrete implementation of IDataMaskingProfileContext to be instantiated on demand.

Configuration includes:

  • @defaultVersion is an optional version number identifying the version to be used when version 0 is requested. Omission or 0 implies the maximum version (which is configured before this value).
  • @domain is the required default identifier used to select this profile.
  • legacyDomain is an optional and repeating element identifying alternate identifiers by which the profile may be selected. This enables domain identifier naming conventions to be changed without breaking pre-existing references.
  • legacyDomain/@id is a required identifier of this profile.
  • @maximumVersion is an optional version number identifying the highest version supported by the configuration. Omission or 0 implies a value matching the minimum version (whether implicitly or explicitly defined).
  • @minimumVersion is an optional version number identifying the lowest version supported by the configuration. Omission or 0 implies 1.
  • @name is an optional label for the instance. If given, the value is used in trace output.
  • property is an optional and repeating element describing a custom context property recognized by the profile. The profile can declare awareness of properties without explicit use of them.
  • property/@name is a required context property name.
  • property/@minimumVersion is an optional version number indicating the lowest profile version aware of the property. Omission or 0 implies the profile minimum.
  • property/@maximumVersion is an optional version number indicating the highest profile version aware of the property. Omisssion or 0 implies the profile maximum.
  • valueType is an optional and repeating element describing the value types defined by the profile. Element content depends on the value of valuetype_t.

For profiles supporting a single version, value type names must be unique. For profiles supporting multiple versions, value type names may be repeated but must be unique for each version. To illustrate, consider this snippet:

profile:
+  minimumVersion: 1
+  maximumVersion: 2
+  valueType:
+    - name: foo
+      maximumVersion: 1
+    - name: foo
+      minimumVersion: 2
+    - name: bar
+    - name: bar
+      minimumVersion: 2
+

In the example, foo and bar are each defined twice. The redefinition of foo is acceptable because each instance applies to a different version. The redefinition of bar is invalid because both instances claim to apply to version 2.

The value type name * is reserved by this class. A profile that includes a value type named * supports unconditional value masking. maskValue requests specifying unknown value type names can fall back to this special type and force masking for these values. Without this type, maskValue masks what it thinks should be masked; with this type, it trusts the caller to request masking for only those values known to require it. Value types included in unselected value type sets are known to the profile and, as such, can be used to prevent specific typed values from ever being masked.

The requirement of a type definition instead of a simpler flag is to enable the definition of mask styles. It has the side effect of enabling the definition of rules. In theory, an entire profile could be defined using a single value type. This may make sense in some cases, but not in all. For example, a partial mask style intended for use with U.S. Social Security numbers could be inappropriately applied to a password. Use care when configuring this type.

TSerialProfile

An extension of TProfile that adds support for maskContent. It is assumed by maskContent that each applicable rule must be applied serially, i.e., one after the other, using an bool applyRule(buffer, length, context) interface.

Template parameters are unchanged from TProfile.

Configuration options are unchanged from TProfile.

TValueType

An implementation of IDataMaskingProfileValueType that manages mask styles and creates rules for the profile.

Template parameters are:

  • maskstyle_t identifies the concrete implementation of IDataMaskingProfileMaskStyle to be instantiated during configuration.
  • rule_t identifies the concrete implementation of rules to be created during configuration.

Configuration includes:

  • @maximumVersion is an optional version number, needed only when the type does not apply to the maximum version of the profile in which it is defined.
  • @minimumVersion is an optional version number, needed only when the type does not apply to the minumum version of the profile in which it is defined.
  • maskStyle is an optional and repeating element describing mask styles defined by the type. Element content depends on the value of maskstyle_t.
  • memberOf is an optional and repeating element identifying user-defined set labels. All value types that are not explicitly assigned set membership are considered for all masking requests. Value types assigned set membership are considered only when one of their assigned sets is selected with the request context.
  • memberOf/@name is a required user-defined set label.
  • @name is a required unique identifier for the type.
  • rule is an optional and repeating element describing rules defined by the type. Element content depends on the value of rule_t.

For value types supporting a single version, mask style names must be unique. For types supporting multiple versions, style names may be repeated but must be unique for each version. To illustrate, consider this snippet:

valueType:
+  minimumVersion: 1
+  maximumVersion: 2
+  maskStyle:
+    - name: foo
+      maximumVersion: 1
+    - name: foo
+      minimumVersion: 2
+    - name: bar
+    - name: bar
+      minimumVersion: 2
+

In the example, foo and bar are each defined twice. The redefinition of foo is acceptable because each instance applies to a different version. The redefinition of bar is invalid because both instances claim to apply to version 2.

Entry Point Functions

This section describes the entry point functions exported by the shared library. The library must export one function, and may export multiple functions.

The description of each entry point will identify which of the previously described classes is used to represent the returned collection of profiles. If a templated class is identified, the template parameters are also listed. Refer to the class descriptions for additional information.

newPartialMaskSerialToken

Returns a (possibly empty) collection of profiles supporting maskValue and maskContent operations.

  • The profile collection is an instance of TPlugin.
  • Collection profiles are implemented using TProfile.
  • Profile value types are implemented using TValueType.
  • Value type mask styles are implemented using CPartialMaskStyle.
  • Value type and profile rules are implemented using CSerialTokenRule.
  • Profile contexts are implemented using CContext.
`,63),s=[o];function r(l,m,d,p,c,u){return i(),t("div",null,s)}const g=e(a,[["render",r]]);export{f as __pageData,g as default}; diff --git a/assets/system_masking_plugins_datamasker_readme.md.iLx0XZB6.lean.js b/assets/system_masking_plugins_datamasker_readme.md.iLx0XZB6.lean.js new file mode 100644 index 00000000000..99a759e1d89 --- /dev/null +++ b/assets/system_masking_plugins_datamasker_readme.md.iLx0XZB6.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as i,V as n}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"system/masking/plugins/datamasker/readme.md","filePath":"system/masking/plugins/datamasker/readme.md","lastUpdated":1721825996000}'),a={name:"system/masking/plugins/datamasker/readme.md"},o=n("",63),s=[o];function r(l,m,d,p,c,u){return i(),t("div",null,s)}const g=e(a,[["render",r]]);export{f as __pageData,g as default}; diff --git a/assets/system_security_plugins_jwtSecurity_README.md.1wwh5vak.js b/assets/system_security_plugins_jwtSecurity_README.md.1wwh5vak.js new file mode 100644 index 00000000000..95e00a21e71 --- /dev/null +++ b/assets/system_security_plugins_jwtSecurity_README.md.1wwh5vak.js @@ -0,0 +1,8 @@ +import{_ as e,c as t,o,V as i}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"system/security/plugins/jwtSecurity/README.md","filePath":"system/security/plugins/jwtSecurity/README.md","lastUpdated":1721825996000}'),s={name:"system/security/plugins/jwtSecurity/README.md"},a=i(`

JWT Authorization Security Manager Plugin

The purpose of this plugin is to provide authentication and authorization capabilities for HPCC Systems users, with the credentials passed via valid JWT tokens.

The intention is to adhere as closely as possibly to the OpenID Connect (OIC) specification, which is a simple identity layer on top of the OAuth 2.0 protocol, while maintaining compatibility with the way HPCC Systems performs authentication and authorization today. More information about the OpenID Connect specification can be found at https://openid.net/specs/openid-connect-core-1_0.html.

One of the big advantages of OAuth 2.0 and OIC is that the service (in this case, HPCC Systems) never interacts with the user directly. Instead, authentication is performed by a trusted third party and the (successful) results are passed to the service in the form of a verifiable encoded token.

Unfortunately, HPCC Systems does not support the concept of third-party verification. It assumes that users -- really, any client application that operates as a user, including things like IDEs -- will submit username/password credentials for authentication. Until that is changed, HPCC Systems won't be able to fully adhere to the OIC specification.

We can, however, implement most of the specification. That is what this plugin does.

NOTE: This plugin is not available in a Windows build.

Code Documentation

Doxygen (https://www.doxygen.nl/index.html) can be used to create nice HTML documentation for the code. Call/caller graphs are also generated for functions if you have dot (https://www.graphviz.org/download/) installed and available on your path.

Assuming doxygen is on your path, you can build the documentation via:

cd system/security/plugins/jwtSecurity
+doxygen Doxyfile
+

The documentation can then be accessed via docs/html/index.html.

Theory of Operations

The plugin is called by the HPCC Systems esp process when a user needs to be authenticated. That call will contain the user's username and either a reference to a session token or a password. The session token is present only for already-authenticated users.

If the session token is not present, the plugin will call a JWT login service (also known as a JWT login endpoint) with the username and password, plus a nonce value for additional security.

That service authenticates the username/password credentials. If everything is good, the service constructs an OIC-compatible token that includes authorization information for that user and returns it to the plugin. The token is validated according to the OIC specification, including signature verification.

Note that token signature verification requires an additional piece of information. Tokens can be signed with a hash-based algorithm or with a public key-based algorithm (the actual algorithm used is determined by the JWT service). To verify either kind of algorithm, the plugin will need either the secret hash key or the public key that matches what the JWT service used. That key is read by the plugin from a file, and the file is determined by a configuration setting (see below). It is possible to change the contents of that file without restarting the esp process. Note, though, that the plugin may not notice that the file's contents have changed for several seconds (changes do not immediately take effect).

HPCC Systems uses a well-defined authorization scheme, originally designed around an LDAP implementation. That scheme is represented within the token as JWT claims. This plugin will unpack those claims and map to the authorization checks already in place within the HPCC Systems platform.

OIC includes the concept of refresh tokens. Refresh tokens enable a service to re-authorize an existing token without user intervention. Re-authorization typically happens due to a token expiring. Tokens should have a relatively short lifetime -- e.g. 15-30 minutes -- to promote good security and also give administrators the ability to modify a user's authorization while the user is logged in. This plugin fully supports refresh tokens by validating token lifetime at every authorization check and calling a JWT refresh service (also known as a JWT refresh endpoint) when needed. This largely follows the OIC specification.

Deviations From OIC Specification

  • Initial authentication: As stated above, the esp process will gather the username/password credentials instead of a third party, then send those credentials off to another service. In a true OIC configuration, the client process (the esp process) never sees user credentials and relies on an external service to gather them from the user.
  • The request made to the JWT login service is a POST HTTP or HTTPS call (depending on your configuration) containing four items in JSON format; example:
	{
+		"username": "my_username",
+		"password": "my_password",
+		"client_id": "https://myhpcccluster.com",
+		"nonce": "hf674DTRMd4Z1s"
+	}

Implications of Deviations

The most obvious outcome of this implementation is that a custom service/endpoint needs to be available. Or rather two services: One to handle the initial user login and one to handle token refreshes. Neither service precisely handles requests and replies in an OIC-compatible way, but the tokens themselves are OIC-compatible, which is good. That allows you to use third-party JWT libraries to construct and validate those tokens.

HPCC Systems Configuration Notes

Several items must be defined in the platform's configuration. Within configmgr, the jwtsecmgr Security Manager plugin must be added as a component and then modified according to your environment:

  • The URL or unique name of this HPCC Systems cluster, used as the client_id in token requests
  • Full URL to the JWT Login Endpoint (should be HTTPS, but not required)
  • Full URL to the JWT Refresh Endpoint (should be HTTPS, but not required)
  • Boolean indicating whether to accept self-signed certificates for those endpoints; defaults to false
  • Secrets vault key/name or subdirectory under /opt/HPCCSystems/secrets/esp in which the JWT key used for the chosen signature algorithm is stored; defaults to "jwt-security"
  • Default permission access level (either "Full" or "None"); defaults to "Full"
  • Default workunit scope access level (either "Full" or "None"); defaults to "Full"
  • Default file scope access level (either "Full" or "None"); defaults to "Full"

Only the first three items have no default values and must be supplied.

Once the jwtseccmgr component is added, you have to tell other parts of the system to use the plugin. For user authentication and permissions affecting features and workunit scopes, you need to add the plugin to the esp component. Instructions for doing so can be found in the HPCC Systems Administrator's Guide manual (though the manual uses the htpasswd plugin as an example, the process is the same).

If you intend to implement file scope permissions then you will also need provide Dali information about the JWT plugin. In configmgr, within the Dali Server component, select the LDAP tab. Change the authMethod entry to secmgrPlugin and enter "jwtsecmgr" as the authPluginType. Make sure checkScopeScans is set to true.

HPCC Systems Authorization and JWT Claims

This plugin supports all authorizations documented in the HPCC Systems® Administrator's Guide with the exception of "View Permissions". Loosely speaking, the permissions are divided into three groups: Feature, Workunit Scope, and File Scope.

Feature permissions are supported exactly as documented. A specific permission would exist as a JWT claim, by name, with the associated value being the name of the permission. For example, to grant read-only access to ECL Watch, use this claim:

	{ "SmcAccess": "Read" }

File and workunit scope permissions are handled the same way, but different from feature permissions. The claim is one of the Claim constants in the tables below, and the associated value is a matching pattern. A pattern can be simple string or it can use wildcards (specifically, Linux's file globbing wildcards). Wildcards are not typically needed.

Multiple patterns can be set for each claim.

Workunit Scope Permissions

MeaningClaimValue
User has view rights to workunit scopeAllowWorkunitScopeViewpattern
User has modify rights to workunit scopeAllowWorkunitScopeModifypattern
User has delete rights to workunit scopeAllowWorkunitScopeDeletepattern
User does not have view rights to workunit scopeDenyWorkunitScopeViewpattern
User does not have modify rights to workunit scopeDenyWorkunitScopeModifypattern
User does not have delete rights to workunit scopeDenyWorkunitScopeDeletepattern

File Scope Permissions

MeaningClaimValue
User has view rights to file scopeAllowFileScopeViewpattern
User has modify rights to file scopeAllowFileScopeModifypattern
User has delete rights to file scopeAllowFileScopeDeletepattern
User does not have view rights to file scopeDenyFileScopeViewpattern
User does not have modify rights to file scopeDenyFileScopeModifypattern
User does not have delete rights to file scopeDenyFileScopeDeletepattern
`,41),n=[a];function r(l,c,d,h,p,u){return o(),t("div",null,n)}const g=e(s,[["render",r]]);export{f as __pageData,g as default}; diff --git a/assets/system_security_plugins_jwtSecurity_README.md.1wwh5vak.lean.js b/assets/system_security_plugins_jwtSecurity_README.md.1wwh5vak.lean.js new file mode 100644 index 00000000000..7fd67d327ec --- /dev/null +++ b/assets/system_security_plugins_jwtSecurity_README.md.1wwh5vak.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o,V as i}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"system/security/plugins/jwtSecurity/README.md","filePath":"system/security/plugins/jwtSecurity/README.md","lastUpdated":1721825996000}'),s={name:"system/security/plugins/jwtSecurity/README.md"},a=i("",41),n=[a];function r(l,c,d,h,p,u){return o(),t("div",null,n)}const g=e(s,[["render",r]]);export{f as __pageData,g as default}; diff --git a/assets/testing_regress_cleanupReadme.md.WHcEY6YL.js b/assets/testing_regress_cleanupReadme.md.WHcEY6YL.js new file mode 100644 index 00000000000..293804c7277 --- /dev/null +++ b/assets/testing_regress_cleanupReadme.md.WHcEY6YL.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as a,V as r}from"./chunks/framework.gBlNPWt_.js";const W=JSON.parse('{"title":"Cleanup Parameter of Regression Suite run and query sub-command","description":"","frontmatter":{},"headers":[],"relativePath":"testing/regress/cleanupReadme.md","filePath":"testing/regress/cleanupReadme.md","lastUpdated":1721825996000}'),s={name:"testing/regress/cleanupReadme.md"},o=r('

Cleanup Parameter of Regression Suite run and query sub-command

The cleanup parameter has been introduced to allow the user to automatically delete the workunits created by executing the Regression Suite on their local system.

It is an optional argument of the run and query sub-command.

A custom logging system also creates log files for each execution of the run and query sub-command that contains information about the workunit deletion.

Command:

./ecl-test run --cleanup [mode]

./ecl-test query --cleanup [mode]

Modes allowed are ‘workunits’, ‘passed’. Default is ‘none’.

  • workunits - all passed and failed workunits are deleted.

  • passed - only the passed workunits of the queries executed are deleted.

  • none - no workunits created during the current run command are deleted.

Result:

The sample terminal output for hthor target:

./ecl-test query ECL_query action1.ecl action2.ecl action4.ecl action5.ecl -t hthor --cleanup workunits

[Action] Suite: hthor
[Action] Queries: 4
[Action] 1. Test: action1.ecl
[Pass] 1. Pass action1.ecl - W20240526-094322 (2 sec)
[Pass] 1. URL http://127.0.0.1:8010/?Widget=WUDetailsWidget&Wuid=W20240526-094322
[Action] 2. Test: action2.ecl
[Pass] 2. Pass action2.ecl - W20240526-094324 (2 sec)
[Pass] 2. URL http://127.0.0.1:8010/?Widget=WUDetailsWidget&Wuid=W20240526-094324
[Action] 3. Test: action4.ecl
[Pass] 3. Pass action4.ecl - W20240526-094325 (2 sec)
[Pass] 3. URL http://127.0.0.1:8010/?Widget=WUDetailsWidget&Wuid=W20240526-094325
[Action] 4. Test: action5.ecl
[Pass] 4. Pass action5.ecl - W20240526-094327 (2 sec)
[Pass] 4. URL http://127.0.0.1:8010/?Widget=WUDetailsWidget&Wuid=W20240526-094327
[Action]
-------------------------------------------------
Result:
Passing: 4
Failure: 0
-------------------------------------------------
Log: /root/HPCCSystems-regression/log/hthor.24-05-26-09-43-22.log
-------------------------------------------------
Elapsed time: 11 sec (00:00:11)
-------------------------------------------------

[Action] Automatic Cleanup Routine

[Pass] 1. Workunit Wuid=W20240526-094322 deleted successfully.
[Pass] 2. Workunit Wuid=W20240526-094324 deleted successfully.
[Failure] 3. Failed to delete Wuid=W20240526-094325. URL: http://127.0.0.1:8010/?Widget=WUDetailsWidget&Wuid=W20240526-094325
Failed cannot open workunit Wuid=W20240526-094325.. Response status code: 200
[Pass] 4. Workunit Wuid=W20240526-094327 deleted successfully.
Suite destructor.

',15),i=[o];function n(l,u,d,c,p,m){return a(),t("div",null,i)}const b=e(s,[["render",n]]);export{W as __pageData,b as default}; diff --git a/assets/testing_regress_cleanupReadme.md.WHcEY6YL.lean.js b/assets/testing_regress_cleanupReadme.md.WHcEY6YL.lean.js new file mode 100644 index 00000000000..97284a1ec75 --- /dev/null +++ b/assets/testing_regress_cleanupReadme.md.WHcEY6YL.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as a,V as r}from"./chunks/framework.gBlNPWt_.js";const W=JSON.parse('{"title":"Cleanup Parameter of Regression Suite run and query sub-command","description":"","frontmatter":{},"headers":[],"relativePath":"testing/regress/cleanupReadme.md","filePath":"testing/regress/cleanupReadme.md","lastUpdated":1721825996000}'),s={name:"testing/regress/cleanupReadme.md"},o=r("",15),i=[o];function n(l,u,d,c,p,m){return a(),t("div",null,i)}const b=e(s,[["render",n]]);export{W as __pageData,b as default}; diff --git a/assets/testing_regress_ecl_analyzers_corporate_tmp_README.md.QgDQ6IeH.js b/assets/testing_regress_ecl_analyzers_corporate_tmp_README.md.QgDQ6IeH.js new file mode 100644 index 00000000000..e2d00fd44cd --- /dev/null +++ b/assets/testing_regress_ecl_analyzers_corporate_tmp_README.md.QgDQ6IeH.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as s,m as r}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"testing/regress/ecl/analyzers/corporate/tmp/README.md","filePath":"testing/regress/ecl/analyzers/corporate/tmp/README.md","lastUpdated":1721825996000}'),a={name:"testing/regress/ecl/analyzers/corporate/tmp/README.md"},o=r("p",null,"This directory is for the function kbdumptree. The engine should create this but sometimes permission problems arrise.",-1),n=[o];function c(i,p,l,d,m,_){return s(),t("div",null,n)}const u=e(a,[["render",c]]);export{f as __pageData,u as default}; diff --git a/assets/testing_regress_ecl_analyzers_corporate_tmp_README.md.QgDQ6IeH.lean.js b/assets/testing_regress_ecl_analyzers_corporate_tmp_README.md.QgDQ6IeH.lean.js new file mode 100644 index 00000000000..e2d00fd44cd --- /dev/null +++ b/assets/testing_regress_ecl_analyzers_corporate_tmp_README.md.QgDQ6IeH.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as s,m as r}from"./chunks/framework.gBlNPWt_.js";const f=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"testing/regress/ecl/analyzers/corporate/tmp/README.md","filePath":"testing/regress/ecl/analyzers/corporate/tmp/README.md","lastUpdated":1721825996000}'),a={name:"testing/regress/ecl/analyzers/corporate/tmp/README.md"},o=r("p",null,"This directory is for the function kbdumptree. The engine should create this but sometimes permission problems arrise.",-1),n=[o];function c(i,p,l,d,m,_){return s(),t("div",null,n)}const u=e(a,[["render",c]]);export{f as __pageData,u as default}; diff --git a/assets/tools_esdlcmd_README.md.kugmSdg_.js b/assets/tools_esdlcmd_README.md.kugmSdg_.js new file mode 100644 index 00000000000..7a6abbe4e4e --- /dev/null +++ b/assets/tools_esdlcmd_README.md.kugmSdg_.js @@ -0,0 +1,307 @@ +import{_ as s,c as i,o as t,V as a}from"./chunks/framework.gBlNPWt_.js";const c=JSON.parse('{"title":"esdl Utility","description":"","frontmatter":{},"headers":[],"relativePath":"tools/esdlcmd/README.md","filePath":"tools/esdlcmd/README.md","lastUpdated":1721825996000}'),n={name:"tools/esdlcmd/README.md"},e=a(`

esdl Utility

The esdl utility tool aids with creating and managing ESDL-based and Dynamic ESDL services on an HPCC cluster. It consists of several different commands.

To generate output from an ESDL definition:

xml               Generate XML from ESDL definition.
+ecl               Generate ECL from ESDL definition.
+xsd               Generate XSD from ESDL definition.
+wsdl              Generate WSDL from ESDL definition.
+java              Generate Java code from ESDL definition.
+cpp               Generate C++ code from ESDL definition.
+monitor           Generate ECL code for result monitoring / differencing
+monitor-template  Generate a template for use with 'monitor' command
+

To manage ESDL and DESDL services:

publish               Publish ESDL Definition for ESP use.
+list-definitions      List all ESDL definitions.
+get-definition        Get ESDL definition.
+delete                Delete ESDL Definition.
+bind-service          Configure ESDL based service on target ESP (with existing ESP Binding).
+list-bindings         List all ESDL bindings.
+unbind-service        Remove ESDL based service binding on target ESP.
+bind-method           Configure method associated with existing ESDL binding.
+unbind-method         Remove method associated with existing ESDL binding.
+get-binding           Get ESDL binding.
+manifest              Build a service binding or bundle from a manifest file.
+bind-log-transform    Configure log transform associated with existing ESDL binding.
+unbind-log-transform  Remove log transform associated with existing ESDL binding.
+

The sections below cover the commands in more detail.

manifest

The manifest command creates an XML configuration file for an ESDL ESP from an input XML manifest file. The type of configuration output depends on the manifest file input and on command-line options.

Manifest File

A manifest file is an XML-formatted template combining elements in and outside of the manifest's urn:hpcc:esdl:manifest namespace. Recognized elements of this namespace control the tool while all other markup is copied to the output. The goal of using a manifest file with the tool is to make configuring and deploying services easier:

  1. The manifest file format abstracts some of the complexity of the actual configuration.
  2. By allowing you to include external files like ESDL Scripts and XSLTs into the ouput, you can store and maintain them separately in your repo.

The result of running the manifest tool on a manifest file is an XML artifact suitable for use with the ESDL ESP. Supported output includes:

  • binding: The output is an ESDL binding that may be published to dali.
  • bundle: The output is an ESDL bundle file that may be used to launch an ESP in application mode.

Example

A simplified example showing the format of a bundle manifest file:

xml
<em:Manifest xmlns:em="urn:hpcc:esdl:manifest">
+    <em:ServiceBinding esdlservice="WsFoobar" id="WsFoobar_desdl_binding" auth_feature="DEFERRED">
+        <Methods>
+            <em:Scripts>
+                <em:Include file="WsFoobar-request-prep.xml"/>
+                <em:Include file="WsFoobar-logging-prep.xml"/>
+            </em:Scripts>
+            <Method name="FoobarSearch" url="127.0.0.1:8888">
+                <em:Scripts>
+                    <em:Include file="FoobarSearch-scripts.xml"/>
+                </em:Scripts>
+            </Method>
+        </Methods>
+        <LoggingManager>
+            <LogAgent transformSource="local" name="main-logging">
+                <LogDataXPath>
+                    <LogInfo name="PreparedData" xsl="log-prep">
+                </LogDataXPath>
+                <XSL>
+                    <em:Transform name="log-prep">
+                        <em:Include file="log-prep.xslt">
+                    </em:Transform>
+                </XSL>
+            </LogAgent>
+        </LoggingManager>
+    </em:ServiceBinding>
+    <em:EsdlDefinition>
+        <em:Include file="WsFoobar.ecm"/>
+    </em:EsdlDefinition>
+</em:Manifest>

The tool is permissive and flexible, copying through most markup to the output. Recognized elements in the manifest namespace may be treated differently. They are only required in order to take advantage of the automated processing and simplified format of the manifest file. This example highlights the recommended usage of manifest elements to use the tool's capabilities. Although you could replace some of the elements below with verbatim bundle or binding output elements we won't cover that usage here.

  • <em:Manifest> is the required root element. By default the tool outputs a bundle, though you may explicitly override that on the command line or by providing an @outputType='binding' attribute.
  • <em:ServiceBinding> is valid for both bundle and binding output. It is necessary to enable recognition of <em:Scripts>, and <em:Transform> elements.
  • <em:EsdlDefinition> is relevant only for bundle output. It is necessary to enable element order preservation and recognition of <em:Include> as a descendant element.
  • <em:Include> causes external file contents to be inserted in place of the element. The processing of included files is context dependent; the parent of the <em:Include> element dictates how the file is handled. File inclusion facilitates code reuse in a configuration as code development environment.
  • <em:Scripts> and <em:Transform> trigger preservation of element order for all descendent elements and enable <em:Include> recognition.

XML element ordering is significant to proper ESDL script, XSLT, and ESXDL content processing. The platform's IPropertyTree implementation, used when loading an artifact, does not preserve order. Output configuration files must embed order-sensitive content as text as opposed to XML markup. The tool allows configuration authors to create and maintain files as XML markup, which is easy to read. It then automates the conversion of the XML markup into the embedded text required by an ESP.

Syntax

These elements may create artifact content, change the tool's behavior, or both. When used as intended, none of these elements will appear in the generated output:

Manifest

Required root element of all manifest files that create output:

  • bundle output is created by default. It can be made explicit by setting either/or:
    • command line option --output-type to bundle
    • manifest property @outputType to bundle.
  • binding output is created when either/or:
    • command line option --output-type is binding
    • manifest property @outputType is binding.
AttributeRequired?ValueDescription
@outputTypeNstringA hint informing the tool which type of output to generate. The command line option --output-type may supersede this value to produce a different output.
A bundle manifest is a superset of a binding manifest and may logically be used to create either output type. A binding manifest, as a subset of a bundle, cannot be used to create a valid bundle.
@xmlns[:prefix]YstringThe manifest namespace urn:hpcc:esdl:manifest must be declared. The default namespace prefix should not be used unless all other markup is fully qualified.

ServiceBinding

Recommended child of Manifest that creates ESDL binding content and applies ESDL binding-specific logic to descendent content:

  • A Binding output element is always created containing attributes of Manifest as required. See the sections below for details of the attributes referenced by the tool and how they're output.
  • A child of Binding named Definition is created with attributes @id and @esdlservice.
  • Recognition of <em:Scripts> elements is enabled.
  • Recognition of <em:Transform> elements is enabled.

While possible to omit the <em:ServiceBinding> element and instead embed a complete Binding tree in the manifest, it is discouraged because you lose the benefit of the processing described above.

There are three categories of attributes that can be defined on the <em:ServiceBinding> element:

  1. Standard attributes needed for setup
  2. Service-specific or binding-specific attributes
  3. Auxillary attributes recommended for read-only reference
Standard Attributes

First are the standard attributes used to setup and define the binding:

AttributeRequired?ValueUsage
@esdlserviceYesstring- Name of the ESDL service to which the binding is bound. Output on the Binding/Definition element.
- Also used to generate a value for Definition/@id for bundle type output.

Note that Definition/@id is not output by the tool for binding output as it would need to match the ESDLDefinitionId passed on command line esdl bind-service call, which may differ between environments.

Service-Specific Attributes

Second are binding-specific or service-specific attributes. This is an open-ended category where most future attributes will belong. Any attribute not in the other two categories is included here and output on the Binding/Definition element:

AttributeRequired?ValueUsage
@auth_featureNostringUsed declare authorization settings if they aren't present in the ESDL Definition, or override them if they are. Additional documentation on this attribute is forthcoming.
@returnSchemaLocationOnOKNoBooleanWhen true, a successful SOAP response (non SOAP-Fault) will include the schema location property. False by default.
@namespaceNostringString specifying the namespace for all methods in the binding. May contain variables that are replaced with their values by the ESP during runtime:
- \${service} : lowercase service name
- \${esdl-service} : service name, possibly mixed case
- \${method} : lowercase method name
- \${esdl-method} : method name, possibly mixed case
- \${optionals} : comma-delimited list of all optional URL parameters included in the method request, enclosed in parentheses
- \${version} : client version number
Auxillary Attributes

Finally are auxillary attributes. These should be thought of as read-only or for reference, and it is not recommended that you set these in the manifest. They are set by the system when publishing to dali using esdl bind-service, and they are present on binding configurations retrieved from the dali. If you set these attributes in the manifest, the values will be overwritten when running esdl bind-service. Alternately, if you run as an esdl application, these values aren't set by default and don't have a material effect on the binding, but they may appear in the trace log.

AttributeRequired?ValueUsage
@createdNostringTimestamp of binding creation
@espbindingNostringSet to match the id. Otherwise the value is unset and unused.
@espprocessNostringName of the ESP process this binding is running on
@idNostringRuntime name of the binding. When publishing to dali the value is [ESP Process].[port].[ESDL Service]. When not present in the manifest a default value is generated of the form [@esdlservice]_desdl_binding
@portNostringPort on the ESP Process listening for connections to the binding.
@publishedByNostringUserid of the person publishing the binding

EsdlDefinition

Recommended child of <em:Manifest> that enables ESDL definition-specific logic in the tool:

  • A Definitions element is output. All content is enclosed in a CDATA section.
  • The manifest <em:Include> element is recognized to import ESDL definitions:
    • An included .ecm file is transformed into XML.
    • An included .xml file is imported as-is.
AttributeRequired?ValueUsage
N/A

The element is not required since it is possible to embed a complete Definitions element hierarchy in the manifest.

Include

Optional element that imports the contents of another file into the output in place of itself. The outcome of the import depends on the context in which this element is used. See EsdlDefinition, Scripts, and Transform for more information.

AttributeRequired?ValueUsage
@fileYesfile pathFull or partial path to an external file to be imported. If a partial path is outside of the tool's working directory, the tool's command line must specify the appropriate root directory using either -I or --include-path.

Any XSLTs or ESDL Scripts written inline in a manifest file will have XML escaping applied where required to generate valid XML. If an XSLT contains any text content or markup that needs to be preserved as-is (no XML escaping applied) then be sure to use an <em:Include> operation. Included files are inserted into the output as-is, with the exception of encoding nested CDATA markup. If the included file will be inside a CDATA section on output, then any CDATA end markup in the file will be encoded as ]]]]><![CDATA[> to prevent nested CDATA sections or a prematurely ending a CDATA section.

For details on using XSLT to generate unescaped output, see this section of the specification: https://www.w3.org/TR/1999/REC-xslt-19991116#disable-output-escaping

Scripts

Optional repeatable element appearing within an <em:ServiceBinding> element that processes child elements and creates output expected for an ESDL binding:

  • The <em:Scripts> element is replaced on output with <Scripts>. Then all content is enclosed in a CDATA section after wrapping it with a new Scripts element. That new <Scripts> element contains namespaces declared by the input <em:Scripts> element. The input <em:Scripts foo="..." xmlns:bar="..."><!-- content --></em:Scripts> becomes <Scripts><![CDATA[<Scripts xmlns:bar="..."><!-- content --></Scripts>]]></Scripts>.
  • The manifest <em:Include> element is recognized to import scripts from external files. The entire file, minus leading and trailing whitespace, is imported. Refrain from including files that contain an XML declaration.

Transform

Optional repeatable element appearing within an <em:ServiceBinding> element that processes child elements and creates output expected for an ESDL binding:

  • All content is enclosed in a CDATA section. The input <em:Transform> <!-- content --> </em:Transform> becomes <Transform><![CDATA[<!-- content -->]]></Transform>.
  • The manifest <em:Include> element is recognized to import transforms from external files. The entire file, minus leading and trailing whitespace, is imported. Refrain from including files that contain an XML declaration.

Usage

Usage:
+
+esdl manifest <manifest-file> [options]
+
+Options:
+    -I | --include-path <path>
+                        Search path for external files included in the manifest.
+                        Use once for each path.
+    --outfile <filename>
+                        Path and name of the output file
+    --output-type <type>
+                        When specified this option overrides the value supplied
+                        in the manifest attribute Manifest/@outputType.
+                        Allowed values are 'binding' or 'bundle'.
+                        When not specified in either location the default is
+                        'bundle'
+    --help              Display usage information for the given command
+    -v,--verbose        Output additional tracing information
+    -tcat,--trace-category <flags>
+                        Control which debug messages are output; a case-insensitive
+                        comma-delimited combination of:
+                            dev: all output for the developer audience
+                            admin: all output for the operator audience
+                            user: all output for the user audience
+                            err: all error output
+                            warn: all warning output
+                            prog: all progress output
+                            info: all info output
+                        Errors and warnings are enabled by default if not verbose,
+                        and all are enabled when verbose. Use an empty <flags> value
+                        to disable all.
+

Output

The esdl manifest command reads the manifest, processes statements in the urn:hpcc:esdl:manifest namespace and generates an output XML file formatted to the requirements of the ESDL ESP. This includes wrapping included content in CDATA sections to ensure element order is maintained and replacing urn:hpcc:esdl:manifest elements as required.

An example output of each type -bundle and binding- is shown below. The examples use the sample manifest above as input plus these included files:

WsFoobar-request-prep.xml

xml
<es:BackendRequest name="request-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+    <es:set-value target="RequestValue" value="'foobar'"/>
+</es:BackendRequest>

WsFoobar-logging-prep.xml

xml
<es:PreLogging name="logging-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+    <es:set-value target="LogValue" value="23"/>
+</es:PreLogging>

FoobarSearch-scripts.xml

xml
<Scripts>
+    <es:BackendRequest name="search-request-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+        <es:if test="RequestOption>1">
+            <es:set-value target="HiddenOption" value="true()"/>
+        </es:if>
+    </es:BackendRequest>
+
+    <es:PreLogging name="search-logging-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+        <es:if test="RequestOption=1">
+            <es:set-value target="ProductPrice" value="10"/>
+        </es:if>
+    </es:PreLogging>
+</Scripts>

log-prep.xslt

xml
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+    <xsl:output method="xml" omit-xml-declaration="yes"/>
+    <xsl:variable name="logContent" select="/UpdateLogRequest/LogContent"/>
+    <xsl:variable name="transactionId" select="$logContent/UserContext/Context/Row/Common/TransactionId"/>
+    <xsl:template match="/">
+        <Result>
+        <Dataset name='special-data'>
+            <Row>
+            <Records>
+                <Rec>
+                <transaction_id><xsl:value-of select="$transactionId"/></transaction_id>
+                <request_data>
+                    <xsl:text disable-output-escaping="yes">&amp;lt;![CDATA[COMPRESS('</xsl:text>
+                    <xsl:copy-of select="$logContent/UserContent/Context"/>
+                    <xsl:text disable-output-escaping="yes">')]]&amp;gt;</xsl:text>
+                </request_data>
+                <request_format>SPECIAL</request_format>
+                <type>23</type>
+                </Rec>
+            </Records>
+            </Row>
+        </Dataset>
+        </Result>
+    </xsl:template>
+</xsl:stylesheet>

WsFoobar.ecm

ESPrequest FoobarSearchRequest
+{
+    int RequestOption;
+    string RequestName;
+    [optional("hidden")] bool HiddenOption;
+};
+
+ESPresponse FoobarSearchResponse
+{
+    int FoundCount;
+    string FoundAddress;
+};
+
+ESPservice [
+    auth_feature("DEFERRED"),
+    version("1"),
+    default_client_version("1"),
+] WsFoobar
+{
+    ESPmethod FoobarSearch(FoobarSearchRequest, FoobarSearchResponse);
+};
+

Bundle

The bundle is suitable to configure a service on an ESP launched in esdl application mode.

xml
  <EsdlBundle>
+    <Binding id="WsFoobar_desdl_binding">
+      <Definition esdlservice="WsFoobar" id="WsFoobar.1">
+        <Methods>
+          <Scripts>
+            <![CDATA[
+              <Scripts>
+                <es:BackendRequest name="request-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+                    <es:set-value target="RequestValue" value="'foobar'"/>
+                </es:BackendRequest>
+                <es:PreLogging name="logging-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+                    <es:set-value target="LogValue" value="23"/>
+                </es:PreLogging>
+              </Scripts>
+            ]]>
+          </Scripts>
+          <Method name="FoobarSearch" url="127.0.0.1:8888">
+            <Scripts>
+              <![CDATA[
+                <Scripts>
+                  <Scripts>
+                      <es:BackendRequest name="search-request-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+                          <es:if test="RequestOption>1">
+                              <es:set-value target="HiddenOption" value="true()"/>
+                          </es:if>
+                      </es:BackendRequest>
+
+                      <es:PreLogging name="search-logging-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+                          <es:if test="RequestOption=1">
+                              <es:set-value target="ProductPrice" value="10"/>
+                          </es:if>
+                      </es:PreLogging>
+                  </Scripts>
+                </Scripts>
+              ]]>
+            </Scripts>
+          </Method>
+        </Methods>
+        <LoggingManager>
+          <LogAgent transformSource="local" name="main-logging">
+            <LogDataXPath>
+              <LogInfo name="PreparedData" xsl="log-prep"/>
+            </LogDataXPath>
+            <XSL>
+              <Transform name="log-prep">
+                <![CDATA[
+                  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+                      <xsl:output method="xml" omit-xml-declaration="yes"/>
+                      <xsl:variable name="logContent" select="/UpdateLogRequest/LogContent"/>
+                      <xsl:variable name="transactionId" select="$logContent/UserContext/Context/Row/Common/TransactionId"/>
+                      <xsl:template match="/">
+                          <Result>
+                          <Dataset name='special-data'>
+                              <Row>
+                              <Records>
+                                  <Rec>
+                                  <transaction_id><xsl:value-of select="$transactionId"/></transaction_id>
+                                  <request_data>
+                                      <xsl:text disable-output-escaping="yes">&amp;lt;![CDATA[COMPRESS('</xsl:text>
+                                      <xsl:copy-of select="$logContent/UserContent/Context"/>
+                                      <xsl:text disable-output-escaping="yes">')]]&amp;gt;</xsl:text>
+                                  </request_data>
+                                  <request_format>SPECIAL</request_format>
+                                  <type>23</type>
+                                  </Rec>
+                              </Records>
+                              </Row>
+                          </Dataset>
+                          </Result>
+                      </xsl:template>
+                  </xsl:stylesheet>
+                ]]>
+              </Transform>
+            </XSL>
+          </LogAgent>
+        </LoggingManager>
+      </Definition>
+    </Binding>
+    <Definitions>
+      <![CDATA[
+        <esxdl name="WsFoobar"><EsdlRequest name="FoobarSearchRequest"><EsdlElement  type="int" name="RequestOption"/><EsdlElement  type="string" name="RequestName"/><EsdlElement  optional="hidden" type="bool" name="HiddenOption"/></EsdlRequest>
+        <EsdlResponse name="FoobarSearchResponse"><EsdlElement  type="int" name="FoundCount"/><EsdlElement  type="string" name="FoundAddress"/></EsdlResponse>
+        <EsdlRequest name="WsFoobarPingRequest"></EsdlRequest>
+        <EsdlResponse name="WsFoobarPingResponse"></EsdlResponse>
+        <EsdlService version="1" auth_feature="DEFERRED" name="WsFoobar" default_client_version="1"><EsdlMethod response_type="FoobarSearchResponse" request_type="FoobarSearchRequest" name="FoobarSearch"/><EsdlMethod response_type="WsFoobarPingResponse" auth_feature="none" request_type="WsFoobarPingRequest" name="Ping"/></EsdlService>
+        </esxdl>
+      ]]>
+    </Definitions>
+  </EsdlBundle>

Binding

The binding can be used to configure a service for an ESP using a dali.

xml
<Binding id="WsFoobar_desdl_binding">
+  <Definition esdlservice="WsFoobar" id="WsFoobar.1">
+    <Methods>
+      <Scripts>
+        <![CDATA[
+          <Scripts>
+            <es:BackendRequest name="request-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+                <es:set-value target="RequestValue" value="'foobar'"/>
+            </es:BackendRequest>
+            <es:PreLogging name="logging-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+                <es:set-value target="LogValue" value="23"/>
+            </es:PreLogging>
+          </Scripts>
+        ]]>
+      </Scripts>
+      <Method name="FoobarSearch" url="127.0.0.1:8888">
+        <Scripts>
+          <![CDATA[
+            <Scripts>
+              <Scripts>
+                  <es:BackendRequest name="search-request-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+                      <es:if test="RequestOption>1">
+                          <es:set-value target="HiddenOption" value="true()"/>
+                      </es:if>
+                  </es:BackendRequest>
+
+                  <es:PreLogging name="search-logging-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+                      <es:if test="RequestOption=1">
+                          <es:set-value target="ProductPrice" value="10"/>
+                      </es:if>
+                  </es:PreLogging>
+              </Scripts>
+            </Scripts>
+          ]]>
+        </Scripts>
+      </Method>
+    </Methods>
+    <LoggingManager>
+      <LogAgent transformSource="local" name="main-logging">
+        <LogDataXPath>
+          <LogInfo name="PreparedData" xsl="log-prep"/>
+        </LogDataXPath>
+        <XSL>
+          <Transform name="log-prep">
+            <![CDATA[
+              <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+                  <xsl:output method="xml" omit-xml-declaration="yes"/>
+                  <xsl:variable name="logContent" select="/UpdateLogRequest/LogContent"/>
+                  <xsl:variable name="transactionId" select="$logContent/UserContext/Context/Row/Common/TransactionId"/>
+                  <xsl:template match="/">
+                      <Result>
+                      <Dataset name='special-data'>
+                          <Row>
+                          <Records>
+                              <Rec>
+                              <transaction_id><xsl:value-of select="$transactionId"/></transaction_id>
+                              <request_data>
+                                  <xsl:text disable-output-escaping="yes">&amp;lt;![CDATA[COMPRESS('</xsl:text>
+                                  <xsl:copy-of select="$logContent/UserContent/Context"/>
+                                  <xsl:text disable-output-escaping="yes">')]]&amp;gt;</xsl:text>
+                              </request_data>
+                              <request_format>SPECIAL</request_format>
+                              <type>23</type>
+                              </Rec>
+                          </Records>
+                          </Row>
+                      </Dataset>
+                      </Result>
+                  </xsl:template>
+              </xsl:stylesheet>
+            ]]>
+          </Transform>
+        </XSL>
+      </LogAgent>
+    </LoggingManager>
+  </Definition>
+</Binding>
`,78),l=[e];function h(p,k,o,d,r,E){return t(),i("div",null,l)}const u=s(n,[["render",h]]);export{c as __pageData,u as default}; diff --git a/assets/tools_esdlcmd_README.md.kugmSdg_.lean.js b/assets/tools_esdlcmd_README.md.kugmSdg_.lean.js new file mode 100644 index 00000000000..8ed2fc1e51b --- /dev/null +++ b/assets/tools_esdlcmd_README.md.kugmSdg_.lean.js @@ -0,0 +1 @@ +import{_ as s,c as i,o as t,V as a}from"./chunks/framework.gBlNPWt_.js";const c=JSON.parse('{"title":"esdl Utility","description":"","frontmatter":{},"headers":[],"relativePath":"tools/esdlcmd/README.md","filePath":"tools/esdlcmd/README.md","lastUpdated":1721825996000}'),n={name:"tools/esdlcmd/README.md"},e=a("",78),l=[e];function h(p,k,o,d,r,E){return t(),i("div",null,l)}const u=s(n,[["render",h]]);export{c as __pageData,u as default}; diff --git a/assets/tools_tagging_README.md.aY1N0cJO.js b/assets/tools_tagging_README.md.aY1N0cJO.js new file mode 100644 index 00000000000..ac1e2331cac --- /dev/null +++ b/assets/tools_tagging_README.md.aY1N0cJO.js @@ -0,0 +1,13 @@ +import{_ as e,c as a,o as s,V as n}from"./chunks/framework.gBlNPWt_.js";const m=JSON.parse('{"title":"Tagging new versions","description":"","frontmatter":{},"headers":[],"relativePath":"tools/tagging/README.md","filePath":"tools/tagging/README.md","lastUpdated":1721825996000}'),i={name:"tools/tagging/README.md"},t=n(`

Tagging new versions

General

The file tools/git/aliases.sh contains various git aliases which are useful when using git, and may be used by the merge scripts.

The file env.sh.example contains some example environment variable settings. Copy that locally to env.sh and modify it to match your local setup.

Before running any of the other scripts, process the contents of that file as a source file

. env.sh

to initialize the common environment variables.

Pre-requisites

The following tools are required:

  • git
  • helm

The following repositories should be checked out in a directory reserved for merging and tagging (default for scripts is ~/git):

git clone git@github.com:hpcc-systems/eclide.git
+git clone git@github.com:hpcc-systems/hpcc4j.git HPCC-JAPIs
+git clone git@github.com:hpcc-systems/Spark-HPCC.git
+git clone git@github.com:hpcc-systems/LN.git ln
+git clone git@github.com:hpcc-systems/HPCC-Platform.git hpcc
+git clone git@github.com:hpcc-systems/helm-chart.git

The following are required for builds prior to 8.12.x

git clone git@github.com:hpcc-systems/nagios-monitoring.git
+git clone git@github.com:hpcc-systems/ganglia-monitoring.git

The files git-fixversion and git-unupmerge can copied so they are on your default path, and then they will be available as git commands.

Tagging new versions

The following process should be followed when tagging a new set of versions.

  1. Upmerge all changes between candidate branches for the different versions

You can set the all environment variable to a subset of the projects (e.g. export all=hpcc) if there are no changes in the other repositories. The only effect for projects that are upmerged with no changes will be that they gain an empty merge transaction. If multiple people are merging PRs to different repositories it may be safer to upmerge all projects.

For example:

./upmerge A.a.x candidate-A.b.x
+./upmerge A.b.x candidate-A.c.x
+./upmerge A.b.x candidate-B.0.x
+./upmerge B.0.x master
  1. Create new point-release candidate branches:
./gorc.sh A.a.x
+./gorc.sh A.b.x
+./gorc.sh A.c.x

Taking a build gold:

Go gold with each of the explicit versions

./gogold.sh 7.8.76
+./gogold.sh 7.10.50

If you have merged changes onto a point-release branch you would normally create a new rc before going gold. If the change was trivial (e.g. removing an unwanted file) then you can use the --ignore option to skip that step.

Creating a new rc for an existing point release:

This normally happens after cherry-picking a late fix for a particular version, which has already been merged into the .x candidate branch.

./gorc.sh A.a.<n>

Create a new minor/major branch

A new minor branch is created from the current master...

./gominor.sh
`,33),o=[t];function l(p,r,c,g,h,d){return s(),a("div",null,o)}const b=e(i,[["render",l]]);export{m as __pageData,b as default}; diff --git a/assets/tools_tagging_README.md.aY1N0cJO.lean.js b/assets/tools_tagging_README.md.aY1N0cJO.lean.js new file mode 100644 index 00000000000..2eeb44197c7 --- /dev/null +++ b/assets/tools_tagging_README.md.aY1N0cJO.lean.js @@ -0,0 +1 @@ +import{_ as e,c as a,o as s,V as n}from"./chunks/framework.gBlNPWt_.js";const m=JSON.parse('{"title":"Tagging new versions","description":"","frontmatter":{},"headers":[],"relativePath":"tools/tagging/README.md","filePath":"tools/tagging/README.md","lastUpdated":1721825996000}'),i={name:"tools/tagging/README.md"},t=n("",33),o=[t];function l(p,r,c,g,h,d){return s(),a("div",null,o)}const b=e(i,[["render",l]]);export{m as __pageData,b as default}; diff --git a/cmake_modules/DOCUMENTATION.html b/cmake_modules/DOCUMENTATION.html new file mode 100644 index 00000000000..206ff99df16 --- /dev/null +++ b/cmake_modules/DOCUMENTATION.html @@ -0,0 +1,75 @@ + + + + + + CMake files structure and usage | HPCC Platform + + + + + + + + + + + + + +
Skip to content

CMake files structure and usage

Directory structure of CMake files

- /

: - CMakeLists.txt - Root CMake file - version.cmake - common cmake file where version variables are set - build-config.h.cmake - cmake generation template for build-config.h

\- cmake\_modules/ - Directory storing modules and configurations for CMake
+
+:   -   FindXXXXX.cmake - CMake find files used to locate libraries,
+        headers, and binaries
+    -   commonSetup.cmake - common configuration settings for the
+        entire project (contains configure time options)
+    -   docMacros.cmake - common documentation macros used for
+        generating fop and pdf files
+    -   optionDefaults.cmake - contains common variables for the
+        platform build
+    -   distrocheck.sh - script that determines if the OS uses DEB
+        or RPM
+    -   getpackagerevisionarch.sh - script that returns OS version
+        and arch in format used for packaging
+
+    \- dependencies/ - Directory storing dependency files used for package dependencies
+
+    :   -   \<OS\>.cmake - File containing either DEB or RPM
+            dependencies for the given OS
+
+\- build-utils/ - Directory for build related utilities
+
+:   -   cleanDeb.sh - script that unpacks a deb file and rebuilds
+        with fakeroot to clean up lintain errors/warnings
+

Common Macros

  • MACRO_ENSURE_OUT_OF_SOURCE_BUILD - prevents building from with in source tree
  • HPCC_ADD_EXECUTABLE - simple wrapper around add_executable
  • HPCC_ADD_LIBRARY - simple wrapper around add_library
  • PARSE_ARGUMENTS - macro that can be used by other macros and functions for arg list parsing
  • HPCC_ADD_SUBDIRECTORY - argument controlled add subdirectory wrapper
  • HPCC_ADD_SUBDIRECTORY(test t1 t2 t3) - Will add the subdirectory test if t1,t2, or t3 are set to any value other then False/OFF/0
  • LOG_PLUGIN - used to log any code based plugin for the platform
  • ADD_PLUGIN - adds a plugin with optional build dependencies to the build if dependencies are found

Documentation Macros

  • RUN_XSLTPROC - Runs xsltproc using given args
  • RUN_FOP - Runs fop using given args
  • CLEAN_REL_BOOK - Uses a custom xsl and xsltproc to clean relative book paths from given xml file
  • CLEAN_REL_SET - Uses a custom xsl and xsltproc to clean relative set paths from given xml file
  • DOCBOOK_TO_PDF - Master macro used to generate pdf, uses above macros

Initfiles macro

  • GENERATE_BASH - used to run processor program on given files to replace ###<REPLACE>### with given variables FindXXXXX.cmake

Some standard techniques used in Cmake project files

Common looping

Use FOREACH:

FOREACH( oITEMS
+  item1
+  item2
+  item3
+  item4
+  item5
+)
+  Actions on each item here.
+ENDFOREACH ( oITEMS )
+

Common installs over just install

  • install ( FILES ... ) - installs item with 664 permissions
  • install ( PROGRAMS ... ) - installs runable item with 755 permissions
  • install ( TARGETS ... ) - installs built target with 755 permissions
  • install ( DIRECTORY ... ) - installs directory with 777 permissions

Common settings for generated source files

  • set_source_files_properties(<file> PROPERTIES GENERATED TRUE) - Must be set on generated source files or dependency generation fails and increases build time.

Using custom commands between multiple cmake files

  • GET_TARGET_PROPERTY(<VAR from other cmake file> <var for this file> LOCATION)
  • GET_TARGET_PROPERTY(ESDL_EXE esdl LOCATION) - will get from the top level cache the ESDL_EXE value and set it in esdl for your current cmake file

USE add_custom_command only when 100% needed.

All directories in a cmake project should have a CMakeLists.txt file and be called from the upper level project with an add_subdirectory or HPCC_ADD_SUBDIRECTORY

When you have a property that will be shared between cmake projects use define_property to set it in the top level cache.

  • define_property(GLOBAL PROPERTY TEST_TARGET BRIEF_DOCS "test doc" FULL_DOCS "Full test doc")
  • mark_as_advanced(TEST_TARGET) - this is required to force the property into the top level cache.CMake Layout:

FindXXXXX.cmake format

All of our Find scripts use the following format:

NOT XXXXX_FOUND
+  Externals set
+    define needed vars for finding external based libraries/headers
+
+    Use Native set
+      use FIND_PATH to locate headers
+      use FIND_LIBRARY to find libs
+
+Include Cmake macros file for package handling
+define package handling args for find return  (This will set XXXXX_FOUND)
+
+XXXXX_FOUND
+  perform any modifications you feel is needed for the find
+
+Mark defined variables used in package handling args as advanced for return
+

Will define when done:

XXXXX_FOUND
+XXXXX_INCLUDE_DIR
+XXXXX_LIBRARIES
+

(more can be defined, but must be at min the previous unless looking for only a binary)

For an example, see FindAPR.cmake

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/devdoc/CodeGenerator.html b/devdoc/CodeGenerator.html new file mode 100644 index 00000000000..81022a1957e --- /dev/null +++ b/devdoc/CodeGenerator.html @@ -0,0 +1,36 @@ + + + + + + Eclcc/Code generator | HPCC Platform + + + + + + + + + + + + + +
Skip to content

Introduction

Purpose

The primary purpose of the code generator is to take an ECL query and convert it into a work unit that is suitable for running by one of the engines.

Aims

The code generator has to do its job accurately. If the code generator does not correctly map the ECL to the workunit it can lead to corrupt data and invalid results. Problems like that can often be very hard and frustrating for the ECL users to track down.

There is also a strong emphasis on generating output that is as good as possible. Eclcc contains many different optimization stages, and is extensible to allow others to be easily added.

Eclcc needs to be able to cope with reasonably large jobs. Queries that contain several megabytes of ECL, and generate tens of thousands of activities, and 10s of Mb of C++ are routine. These queries need to be processed relatively quickly.

Key ideas

Nearly all the processing of ECL is done using an expression graph. The representation of the expression graph has some particular characteristics:

  • Once the nodes in the expression graph have been created they are NEVER modified.
  • Nodes in the expression graph are ALWAYS commoned up if they are identical.
  • Each node in the expression graph is link counted (see below) to track its lifetime.
  • If a modified graph is required a new graph is created (sharing nodes from the old one)

The ECL language is a declarative language, and in general is assumed to be pure - i.e. there are no side-effects, expressions can be evaluated lazily and re-evaluating an expression causes no problems. This allows eclcc to transform the graph in lots of interesting ways. (Life is never that simple so there are mechanisms for handling the exceptions.)

From declarative to imperative

One of the main challenges with eclcc is converting the declarative ECL code into imperative C++ code. One key problem is it needs to try to ensure that code is only evaluated when it is required, but that it is also only evaluated once. It isn't always possible to satisfy both constraints - for example a global dataset expression used within a child query. Should it be evaluated once before the activity containing the child query is called, or each time the child query is called? If it is called on demand then it may not be evaluated as efficiently...

This issue complicates many of the optimizations and transformations that are done to the queries. Long term the plan is to allow the engines to support more delayed lazy-evaluation, so that whether something is evaluated is more dynamic rather than static.

Flow of processing

The idealised view of the processing within eclcc follows the following stages:

  • Parse the ECL into an expression graph.
  • Expand out function calls.
  • Normalize the expression graph so it is in a consistent format.
  • Normalize the references to fields within datasets to tie them up with their scopes.
  • Apply various global optimizations.
  • Translate logical operations into the activities that will implement them.
  • Resource and generate the global graph
  • For each activity, resource, optimize and generate its child graphs.

In practice the progression is not so clear cut. There tends to be some overlap between the different stages, and some of them may occur in slightly different orders. However the order broadly holds.

Working on the code generator

The regression suite

Before any change is accepted for the code generator it is always run against several regression suites to ensure that it doesn't introduce any problems, and that the change has the desired effect. There are several different regression suites:

  • testing/regress/ecl - The run time regression suite.
  • ecl/regress - a compiler regression suite. This contains tests that cannot run and error tests.
  • LN private suite - This contains a large selection (>10Gb) of archived queries. The contain proprietary code so unfortunately cannot be released as open source.

The ecl/regress directory contains a script 'regress.sh' that is used for running the regression tests. It should be executed in the directory containing the ecl files. The script generates the c++ code (and workunits) for each of the source files to a target directory, and then executes a comparison program to compare the new results with a previous "golden" reference set.

Before making any changes to the compiler, a reference set should be created by running the regression script and copying the generated files to the reference directory.

Here is a sample command line

~/dev/hpcc/ecl/regress/regress.sh -t /regress/hpcc -e /home/<user>/buildr/Release/bin/eclcc -I /home/<user>/dev/hpcc/ecl/regress/modules -I /home/<user>/dev/hpcc/plugins/javaembed -I /home/<user>/dev/hpcc/plugins/v8embed -c /regress/hpcc.master -d bcompare

(A version of this command resides in a shell script in each of my regression suite directories, with the -t and -c options adapted for each suite.)

For a full list of options execute the script with no parameters, or take a look at the script itself. A couple of useful options are:

  • The script can be run on a single file by using the -q option.
  • The (-e) option selects the path of the eclcc. This is particularly useful when running from the build directory (see below), or using multiple build directories to compare behaviour between different versions.

We strongly recommend using a comparison program which allows rules to be defined to ignore certain differences (e.g., beyond compare).

Running directly from the build directory

It is much quicker to run eclcc directly from the build directory, rather than deploying a system and running eclcc from there. To do this you need to configure some options that eclcc requires, e.g. where the include files are found. The options can be set by either setting environment variables or by specifiying options in an eclcc.ini file. The following are the names of the different options:


Environment flag Ini file option


CL_PATH compilerPath

ECLCC_LIBRARY_PATH libraryPath

ECLCC_INCLUDE_PATH includePath

ECLCC_PLUGIN_PATH plugins

HPCC_FILEHOOKS_PATH filehooks

ECLCC_TPL_PATH templatePath

ECLCC_ECLLIBRARY_PATH eclLibrariesPath

ECLCC_ECLBUNDLE_PATH eclBundlesPath

The eclcc.ini can either be a file in the local directory, or specified on the eclcc command line with -specs. Including the settings in a local eclcc.ini file also it easy to debug eclcc directly from the build directory within the eclipse environment.

Hints and tips

  • Logging

    There is an option for eclcc to output a logging file, and another to specify the level of detail in that logging file. If the detail level is above 500 then the expresssion tree for the query is output to the logging file after each of the code transformations. The tracing is very useful for tracking down at which stage inconsistencies are introduced in the expression graph, and also for learning how each transformation affects the query.

    The output format defaults to ECL - which is regenerated from the expression tree. (This ECL cannot generally be compiled without editing - partly because it contains extra annoations.) Use either of the following:

    eclcc myfile.ecl --logfile myfile.log --logdetail 999

    regress.sh -q myfile.ecl -l myfile.log

  • -ftraceIR

    There is a debug option (-ftraceIR) that generates an intermediate representation of the expression graph rather than regenerating ECL. The output tends to be less compact and harder to read quickly, but has the advantage of being better structured, and contains more details of the internal representation. ecl/hql/hqlir.cpp contains more details of the format.

  • Adding extra logging into the source code

    If you want to add tracing of expressions at any point in the code generation then adding either of the following calls will include the expression details in the log file:

    dbglogExpr(expr); // regenerate the ecl for an expression. See other functions in ecl/hql/hqlthql.hpp

    EclIR::dbglogIR(expr); // regenerate the IR for an expression. See other functions in ecl/hql/hqlir.hpp

  • Logging while debugging

    If you are debugging inside gdb it is often useful to be able to dump out details of an expression. Calling EclIR:dump_ir(expr); will generate the IR to stdout.

    p EclIR::dump_ir(expr)

    The function can also be used with multiple parameters. Each expression will be dumped out, but common child nodes will only be generated once. This can be very useful when trying to determine the difference between two expressions. The quickest way is to call EclIR::dump_ir(expr1, expr2). The first difference between the expressions will be the expression that follows the first "return".

  • Expression sequence ids.

    Sometimes it can be hard to determine where a particular IHqlExpression node was created. If that is the case, then defining DEBUG_TRACK_INSTANCEID (in ecl/hql/hqlexpr.ipp) will add a unique sequence number to each IHqlExpression that is created. There is also a function checkSeqId() at the start of ecl/hql/hqlexpr.cpp which is called whenever an expression is created, linked, released etc.. Setting a breakpoint in that function can allow you to trace back exactly when and why a particular node was created.

Expressions

Expression Graph representation

The key data structure within eclcc is the graph representation. The design has some key elements.

  • Once a node is created it is never modified.

    Some derived information (e.g., sort order, number of records, unique hash, ...) might be calculated and stored in the class after it has been created, but that doesn't change what the node represents in any way. Some nodes are created in stages - e.g., records, modules. These nodes are marked as fully completed when closeExpr() is called, after which they cannot be modified.

  • Nodes are always commoned up.

    If the same operator has the same arguments and type then there will be a unique IHqlExpression to represent it. This helps ensure that graphs stay as graphs and don't get converted to trees. It also helps with optimizations, and allows code duplicated in two different contexts to be brought together.

  • The nodes are link counted.

    Link counts are used to control the lifetime of the expression objects. Whenever a reference to an expression node is held, its link count is increased, and decreased when no longer required. The node is freed when there are no more references. (This generally works well, but does give us problems with forward references.)

  • The access to the graph is through interfaces.

    The main interfaces are IHqlExpression, IHqlDataset and IHqlScope. They are all defined in hqlexpr.hpp. The aim of the interfaces is to hide the implementation of the expression nodes so they can be restructured and changed without affecting any other code.

  • The expression classes use interfaces and a type field rather than polymorphism. This could be argued to be bad object design...but.

    There are more than 500 different possible operators. If a class was created for each of them the system would quickly become unwieldy. Instead there are several different classes which model the different types of expression (dataset/expression/scope).

    The interfaces contain everything needed to create and interrogate an expression tree, but they do not contain functionality for directly processing the graph.

    To avoid some of the shortcomings of type fields there are various mechanisms for accessing derived attributes which avoid interrogating the type field.

  • Memory consumption is critical.

It is not unusual to have 10M or even 100M nodes in memory as a query is being processed. At that scale the memory consumption of each node matters - so great care should be taken when considering increasing the size of the objects. The node classes contain a class hierarchy which is there purely to reduce the memory consumption - not to reflect the functionality. With no memory constraints they wouldn't be there, but removing a single pointer per node can save 1Gb of memory usage for very complex queries.

IHqlExpression

This is the interface that is used to walk and interrogate the expression graph once it has been created. Some of the main functions are: getOperator() What does this node represent? It returns a member of the node_operator enumerated type. numChildren() How many arguments does node have? queryChild(unsigned n) What is the nth child? If the argument is out of range it returns NULL. queryType() The type of this node. queryBody() Used to skip annotations (see below) queryProperty() Does this node have a child which is an attribute that matches a given name. (see below for more about attributes). queryValue() For a no_constant return the value of the constant. It returns NULL otherwise.

The nodes in the expression graph are created through factory functions. Some of the expression types have specialised functions - e.g., createDataset, createRow, createDictionary, but scalar expressions and actions are normally created with createValue().

Note: Generally ownership of the arguments to the createX() functions are assumed to be taken over by the newly created node.

The values of the enumeration constants in node_operator are used to calculate "crcs" which are used to check if the ECL for a query matches, and if disk and index record formats match. It contains quite a few legacy entries no_unusedXXX which can be used for new operators (otherwise new operators must be added to the end).

IHqlSimpleScope

This interface is implemented by records, and is used to map names to the fields within the records. If a record contains IFBLOCKs then each of the fields in the ifblock is defined in the IHqlSimpleScope for the containing record.

IHqlScope

Normally obtained by calling IHqlExpression::queryScope(). It is primarily used in the parser to resolve fields from within modules.

The ECL is parsed on demand so as the symbol is looked up it may cause a cascade of ECL to be compiled. The lookup context (HqlLookupContext ) is passed to IHqlScope::lookupSymbol() for several reasons:

  • It contains information about the active repository - the source of the ECL which will be dynamically parsed.
  • It contains caches of expanded functions - to avoid repeating expansion transforms.
  • Some members are used for tracking definitions that are read to build dependency graphs, or archives of submitted queries.

The interface IHqlScope currently has some members that are used for creation; this should be refactored and placed in a different interface.

IHqlDataset

This is normally obtained by calling IHqlExpression::queryDataset(). It has shrunk in size over time, and could quite possibly be folded into IHqlExpression with little pain.

There is a distinction in the code generator between "tables" and "datasets". A table (IHqlDataset::queryTable()) is a dataset operation that defines a new output record. Any operation that has a transform or record that defines an output record (e.g., PROJECT,TABLE) is a table, whilst those that don't (e.g., a filter, dedup) are not. There are a few apparent exceptions -e.g., IF (This is controlled by definesColumnList() which returns true the operator is a table.)

Properties and attributes

There are two related by slightly different concepts. An attribute refers to the explicit flags that are added to operators (e.g., , LOCAL, KEEP(n) etc. specified in the ECL or some internal attributes added by the code generator). There are a couple of different functions for creating attributes. createExtraAttribute() should be used by default. createAttribute() is reserved for an attribute that never has any arguments, or in unusual situations where it is important that the arguments are never transformed. They are tested using queryAttribute()/hasAttribute() and represented by nodes of kind no_attr/no_expr_attr.

The term "property" refers to computed information (e.g., record counts) that can be derived from the operator, its arguments and attributes. They are covered in more detail below.

Field references

Fields can be selected from active rows of a dataset in three main ways:

  • Some operators define LEFT/RIGHT to represent an input or processed dataset. Fields from these active rows are referenced with LEFT.<field-name>. Here LEFT or RIGHT is the "selector".

  • Other operators use the input dataset as the selector. E.g., myFile(myFile.id != 0). Here the input dataset is the "selector".

  • Often when the input dataset is used as the selector it can be omitted. E.g., myFile(id != 0). This is implicitly expanded by the PARSER to the second form. A reference to a field is always represented in the expression graph as a node of kind no_select (with createSelectExpr). The first child is the selector, and the second is the field. Needless to say there are some complications...

  • LEFT/RIGHT.

    The problem is that the different uses of LEFT/RIGHT need to be disambiguated since there may be several different uses of LEFT in a query. This is especially true when operations are executed in child queries. LEFT is represented by a node no_left(record, selSeq). Often the record is sufficient to disambiguate the uses, but there are situations where it isn't enough. So in addition no_left has a child which is a selSeq (selector sequence) which is added as a child attribute of the PROJECT or other operator. At parse time it is a function of the input dataset that is later normalized to a unique id to reduce the transformation work.

  • Active datasets. It is slightly more complicated - because the dataset used as the selector can be any upstream dataset up to the nearest table. So the following ECL code is legal:

    x := DATASET(...)
    +y := x(x.id != 0);
    +z := y(x.id != 100);
    +

Here the reference to x.id in the definition of z is referring to a field in the input dataset.

Because of these semantics the selector in a normalized tree is actually inputDataset->queryNormalizedSelector() rather than inputDatset. This function currently returns the table expression node (but it may change in the future see below).

Attribute "new"

In some situations ECL allows child datasets to be treated as a dataset without an explicit NORMALIZE. E.g., EXISTS(myDataset.ChildDataset);

This is primarily to enable efficient aggregates on disk files to be generated, but it adds some complications with an expression of the form dataset.childdataset.grandchild. E.g.,:

EXISTS(dataset(EXISTS(dataset.childdataset.grandchild))
+

Or:

EXISTS(dataset.childdataset(EXISTS(dataset.childdataset.grandchild))
+

In the first example dataset.childdataset within the dataset.childdataset.grandchild is a reference to a dataset that doesn't have an active cursor and needs to be iterated), whilst in the second it refers to an active cursor.

To differentiate between the two, all references to fields within datasets/rows that don't have active selectors have an additional attribute("new") as a child of the select. So a no_select with a "new" attribute requires the dataset to be created, one without is a member of an active dataset cursor.

If you have a nested row, the new attribute is added to the selection from the dataset, rather than the selection from the nested row. The functions queryDatasetCursor() and querySelectorDataset()) are used to help interpret the meaning.

(An alternative would be to use a different node from no_select - possibly this should be considered - it would be more space efficient.)

The expression graph generated by the ECL parser doesn't contain any new attributes. These are added as one of the first stages of normalizing the expression graph. Any code that works on normalized expressions needs to take care to interpret no_selects correctly.

Transforming selects

When an expression graph is transformed and none of the records are changed, the representation of LEFT/RIGHT remains the same. This means any no_select nodes in the expression tree will also stay the same.

However, if the transform modifies a table (highly likely) it means that the selector for the second form of field selector will also change. Unfortunately this means that transforms often cannot be short-circuited.

It could significantly reduce the extent of the graph that needs traversing, and the number of nodes replaced in a transformed graph if this could be avoided. One possibility is to use a different value for dataset->queryNormalizedSelector() using a unique id associated with the table. I think it would be a good long term change, but it would require unique ids (similar to the selSeq) to be added to all table expressions, and correctly preserved by any optimization.

Annotations

Sometimes it is useful to add information into the expression graph (e.g., symbol names, position information) that doesn't change the meaning, but should be preserved. Annotations allow information to be added in this way.

An annotation's implementation of IHqlExpression generally delegates the majority of the methods through to the annotated expression. This means that most code that interrogates the expression graph can ignore their presence, which simplifies the caller significantly. However transforms need to be careful (see below).

Information about the annotation can be obtained by calling IHqlExpression:: getAnnotationKind() and IHqlExpression:: queryAnnotation().

Associated side-effects

In legacy ECL you will see code like the following::

EXPORT a(x) := FUNCTION
+   Y := F(x);
+   OUTPUT(Y);
+   RETURN G(Y);
+END;
+

The assumption is that whenever a(x) is evaluated the value of Y will be output. However that doesn't particularly fit in with a declarative expression graph. The code generator creates a special node (no_compound) with child(0) as the output action, and child(1) as the value to be evaluated (g(Y)).

If the expression ends up being included in the final query then the action will also be included (via the no_compound). At a later stage the action is migrated to a position in the graph where actions are normally evaluated.

Derived properties

There are many pieces of information that it is useful to know about a node in the expression graph - many of which would be expensive to recomputed each time there were required. Eclcc has several mechanisms for caching derived information so it is available efficiently.

  • Boolean flags - getInfoFlags()/getInfoFlags2().

    There are many Boolean attributes of an expression that are useful to know - e.g., is it constant, does it have side-effects, does it reference any fields from a dataset etc. etc. The bulk of these are calculated and stored in a couple of members of the expression class. They are normally retrieved via accessor functions e.g., containsAssertKeyed(IHqlExpression*).

  • Active datasets - gatherTablesUsed().

    It is very common to want to know which datasets an expression references. This information is calculated and cached on demand and accessed via the IHqlExpression::gatherTablesUsed() functions. There are a couple of other functions IHqlExpression::isIndependentOfScope() and IHqlExpression::usesSelector() which provide efficient functions for common uses.

  • Information stored in the type.

    Currently datasets contain information about sort order, distribution and grouping as part of the expression type. This information should be accessed through the accessor functions applied to the expression (e.g., isGrouped(expr)). At some point in the future it is planned to move this information as a general derived property (see next).

  • Other derived property.

    There is a mechanism (in hqlattr) for calculating and caching an arbitrary derived property of an expression. It is currently used for number of rows, location-independent representation, maximum record size etc. . There are typically accessor functions to access the cached information (rather than calling the underlying IHqlExpression::queryAttribute() function).

  • Helper functions.

    Some information doesn't need to be cached because it isn't expensive to calculate, but rather than duplicating the code, a helper function is provided. E.g., queryOriginalRecord() and hasUnknownTransform(). They are not part of the interface because the number would make the interface unwieldy and they can be completely calculated from the public functions.

    However, it can be very hard to find the function you are looking for, and they would greatly benefit from being grouped e.g., into namespaces.

Transformations

One of the key processes in eclcc is walking and transforming the expression graphs. Both of these are covered by the term transformations. One of the key things to bear in mind is that you need to walk the expression graph as a graph, not as a tree. If you have already examined a node once you shouldn't repeat the work - otherwise the execution time may be exponential with node depth.

Other things to bear in mind

  • If a node isn't modified don't create a new one - return a link to the old one.
  • You generally need to walk the graph and gather some information before creating a modified graph. Sometimes creating a new graph can be short-circuited if no changes will be required.
  • Sometimes you can be tempted to try and short-circuit transforming part of a graph (e.g., the arguments to a dataset activity), but because of the way references to fields within dataset work that often doesn't work.
  • If an expression is moved to another place in the graph, you need to be very careful to check if the original context was conditional and that the new context is not.
  • The meaning of expressions can be context dependent. E.g., References to active datasets can be ambiguous.
  • Never walk the expressions as a tree, always as a graph!
  • Be careful with annotations.

It is essential that an expression that is used in different contexts with different annotations (e.g., two different named symbols) is consistently transformed. Otherwise it is possible for a graph to be converted into a tree. E.g.,:

A := x; B := x; C = A + B;
+

must not be converted to:

A' := x'; B' := X'';  C' := A' + B';
+

For this reason most transformers will check if expr->queryBody() matches expr, and if not will transform the body (the unannotated expression), and then clone any annotations.

Some examples of the work done by transformations are:

  • Constant folding.
  • Expanding function calls.
  • Walking the graph and reporting warnings.
  • Optimizing the order and removing redundant activities.
  • Reducing the fields flowing through the generated graph.
  • Spotting common sub expressions.
  • Calculating the best location to evaluate an expression (e.g., globally instead of in a child query).
  • Many, many others.

Some more details on the individual transforms are given below..

Key Stages

Parsing

The first job of eclcc is to parse the ECL into an expression graph. The source for the ECL can come from various different sources (archive, source files, remote repository). The details are hidden behind the IEclSource/IEclSourceCollection interfaces. The createRepository() function is then used to resolve and parse the various source files on demand.

Several things occur while the ECL is being parsed:

  • Function definitions are expanded inline.

    A slightly unusual behaviour. It means that the expression tree is a fully expanded expression -which is better suited to processing and optimizing.

  • Some limited constant folding occurs.

    When a function is expanded, often it means that some of the test conditions are always true/false. To reduce the transformations the condition may be folded early on.

  • When a symbol is referenced from another module this will recursively cause the ECL for that module (or definition within that module) to be parsed.

  • Currently the semantic checking is done as the ECL is parsed.

    If we are going to fully support template functions and delayed expansion of functions this will probably have to change so that a syntax tree is built first, and then the semantic checking is done later.

Normalizing

There are various problems with the expression graph that comes out of the parser:

  • Records can have values as children (e.g., { myField := infield.value} ), but it causes chaos if record definitions can change while other transformations are going on. So the normalization removes values from fields.

  • Some activities use records to define the values that output records should contain (e.g., TABLE). These are now converted to another form (e.g., no_newusertable).

  • Sometimes expressions have multiple definition names. Symbols and annotations are rationalized and commoned up to aid commoning up other expressions.

  • Some PATTERN definitions are recursive by name. They are resolved to a form that works if all symbols are removed.

  • The CASE/MAP representation for a dataset/action is awkward for the transforms to process. They are converted to nested Ifs.

    (At some point a different representation might be a good idea.)

  • EVALUATE is a weird syntax. Instances are replaced with equivalent code which is much easier to subsequently process.

  • The datasets used in index definitions are primarily there to provide details of the fields. The dataset itself may be very complex and may not actually be used. The dataset input to an index is replaced with a dummy "null" dataset to avoid unnecessary graph transforming, and avoid introducing any additional incorrect dependencies.

Scope checking

Generally if you use LEFT/RIGHT then the input rows are going to be available wherever they are used. However if they are passed into a function, and that function uses them inside a definition marked as global then that is invalid (since by definition global expressions don't have any context).

Similarly if you use syntax <dataset>.<field>, its validity and meaning depends on whether <dataset> is active. The scope transformer ensures that all references to fields are legal, and adds a "new" attribute to any no_selects where it is necessary.

Constant folding: foldHqlExpression

This transform simplifies the expression tree. Its aim is to simplify scalar expressions, and dataset expressions that are valid whether or not the nodes are shared. Some examples are:

  • 1 + 2 => 3 and any other operation on scalar constants.
  • IF(true, x, y) => x
  • COUNT(<empty-dataset>) => 0
  • IF (a = b, 'c', 'd') = 'd' => IF(a=b, false, true) => a != b
  • Simplifying sorts, projects filters on empty datasets

Most of the optimizations are fairly standard, but a few have been added to cover more esoteric examples which have occurred in queries over the years.

This transform also supports the option to percolate constants through the graph. E.g., if a project assigns the value 3 to a field, it can substitute the value 3 wherever that field is used in subsequent activities. This can often lead to further opportunities for constant folding (and removing fields in the implicit project).

Expression optimizer: optimizeHqlExpression

This transformer is used to simplify, combine and reorder dataset expressions. The transformer takes care to count the number of times each expression is used to ensure that none of the transformations cause duplication. E.g., swapping a filter with a sort is a good idea, but if there are two filters of the same sort and they are both swapped you will now be duplicating the sort.

Some examples of the optimizations include:

  • COUNT(SORT(x)) => COUNT(x)
  • Moving filters over projects, joins, sorts.
  • Combining adjacent projects, projects and joins.
  • Removing redundant sorts or distributes
  • Moving filters from JOINs to their inputs.
  • Combining activities e.g., CHOOSEN(SORT(x)) => TOPN(x)
  • Sometimes moving filters into IFs
  • Expanding out a field selected from a single row dataset.
  • Combine filters and projects into compound disk read operations.

Implicit project: insertImplicitProjects

ECL tends to be written as general purpose definitions which can then be combined. This can lead to potential inefficiencies - e.g., one definition may summarise some data in 20 different ways, this is then used by another definition which only uses a subset of those results. The implicit project transformer tracks the data flow at each point through the expression graph, and removes any fields that are not required.

This often works in combination with the other optimizations. For instance the constant percolation can remove the need for fields, and removing fields can sometimes allow a left outer join to be converted to a project.

Workunits

is this the correct term? Should it be a query? This should really be independent of this document...)

The code generator ultimately creates workunits. A workunit completely describes a generated query. It consists of two parts. There is an xml component - this contains the workflow information, the various execution graphs, and information about options. It also describes which inputs can be supplied to the query and what results are generated. The other part is the generated shared object compiled from the generated C++. This contains functions and classes that are used by the engines to execute the queries. Often the xml is compressed and stored as a resource within the shared object -so the shared object contains a complete workunit.

Workflow

The actions in a workunit are divided up into individual workflow items. Details of when each workflow item is executed, what its dependencies are stored in the <Workflow> section of the xml. The generated code also contains a class definition, with a method perform() which is used to execute the actions associated with a particular workflow item. (The class instances are created by calling the exported createProcess() factory function).

The generated code for an individual workflow item will typically call back into the engine at some point to execute a graph.

Graph

The activity graphs are stored in the xml. The graph contains details of which activities are required, how those activities link together, what dependencies there are between the activities. For each activity it might contain the following information:

  • A unique id.
  • The "kind" of the activity (from enum ThorActivityKind in eclhelper.hpp)
  • The ECL that created the activity.
  • Name of the original definition
  • Location (e.g., file, line number) of the original ECL.
  • Information about the record size, number of rows, sort order etc.
  • Hints which control options for a particular activity (e.g,, the number of threads to use while sorting).
  • Record counts and stats once the job has executed.

Each activity in a graph also has a corresponding helper class instance in the generated code. (The name of the class is cAc followed by the activity number, and the exported factory method is fAc followed by the activity number.) These classes implement the interfaces defined in eclhelper.hpp.

The engine uses the information from the xml to produce a graph of activities that need to be executed. It has a general purpose implementation of each activity kind, and it uses the class instance to tailor that general activity to the specific use e.g., what is the filter condition, what fields are set up, what is the sort order?

Inputs and Results

The workunit xml contains details of what inputs can be supplied when that workunit is run. These correspond to STORED definitions in the ECL. The result xml also contains the schema for the results that the workunit will generate.

Once an instance of the workunit has been run, the values of the results may be written back into dali's copy of the workunit so they can be retrieved and displayed.

Generated code

Aims for the generated C++ code:

  • Minimal include dependencies.

    Compile time is an issue - especially for small on-demand queries. To help reduce compile times (and dependencies with the rest of the system) the number of header files included by the generated code is kept to a minimum. In particular references to jlib, boost and icu are kept within the implementation of the runtime functions, and are not included in the public dependencies.

  • Thread-safe.

    It should be possible to use the members of an activity helper from multiple threads without issue. The helpers may contain some context dependent state, so different instances of the helpers are needed for concurrent use from different contexts (e.g., expansions of a graph.)

  • Concise.

    The code should be broadly readable, but the variable names etc. are chosen to generate compact code.

  • Functional.

    Generally the generated code assigns to a variable once, and doesn't modify it afterwards. Some assignments may be conditional, but once the variable is evaluated it isn't updated. (There are of course a few exceptions - e.g., dataset iterators)

Implementation details

First a few pointers to help understand the code within eclcc:

  • It makes extensive use of link counting. You need understand that concept to get very far.

  • If something is done more than once then that is generally split into a helper function.

    The helper functions aren't generally added to the corresponding interface (e.g., IHqlExpression) because the interface would become bloated. Instead they are added as global functions. The big disadvantage of this approach is they can be hard to find. Even better would be for them to be rationalised and organised into namespaces.

  • The code is generally thread-safe unless there would be a significant performance implication. In generally all the code used by the parser for creating expressions is thread safe. Expression graph transforms are thread-safe, and can execute in parallel if a constant (NUM_PARALLEL_TRANSFORMS) is increased. The data structures used to represent the generated code are NOT thread-safe.

  • Much of the code generation is structured fairly procedurally, with classes used to process the stages within it.

  • There is a giant "God" class HqlCppTranslator - which could really do with refactoring.

Parser

The eclcc parser uses the standard tools bison and flex to process the ECL and convert it to a

: expression graph. There are a couple of idiosyncrasies with the way it is implemented.

  • Macros with fully qualified scope.

    Slightly unusually macros are defined in the same way that other definitions are - in particular to can have references to macros in other modules. This means that there are references to macros within the grammar file (instead of being purely handled by a pre-processor). It also means the lexer keeps an active stack of macros being processed.

  • Attributes on operators.

    Many of the operators have optional attributes (e.g., KEEP, INNER, LOCAL, ...). If these were all reserved words it would remove a significant number of keywords from use as symbols, and could also mean that when a new attribute was added it broke existing code. To avoid this the lexer looks ahead in the parser tables (by following the potential reductions) to see if the token really could come next. If it can't then it isn't reserved as a symbol.

Generated code

As the workunit is created the code generator builds up the generated code and the xml for the workunit. Most of the xml generation is encapsulated within the IWorkUnit interface. The xml for the graphs is created in an IPropertyTree, and added to the workunit as a block.

C++ Output structures

The C++ generation is ultimately controlled by some template files (thortpl.cpp). The templates are plain text and contain references to allow named sections of code to be expanded at particular points.

The code generator builds up some structures in memory for each of those named sections. Once the generation is complete some peephole optimization is applied to the code. This structure is walked to expand each named section of code as required.

The BuildCtx class provides a cursor into that generated C++. It will either be created for a given named section, or more typically from another BuildCtx. It has methods for adding the different types of statements. Some are simple (e.g., addExpr()), whilst some create a compound statement (e.g., addFilter). The compound statements change the active selector so any new statements are added within that compound statement.

As well as building up a tree of expressions, this data structure also maintains a tree of associations. For instance when a value is evaluated and assigned to a temporary variable, the logical value is associated with that temporary. If the same expression is required later, the association is matched, and the temporary value is used instead of recalculating it. The associations are also used to track the active datasets, classes generated for row-meta information, activity classes etc. etc.

Activity Helper

Each activity in an expression graph will have an associated class generated in the C++. Each different activity kind expects a helper that implements a particular IHThorArg interface. E.g., a sort activity of kind TAKsort requires a helper that implements IHThorSortArg. The associated factory function is used to create instances of the helper class.

The generated class might take one of two forms:

  • A parameterised version of a library class. These are generated for simple helpers that don't have many variations (e.g., CLibrarySplitArg for TAKsplit), or for special cases that occur very frequently (CLibraryWorkUnitReadArg for internal results).
  • A class derived from a skeleton implementation of that helper (typically CThorXYZ implementing interface IHThorXYZ). The base class has default implementations of some of the functions, and any exceptions are implemented in the derived class.

Meta helper

This is a class that is used by the engines to encapsulate all the information about a single row -e.g., the format that each activity generates. It is an implementation of the IOutputMeta interface. It includes functions to

  • Return the size of the row.
  • Serialize and deserialize from disk.
  • Destroy and clean up row instances.
  • Convert to xml.
  • Provide information about the contained fields.

Building expressions

The same expression nodes are used for representing expressions in the generated C++ as the original ECL expression graph. It is important to keep track of whether an expression represents untranslated ECL, or the "translated" C++. For instance ECL has 1 based indexes, while C++ is zero based. If you processed the expression x[1] it might get translated to x[0] in C++. Translating it again would incorrectly refer to x[-1].

There are two key classes used while building the C++ for an ECL expression:

CHqlBoundExpr.

This represents a value that has been converted to C++. Depending on the type, one or more of the fields will be filled in.

CHqlBoundTarget.

This represents the target of an assignment -C++ variable(s) that are going to be assigned the result of evaluating an expression. It is almost always passed as a const parameter to a function because the target is well-defined and the function needs to update that target.

A C++ expression is sometimes converted back to an ECL pseudo-expression by calling getTranslatedExpr(). This creates an expression node of kind no_translated to indicate the child expression has already been converted.

Scalar expressions

The generation code for expressions has a hierarchy of calls. Each function is there to allow optimal code to be generated - e.g., not creating a temporary variable if none are required. A typical flow might be:

  • buildExpr(ctx, expr, bound).

    Evaluate the ecl expression "expr" and save the C++ representation in the class bound. This might then call through to...

  • buildTempExpr(ctx, expr, bound);

    Create a temporary variable, and evaluate expr and assign it to that temporary variable.... Which then calls.

  • buildExprAssign(ctx, target, expr);

    evaluate the expression, and ensure it is assigned to the C++ target "target".

    The default implementation might be to call buildExpr....

An operator must either be implemented in buildExpr() (calling a function called doBuildExprXXX) or in buildExprAssign() (calling a function called doBuildAssignXXX). Some operators are implemented in both places if there are different implementations that would be more efficient in each context.

Similarly there are several different assignment functions:

  • buildAssign(ctx, <ecl-target>, <ecl-value>);
  • buildExprAssign(ctx, <c++-target>, <ecl-value>);
  • assign(ctx, <C++target>, <c++source>)

The different varieties are there depending on whether the source value or targets have already been translated. (The names could be rationalised!)

Datasets

Most dataset operations are only implemented as activities (e.g., PARSE, DEDUP). If these are used within a transform/filter then eclcc will generate a call to a child query. An activity helper for the appropriate operation will then be generated.

However a subset of the dataset operations can also be evaluated inline without calling a child query. Some examples are filters, projects, and simple aggregation. It removes the overhead of the child query call in the simple cases, and often generates more concise code.

When datasets are evaluated inline there is a similar hierarchy of function calls:

  • buildDatasetAssign(ctx, target, expr);

    Evaluate the dataset expression, and assign it to the target (a builder interface). This may then call....

  • buildIterate(ctx, expr)

    Iterate through each of the rows in the dataset expression in turn. Which may then call...

  • buildDataset(ctx, expr, target, format)

    Build the entire dataset, and return it as a single value.

Some of the operations (e.g., aggregating a filtered dataset) can be done more efficiently by summing and filtering an iterator, than forcing the filtered dataset to be evaluated first.

Dataset cursors

The interface IHqlCppDatasetCursor allows the code generator to iterate through a dataset, or select a particular element from a dataset. It is used to hide the different representation of datasets, e.g.,

  • Blocked - the rows are in a contiguous block of memory appended one after another.
  • Array - the dataset is represented by an array of pointers to the individual rows.
  • Link counted - similar to array, but each element is also link counted.
  • Nested. Sometimes the cursor may iterate through multiple levels of child datasets.

Generally rows that are serialized (e.g., on disk) are in blocked format, and they are stored as link counted rows in memory.

Field access classes

The IReferenceSelector interface and the classes in hqltcppc[2] provide an interface for getting and setting values within a row of a dataset. They hide the details of the layout - e.g., csv/xml/raw data, and the details of exactly how each type is represented in the row.

Key filepos weirdness

The current implementation of keys in HPCC uses a format which uses a separate 8 byte integer field which was historically used to store the file position in the original file. Other complications are that the integer fields are stored big-endian, and signed integer values are biased.

This introduces some complication in the way indexes are handled. You will often find that the logical index definition is replaced with a physical index definition, followed by a project to convert it to the logical view. A similar process occurs for disk files to support VIRTUAL(FILEPOSITION) etc.

Source code

The following are the main directories used by the ecl compiler.


Directory Contents


rtl/eclrtpl Template text files used to generate the C++ code

rtl/include Headers that declare interfaces implemented by the generated code

common/deftype Interfaces and classes for scalar types and values.

common/workunit Code for managing the representation of a work unit.

ecl/hql Classes and interfaces for parsing and representing an ecl expression graph

ecl/hqlcpp Classes for converting an expression graph to a work unit (and C++)

ecl/eclcc The executable which ties everything together.

Challenges

From declarative to imperative

As mentioned at the start of this document, one of the main challenges with eclcc is converting the declarative ECL code into imperative C++ code. The direction we are heading in is to allow the engines to support more lazy-evaluation so possibly in this instance to evaluate it the first time it is used (although that may potentially be much less efficient). This will allow the code generator to relax some of its current assumptions.

There are several example queries which are already producing pathological behaviour from eclcc, causing it to generate C++ functions which are many thousands of lines long.

The parser

Currently the grammar for the parser is too specialised. In particular the separate productions for expression, datasets, actions cause problems - e.g., it is impossible to properly allow sets of datasets to be treated in the same way as other sets.

The semantic checking (and probably semantic interpretation) is done too early. Really the parser should build up a syntax tree, and then disambiguate it and perform the semantic checks on the syntax tree.

The function calls should probably be expanded later than they are. I have tried in the past and hit problems, but I can't remember all the details. Some are related to the semantic checking.

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/devdoc/CodeReviews.html b/devdoc/CodeReviews.html new file mode 100644 index 00000000000..0107e9f660d --- /dev/null +++ b/devdoc/CodeReviews.html @@ -0,0 +1,24 @@ + + + + + + Code Review Guidelines | HPCC Platform + + + + + + + + + + + + + +
Skip to content

Code Review Guidelines

The Code Submissions document is aimed at developers that are submitting PRs. This document describes some of the goals and expectations for code reviewers.

Review Goals

Code reviews have a few different goals:

  • Catch architectural or design problems.
    These should have been caught earlier, but better later than never...
  • Catch bugs early (incorrect behaviour, inefficiencies, security issues)
  • Ensure the code is readable and maintainable.
    This includes following the project coding standards (see Style Guide).
  • A opportunity for training/passing on information.
    For example providing information about how the current system works, functionality that is already available or suggestions of other approaches the developer may not have thought of.

It is NOT a goal to change the submission until it matches how the reviewer would have coded it.

General comments

Some general comments on code reviews:

  • Code reviewers should be explicit and clearly describe the problem.
    This should include what change is expected if not obvious. Don’t assume the contributor has same understanding/view as reviewer.
  • If a comment is not clear the contributor should ask for clarification.
    ...rather than wasting time trying to second-guess the reviewer.
  • Contributors should feel free to push back if they consider comments are too picky.
    The reviewer can either agree, or provide reasons why they consider it to be an issue.
  • The reviewer should not extend the scope of the original change.
    If the change could be extended, or only partially solves the issue, a new JIRA should be created for the extra work. If the change will introduce regressions, or fundamentally fails to solve the problem then this does not apply!
  • Clearly indicate if a review is incomplete.
    Sometimes a significant design problem means the rest of the code has not been reviewed in detail. Other times an initial review has picked up a set of issues, but the reviewer needs to go back and check other aspects in detail. If this is the case it should be explicitly noted.
  • Repeated issues.
    The reviewer is free to comment on every instance of a repeated issue, but a simple annotation should alert contributor to address appropriately eg: [Please address all instances of this issue]
  • Contributers should provide feedback to the reviewer.
    The contributor should respond to a comment if it isn't obvious where/how they have been addressed (but no need to acknowledge typo/indentation/etc)
  • Only the reviewer should mark issues as resolved using the Github resolve conversation button.
  • Code reviews should be a priority.
    Both reviewers and contributors should respond in a timely manner - don't leave it for days. It destroys the flow of thought and conversation.
  • Check all review comments have been addressed.
    If they have not been addressed you are guaranteed another review/submit cycle. In particular watch out for collapsed conversations. If there are large numbers of comments GitHub will collapse them, which can make comments easy to miss.
  • Sometimes PRs need to be restarted.
    If there are large number of comments > 100, it can be hard to track all the comments and GitHub can become unresponsive. It may be better to close the PR and open a new one.
  • Submit any changes as extra commits. This makes it clear to the reviewer what has changed, and avoids them having to re-review everything. Please do not squash them until the reviewers approve the PR. The few exceptions to this are if the PR is only a couple of lines, or the PR is completely rewritten in response to the review.
  • Reviewers use GitHub's features
    Making use of the "viewed" button can make it easier to track what has changed - or quickly remove trivial changes from view. Ignoring whitespace can often simplify comparisons - especially when code has been refactored or extra conditions or try/catch bocks have been introduced.

Strictness

All code reviews don't need to be equally strict. The "strictness" of the review should reflect the importance and location of the change. Some examples:

  • If it is closely associated with an existing file, then the indentation, style should match the surrounding code - a mixture of styles makes it much harder to read. If it is in a new, independent source file or project this is less of an issue.
  • If the code is in a core library then efficiency and edge cases will be more important.
  • If it is a core part of the system then security is key. If it is a developer only tool then edge cases are less significant.
  • Reviews of draft pull requests are likely to concentrate on the overall approach, rather than the details. They are likely to be more informal (e.g. not always using comments tags).

Checklist

What are some examples of checks to bear in mind when reviewing code?

General:

  • Is the commit title in the correct format, and understandable in a change log?
  • Is the target correct?
  • Is the size appropriate. Could it have been split up?
  • Does the jira contain details of the change, especially the reason?
  • Does it duplicate other functionality?
  • Does the style match the context and the style guide?
  • Is the design encapsulated at the right level? Too abstract or too concrete?

Content:

  • Silly mistakes - indent, typos, commented outcode, spurious changes.
  • Does it introduce any memory leaks? E.g. Correct use of linking? Are exceptions released?
  • Thread safety
    • critical sections or atomic variables if accessed by more than one thread
    • race conditions
    • deadlock
  • authorization. Should it be checked, does it fail by default?
  • Any potential for overflow or DOS? Are all user inputs validated and all lengths protected?
  • Are all secrets stored and passed securely?
  • Comments explaining why for any code that is complex or counter-intuitive.
  • Backward compatibility.
    Could this possibly cause problems if data produced with this change is used in earlier/later versions? Could there be problems if it was used in a mixed-version environment?

Comment tags

When reading comments in a review it can sometimes be hard to know why the reviewer made a comment, or what response is expected. If there is any doubt the contributor should ask. However to make it clearer we are aiming to always add a tag to the front of each review comment. The tag will give an indication of why the comment is being made, its severity and what kind of response is expected. Here is a provisional table of tags:

TagWhatWhyExpected response
design:An architectural or design issueThe reviewer considers the PR has a significant problem which will affect its functionality or future extensibilityreviewer/developer redesign expected before any further changes
scope:The scope of the PR does not match the JiraIf the scope of the fix is too large it can be hard to review, and take much longer to resolve all the issues before the PR is accepted.Discussion. Split the PR into multiple simpler PRs.
function:Incorrect/unexpected functionality implementedThe function doesn't match the description in the jira, or doesn't solve the original problemdeveloper expected to address issue (or discuss)
security:Something in the code introduces a security problemThe reviewer has spotted potential security issues e.g. injection attacksdeveloper expected to discuss the issue (and then address)
bug:A coding issue that will cause incorrect behaviourLikely to cause confusion, invalid results or crashes.developer expected to address issue
efficiency:The code works, but may have scaling or other efficiency issues.Inefficiency can cause problem in some key functions and areasdeveloper addressing the problem (or discuss)
discuss:Reviewer has thought of a potential problem, but not sure if it appliesReviewer has a concern it may be an issue, and wants to check the developer has thought about and addressed the issueDiscussion - either in the PR or offline.
style:Reviewer points out non-conforming code styleMakes the code hard to readDeveloper to fix
indent:A fairly obvious indentation issueMakes the code hard to readDeveloper to fix.
format:Any other unusual formattingMakes the code hard to readDeveloper to fix.
typo:Minor typing errorMakes something (code/message/comment) harder to readDeveloper to fix.
minor:A minor issue that could be improved.Education (the suggestion is better for a particular reason), or something simple to clean up at the same time as other changesDeveloper recommended to fix, but unlikely to stop a merge
picky:A very minor issue that could be improved, but is barely worth commenting onEducation, or something to clean up at the same time as other changesDeveloper discretion to fix, wouldn't stop a merge
future:An additional feature or functionality that fits in but should be done as a separate PR.Ensure that missing functionality is tracked, but PRs are not held up by additional requirements.Contributor to create Jira (unless trivial) and number noted in response.
question:Review has a question that they are not sure of the answer toReviewer would like clarification to help understand the code or design. The answer may lead to further comments.An answer to the question.
note:Reviewer wants to pass on some information to the contributor which they may not knowPassing on knowledge/backgroundcontributor should consider the note, but no change expected/required
personal:Reviewer has an observation based on personal experienceReviewer has comments that would improve the code, but not part of the style guide or required. E.g. patterns for guard conditionsReflect on the suggestion, but no change expected.
documentation:This change may have an impact on documentationMake sure changes can be usedContributor to create Jira describing the impact created, and number noted in response.

The comments should always be constructive. The reviewer should have a reason for each of them, and be able to articulate the reason in the comment or when asked. "I wouldn't have done it like that" is not a good enough on its own!

Similarly there is a difference in opinion within the team on some style issues - e.g. standard libraries or jlib, inline or out of line functions, nested or non-nested classes. Reviews should try and avoid commenting on these unless there is a clear reason why they are significant (functionality, efficiency, compile time) and if so spell it out. Code reviewers should discuss any style issues that they consider should be universally adopted that are not in the style guide.

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/devdoc/CodeSubmissions.html b/devdoc/CodeSubmissions.html new file mode 100644 index 00000000000..bef7b822dd9 --- /dev/null +++ b/devdoc/CodeSubmissions.html @@ -0,0 +1,24 @@ + + + + + + Code Submission Guidelines | HPCC Platform + + + + + + + + + + + + + +
Skip to content

Code Submission Guidelines

We welcome submissions to the platform especially in the form of pull requests into the HPCC-Systems github repository. The following describes some of processes for merging PRs.

Pull requests

There are a few things that should be considered when creating a PR to increase the likelihood that they can be accepted quickly.

  • Write a good commit message
    The format should be HPCC-XXXXX (where XXXXX is the bug number) followed by a description of the issue. The text should make sense in a change log by itself - without reference to the jira or the contents of the PR. We should aim to increase the information that is included as part of the commit message - not rely on on the jira.
  • Ensure the reviewer has enough information to review the change.
    The code reviewer only has the JIRA and the PR to go on. The JIRA (or associated documentation) should contain enough details to review the PR - e.g. the purpose, main aim, why the change was made etc.. If the scope of the jira has changed then the jira should be updated to reflect that.
    If the submission requires changes to the documentation then the JIRA should contain all the details needed to document it, and the PR should either contain the documentation changes, or a documentation JIRA should be created.
  • Fill in the checklist
    The check boxes are there to remind you to consider different aspects of the PR. Not all of them apply to every submission, but if you tick a box and have not really thought about the item then prepare to be embarrassed!
  • Prefer small submissions
    It isn't always possible, but several smaller PRs are much easier to review than one large change. If your submission includes semi-automatic/mechanical changes (e.g. renaming large numbers of function calls, or adding an extra parameter) please keep it as a separate commit. This makes it much easier to review the PR - since the reviewer will be looking for different errors in the different types of changes.
  • Check for silly mistakes
    Review your own code in github, after creating the PR to check for silly mistakes. It doesn't take long, and often catches trivial issues. It may avoid the need for a cycle of code-review/fixes. It may be helpful to add some notes to specific changes e.g. "this change is mainly or solely refactoring method A into method B and C. ". Some common examples of trivial issues to look for include:
    • Inconsistent indentation, or using tabs rather than spaces to indent.
    • Lines of tracing left in.
    • Lines of code commented out that should be deleted.
    • TBD reminders of work that need implementing or removing.
    • Unrelated files that have been accidentally modified.
    • Accidental changes to submodule versions.
    • Typos in error messages, tracing or comments, or in the commit message.
    • Incomplete edits when copy and pasting code.
    • Check subtractions are the right way around, and potential for overflow.
    • New files with the wrong copyright date
  • Check the target branch (see below)
  • Request one or more reviews. For relatively simple changes a single reviewer is normally enough.

Reviewers

All pull requests should be reviewed by someone who is not the author before merging. Complex changes, changes that require input from multiple experts, or that have implications throughout the system should be reviewed by multiple reviewers. This should include someone who is responsible for merging changes for that part of the system. (Unless it is a simple change written by someone with review rights.)

Contributors should use the github reviewers section on the PR to request reviews. After a contributor has pushed a set of changes in response to a review, they should refresh the github review status, so the users are notified it is ready for re-review. When the review is complete, a person with responsibility for merging changes to that part of the system should be added as a reviewer (or refreshed), with a comment that it is ready to merge.

Reviewers should check for PRs that are ready for their review via github's webpage (filter "review-requested:<reviewer-id>") or via the github CLI (e.g. gh pr status). Contributors should similarly ensure they stay up to date with any comments on requests for change on their submissions.

Target branch

The Version support document contains details of the different versions that are supported, and which version should be targetted for different kinds of changes. Occasionally earlier branches will be chosen, (e.g. security fixes to even older versions) but they should always be carefully discussed (and documented).

Changes will always be upmerged into the next point release for all the more recent major and minor versions (and master).

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/devdoc/DevDocs.html b/devdoc/DevDocs.html new file mode 100644 index 00000000000..3b20a829e25 --- /dev/null +++ b/devdoc/DevDocs.html @@ -0,0 +1,31 @@ + + + + + + Working with developer documentation | HPCC Platform + + + + + + + + + + + + + +
Skip to content

Working with developer documentation

Some basic guidlines to ensure your documentation works well with VitePress

Documentation location

Documents can be located anywhere in the repository folder structure. If it makes sense to have documentation "close" to specific components, then it can be located in the same folder as the component. For example, any developer documentation for specific plugins can be located in those folders. If this isn't appropriate then the documentation can be located in the devdoc or subfolders of devdoc.

WARNING

There is an exclusion list in the devdoc/.vitepress/config.js file that prevents certain folders from being included in the documentation. If you add a new document to a folder that is excluded, then it will not be included in the documentation. If you need to add a new document to an excluded folder, then you will need to update the exclusion list in the devdoc/.vitepress/config.js file.

Documentation format

Documentation is written in Markdown. This is a simple format that is easy to read and write. It is also easy to convert to other formats, such as HTML, PDF, and Word. Markdown is supported by many editors, including Visual Studio Code, and is supported by VitePress.

TIP

VitePress extends Markdown with some additional features, such as custom containers, it is recommended that you refer to the VitePress documentation for more details.

Rendering documentation locally with VitePress

To assist with the writing of documentation, VitePress can be used to render the documentation locally. This allows you to see how the documentation will look when it is published. To start the local development server you need to type the following commands in the root HPCC-Platform folder:

sh
npm install
+npm run docs-dev

This will start a local development server and display the URL that you can use to view the documentation. The default URL is http://localhost:5173/HPCC-Platform, but it may be different on your machine. The server will automatically reload when you make changes to the documentation.

WARNING

The first time you start the VitePress server it will take a while to complete. This is because it is locating all the markdown files in the repository and creating the html pages. Once it has completed this step once, it will be much faster to start the server again.

Adding a new document

To add a new document, you need to add a new markdown file to the repository. The file should be named appropriately and have the .md file extension. Once the file exists, you can view it by navigating to the appropriate URL. For example, if you add a new file called MyNewDocument.md to the devdoc folder, then you can view it by navigating to http://localhost:5173/HPCC-Platform/devdoc/MyNewDocument.html.

Adding a new document to the sidebar

To add a new document to the sidebar, you need to add an entry to the devdoc/.vitepress/config.js file. The entry should be added to the sidebar section. For example, to add a new document called MyNewDocument.md to the devdoc folder, you would add the following entry to the sidebar section:

js
sidebar: [
+    ...
+    {
+        text: 'My New Document',
+        link: '/devdoc/MyNewDocument'
+    }
+    ...

TIP

You can find more information on the config.js file in the VitePress documentation.

Editing the main landing page

The conent of the main landing page is located in index.md in the root folder. Its structure uses the VitePress "home" layout.

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/devdoc/Development.html b/devdoc/Development.html new file mode 100644 index 00000000000..4dee4c1816e --- /dev/null +++ b/devdoc/Development.html @@ -0,0 +1,26 @@ + + + + + + Development Guide | HPCC Platform + + + + + + + + + + + + + +
Skip to content

Development Guide

HPCC Source

The most upto date details of building the system are found on the HPCC Wiki.

Getting the sources

The HPCC Platform sources are hosted on GitHub. You can download a snapshot of any branch using the download button there, or you can set up a git clone of the repository. If you are planning to contribute changes to the system, see the CONTRIBUTORS document for information about how to set up a GitHub fork of the project through which pull-requests can be made.

Building the system from sources

Requirements

The HPCC platform requires a number of third party tools and libraries in order to build. The HPCC Wiki contains the details of the dependencies that are required for different distributions.

For building any documentation, the following are also required:

bash
sudo apt-get install docbook
+sudo apt-get install xsltproc
+sudo apt-get install fop

NOTE: Installing the above via alternative methods (i.e. from source) may place installations outside of searched paths.

Building the system

The HPCC system is built using the cross-platform build tool cmake, which is available for Windows, virtually all flavors of Linux, FreeBSD, and other platforms. You should install cmake version 2.8.3 or later before building the sources.

On some distros you will need to build cmake from sources if the version of cmake in the standard repositories for that distro is not modern enough. It is good practice in cmake to separate the build directory where objects and executables are made from the source directory, and the HPCC cmake scripts will enforce this.

To build the sources, create a directory where the built files should be located, and from that directory, run:

bash
cmake <source directory>

Depending on your operating system and the compilers installed on it, this will create a makefile, Visual Studio .sln file, or other build script for building the system. If cmake was configured to create a makefile, then you can build simply by typing:

bash
make

If a Visual Studio solution file was created, you can load it simply by typing the name: hpccsystems-platform.sln

This will load the solution in Visual Studio where you can build in the usual way.

Packaging

To make an installation package on a supported linux system, use the command:

bash
make package

This will first do a make to ensure everything is up to date, then will create the appropriate package for your operating system, Currently supported package formats are rpm (for RedHat/Centos) and .deb (for Debian and Ubuntu). If the operating system is not one of the above, or is not recognized, make package will create a tarball.

The package installation does not start the service on the machine, so if you want to give it a go or test it (see below), make sure to start the service manually and wait until all services are up (mainly wait for EclWatch to come up on port 8010).

Testing the system

After compiling, installing the package and starting the services, you can test the HPCC platform on a single-node setup.

Unit Tests

Some components have their own unit-tests. Once you have compiled (no need to start the services), you can already run them. Supposing you build a Debug version, from the build directory you can run:

bash
./Debug/bin/roxie -selftest

and:

bash
./Debug/bin/eclagent -selftest

You can also run the Dali regression self-tests:

bash
./Debug/bin/daregress localhost

Regression Tests

MORE Completely out of date - needs rewriting.

Compiler Tests

The ECLCC compiler tests rely on two distinct runs: a known good one and your test build. For normal development, you can safely assume that the OSS/master branch in github is good. For overnight testing, golden directories need to be maintained according to the test infrastructure. There are Bash (Linux) and Batch (Windows) scripts to run the regressions:

The basic idea behind this tests is to compare the output files (logs and XML files) between runs. The log files should change slightly (the comparison should be good enough to filter most irrelevant differences), but the XML files should be identical if nothing has changed. You should only see differences in the XML where you have changed in the code, or new tests were added as part of your development.

On Linux, there are two steps:

Step 1: Check-out OSS/master, compile and run the regressions to populate the 'golden' directory:

bash
./regress.sh -t golden -e buildDir/Debug/bin/eclcc

This will run the regressions in parallel, using as many CPUs as you have, and using your just-compiled ECLCC, assuming you compiled for Debug version.

Step 2: Make your changes (or check-out your branch), compile and run again, this time output to a new directory and compare to the 'golden' repo.:

bash
./regress.sh -t my_branch -c golden -e buildDir/Debug/bin/eclcc

This will run the regressions in the same way, output to 'my_branch' dir and compare it to the golden version, highlighting the differences.

NOTE: If you changed the headers that the compiled binaries will use, you must re-install the package (or provide -i option to the script to the new headers).

Step 3: Step 2 only listed the differences, now you need to see what they are. For that, re-run the regressing script omitting the compiler, since the only thing we'll do is to compare verbosely.:

bash
./regress.sh -t my_branch -c golden

This will show you all differences, using the same ignore filters as before, between your two branches. Once you're happy with the differences, commit and issue a pull-request.

TODO: Describe compiler tests on Windows.

Debugging the system

On linux systems, the makefile generated by cmake will build a specific version (debug or release) of the system depending on the options selected when cmake is first run in that directory. The default is to build a release system. In order to build a debug system instead, use command:

bash
cmake -DCMAKE_BUILD_TYPE=Debug <source directory>

You can then run make or make package in the usual way to build the system.

On a Windows system, cmake always generates s solution file with both debug and release target platforms in it, so you can select which one to build within Visual Studio.

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/devdoc/GitAuthenticate.html b/devdoc/GitAuthenticate.html new file mode 100644 index 00000000000..8c872454d08 --- /dev/null +++ b/devdoc/GitAuthenticate.html @@ -0,0 +1,35 @@ + + + + + + HPCC git support | HPCC Platform + + + + + + + + + + + + + +
Skip to content

HPCC git support

Version 8.4 of the HPCC platform allows package files to define dependencies between git repositories and also allows you to compile directly from a git repository.

E.g.

ecl run hthor --main demo.main@ghalliday/gch-ecldemo-d#version1 --server=...

There are no futher requirements if the repositories are public, but private repositories have the additional complication of supplying authentication information. Git provides various methods for providing the credentials...

Credentials for local development

The following are the recommended approaches configuring the credentials on a local development system interacting with github:

  1. ssh key.

In this scenario, the ssh key associated with the local developer machine is registered with the github account. For more details see https://docs.github.com/en/authentication/connecting-to-github-with-ssh/about-ssh

This is used when the github reference is of the form ssh://github.com. The sshkey can be protected with a passcode, and there are various options to avoid having to enter the passcode each time.

It is preferrable to use the https:// protocol instead of ssh:// for links in package-lock.json files. If ssh:// is used it requires any machine that processes the dependency to have access to a registered ssh key.

  1. github authentication

Download the GitHub command line tool (https://github.com/cli/cli). You can then use it to authenticate all git access with

gh auth login

Probably the simplest option if you are using github. More details are found at https://cli.github.com/manual/gh_auth_login

  1. Use a personal access token

These are similar to a password, but with additional restrictions on their lifetime and the resources that can be accessed.

Details on how to to create them are found : https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token

These can then be used with the various git credential caching options. E.g. see https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage

Configuring eclccserver

All of the options above are likely to involve some user interaction - passphrases for ssh keys, web interaction with github authentication, and initial entry for cached access tokens. This is problematic for eclccserver - which cannot support user interaction, and it is preferrable not to pass credentials around.

The solution is to use a personal access token securely stored as a secret. (This would generally be associated with a special service account.) This avoids the need to pass credentials and allows the keys to be rotated.

The following describes the support in the different versions:

Kubernetes

In Kubernetes you need to take the following steps:

a) add the gitUsername property to the eclccserver component in the value.yaml file:

eclccserver:
+- name: myeclccserver
+  gitUsername: ghalliday

b) add a secret to the values.yaml file, with a key that matches the username:

secrets:
+  git:
+    ghalliday: my-git-secret

note: this cannot currently use a vault - probably need to rethink that. (Possibly extract from secret and supply as an optional environment variable to be picked up by the bash script.)

c) add a secret to Kubernetes containing the personal access token:

apiVersion: v1
+kind: Secret
+metadata:
+  name: my-git-secret
+type: Opaque
+stringData:
+  password: ghp_eZLHeuoHxxxxxxxxxxxxxxxxxxxxol3986sS=
kubectl apply -f ~/dev/hpcc/helm/secrets/my-git-secret

When a query is submitted to eclccserver, any git repositories are accessed using the user name and password.

Bare-metal

Bare-metal require some similar configuration steps:

a) Define the environment variable HPCC_GIT_USERNAME

export HPCC_GIT_USERNAME=ghalliday

b) Store the access token in /opt/HPCCSystems/secrets/git/$HPCC_GIT_USERNAME/password

E.g.

$cat /opt/HPCCSystems/secrets/git/ghalliday/password
+ghp_eZLHeuoHxxxxxxxxxxxxxxxxxxxxol3986sS=

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/devdoc/LDAPSecurityManager.html b/devdoc/LDAPSecurityManager.html new file mode 100644 index 00000000000..da634e04848 --- /dev/null +++ b/devdoc/LDAPSecurityManager.html @@ -0,0 +1,24 @@ + + + + + + LDAP Security Manager Init | HPCC Platform + + + + + + + + + + + + + +
Skip to content

LDAP Security Manager Init

This document covers the main steps taken by the LDAP Security Manager during initialization. It is important to note that the LDAP Security Manager uses the LDAP protocol to access an Active Directory, AD. The AD is the store for users, groups, permissions, resources, and more. The term LDAP is generally overused to refer to both.

LDAP Instances

Each service and/or component using the LDAP security manager gets its own instance of the security manager. This includes a unique connection pool (see below). All operations described apply to each LDAP instance.

Initialization Steps

The following sections cover the main steps taken during initialization

Load Configuration

The following items are loaded from the configuration:

AD Hosts

The LDAP Security Manager supports using multiple ADs. The FQDN or IP address of each AD host is read from configuration data and stored internally. The source is a comma separated list stored in the ldapAddress config value. Each entry is added to a pool of ADs.

Note that all ADs are expected to use the same credentials and have the same configuration

AD Credentials

AD credentials consist of a username and password. The LDAP security manager users these to perform all operations on behalf of users and components in the cluster. There are three potential sources for credentials.

  1. A Kubernetes secret. If the ldapAdminSecretKey config value is set, but ldapAdminVaultId is not (see 2) then the AD credentials are retrieved as Kubernetes secrets.
  2. If both ldapAdminSecretKey and ldapAdminVaultId config values are present, the AD credentials are retrieved from the vault.
  3. Hardcoded values from the systemCommonName and systemPassword config values stored in the environment.xml file.

As stated above, when multiple ADs are in use, the configuration of each must be the same. This includes credentials.

Retrieve Server Information from the AD

During initialization, the security manager begins incrementing through the set of defined ADs until it is able to connect and retrieve information from an AD. Once retrieved, the information is used for all ADs (see statement above about all ADs being the same). The accessed AD is marked as the current AD and no other ADs are accessed during initialization.

The retrieved information is used to verify the AD type so the security manager adjusts for variations between types. Additionally, defined DNs may be adjusted to match AD type requirements.

Connections

The manager handles connections to an AD in order to perform required operations. It is possible that values such as permissions and resources may be cached to improve performance.

Connection Pool

The LDAP security manager maintains a pool of LDAP connections. The pool is limited in size to maxConnections from the configuration. The connection pool starts empty. As connections are created, each is added to the pool until the max allowed is reached.

The following process is used when an LDAP connection is needed.

First, the connection pool is searched for a free connection. If found and valid, the connection is returned. A connection is considered free if no one is using it and valid if the AD can be accessed. If no valid free connections are found, a new uninitialized connection is created.

For a new connection, an attempt is made to connect to each AD starting with the current. See Handling AD Hosts below for how ADs are cycled when a connection fails. For each AD, as it cycles through, connection attempts are retried with a short delay between each. If unable to connect, the AD host is marked rejected and the next is attempted. Once a new connection has been established, if the max number of connections has not been reached yet, the connection is added to the pool.

It is important to note that if the pool has reached its max size, new connections will continue to be made, but are not saved in the pool. This allows the pool to maintain a steady working state, but allow for higher demand. Connections not saved to the pool are deleted once no longer in use.

Handling AD Hosts

The manager keeps a list of AD hosts and the index of the current host. The current host is used for all AD operations until there is a failure. At that time the manager marks the host as "rejected" and moves to the next host using a round-robin scheme.

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/devdoc/MemoryManager.html b/devdoc/MemoryManager.html new file mode 100644 index 00000000000..3ed4d9e58f3 --- /dev/null +++ b/devdoc/MemoryManager.html @@ -0,0 +1,24 @@ + + + + + + The Roxie Memory Manager | HPCC Platform + + + + + + + + + + + + + +
Skip to content

Introduction

This memory manager started life as the memory manager which was only used for the Roxie engine. It had several original design goals:

  • Support link counted rows. (When the last reference is released the row is freed.)
  • Be as fast as possible on allocate and deallocate of small rows.
  • Allow rows serialized from slaves to be used directly without being cloned first.
  • Allow the memory used by a single query, or by all queries combined, to be limited, with graceful recovery.
  • Isolate roxie queries from one another, so that one query can't bring down all the rest by allocating too much memory.
  • Guarantee all the memory used by a query is freed when the query finishes, reducing the possibility of memory leaks.
  • Predictable behaviour with no pathogenic cases.

(Note that efficient usage of memory does not appear on that list - the expectation when the memory manager was first designed was that Roxie queries would use minimal amounts of memory and speed was more important. Some subsequent changes e.g., Packed heaps, and configurable bucket sizes help mitigate that.)

Main Structure

The basic design is to reserve (but not commit) a single large block of memory in the virtual address space. This memory is subdivided into "pages". (These are not the same as the os virtual memory pages. The memory manager pages are currently defined as 1Mb in size.)

The page bitmap

The system uses a bitmap to indicate whether each page from the global memory has been allocated. All active IRowManager instances allocate pages from the same global memory space. To help reduce fragmentation allocations for single pages are fulfilled from one end of the address space, while allocations for multiple pages are fulfilled from the other.

IRowManager

This provides the primary interface for allocating memory. The size of a requested allocation is rounded up to the next "bucket" size, and the allocation is then satisfied by the heap associated with that bucket size. Different engines can specify different bucket sizes - an optional list is provided to setTotalMemoryLimit. Roxie tends to use fewer buckets to help reduce the number of active heaps. Thor uses larger numbers since it is more important to minimize the memory wasted.

Roxie uses a separate instance of IRowManager for each query. This provides the mechanism for limiting how much memory a query uses. Thor uses a single instance of an IRowManager for each slave/master.

Heaps

Memory is allocated from a set of "heaps - where each heap allocates blocks of memory of a single size. The heap exclusively owns a set of heaplet (each 1 page in size), which are held in a doubly linked list, and sub allocates memory from those heaplets.

Information about each heaplet is stored in the base of the page (using a class with virtual functions) and the address of an allocated row is masked to determine which heap object it belongs to, and how it should be linked/released etc. Any pointer not in the allocated virtual address (e.g., constant data) can be linked/released with no effect.

Each heaplet contains a high water mark of the address within the page that has already been allocated (freeBase), and a lockless singly-linked list of rows which have been released (r_block). Releasing a row is non-blocking and does not involve any spin locks or critical sections. However, this means that empty pages need to be returned to the global memory pool at another time. (This is done in releaseEmptyPages()).

When the last row in a page is released a flag (possibleEmptyPages) is set in its associated heap. * This is checked before trying to free pages from a particular heap, avoiding waiting on a lock and traversing a candidate list.

Any page which might contain some spare memory is added to a lockless spare memory linked list. * Items are popped from this list when a heap fails to allocate memory from the current heaplet. Each item is checked in turn if it has space before allocating a new heaplet. * The list is also walked when checking to see which pages can be returned to the global memory. The doubly linked heaplet list allows efficient freeing.

Each allocation has a link count and an allocator id associated with it. The allocator id represents the type of the row, and is used to determine what destructor needs to be called when the row is destroyed. (The count for a row also contains a flag in the top bit to indicate if it is fully constructed, and therefore valid for the destructor to be called.)

Huge Heap

A specialized heap is used to manage all allocations that require more than one page of memory. These allocations are not held on a free list when they are released, but each is returned directly to the global memory pool. Allocations in the huge heap can be expanded and shrunk using the resizeRow() functions - see below.

Specialised Heaps:

Packed

By default a fixed size heaps rounds the requested allocation size up to the next bucket size. A packed heap changes this behaviour and it is rounded up to the next 4 byte boundary instead. This reduces the amount of memory wasted for each row, but potentially increases the number of distinct heaps.

Unique

By default all fixed size heaps of the same size are shared. This reduces the memory consumption, but if the heap is used by multiple threads it can cause significant contention. If a unique heap is specified then it will not be shared with any other requests. Unique heaps store information about the type of each row in the heaplet header, rather than per row - which reduces the allocation overhead for each row. (Note to self: Is there ever any advantage having a heap that is unique but not packed??)

Blocked

Blocked is an option on createFixedRowHeap() to allocate multiple rows from the heaplet, and then return the additional rows on subsequent calls. It is likely to reduce the average number of atomic operations required for each row being allocated, but the heap that is returned can only be used from a single thread because it is not thread safe.

Scanning

By default the heaplets use a lock free singly linked list to keep track of rows that have been freed. This requires an atomic operation for each allocation and for each free. The scanning allocator uses an alternative approach. When a row is freed the row header is marked, and a row is allocated by scanning through the heaplet for rows that have been marked as free. Scanning uses atomic store and get, rather than more expensive synchronized atomic operations, so is generally faster than the linked list - provided a free row is found fairly quickly.

The scanning heaps have better best-case performance, but worse worse-case performance (if large numbers of rows need to be scanned before a free row is found). The best-case tends to be true if only one thread/activity is accessing a particular heap, and the worse-case if multiple activities are accessing a heap, particularly if the rows are being buffered. It is the default for thor which tends to have few active allocators, but not for roxie, which tends to have large numbers of allocators.

Delay Release

This is another varation on the scanning allocator, which further reduces the number of atomic operations. Usually when a row is allocated the link count on the heaplet is increased, and when it is freed the link count is decremented. This option delays decrementing the link count when the row is released, by marking the row with a different free flag. If it is subsequently reallocated there is no need to increment the link count. The downside is that it is more expensive to check whether a heaplet is completely empty (since you can no longer rely on the heaplet linkcount alone).

Dynamic Spilling

Thor has additional requirements to roxie. In roxie, if a query exceeds its memory requirements then it is terminated. Thor needs to be able to spill rows and other memory to disk and continue. This is achieved by allowing any process that stores buffered rows to register a callback with the memory manager. When more memory is required these callbacks are called to free up memory, and allow the job to continue.

Each callback can specify a priority - lower priority callbacks are called first since they are assumed to have a lower cost associated with spilling. When more memory is required the callbacks are called in priority order until one of them succeeds. The can also be passed a flag to indicate it is critical to force them to free up as much memory as possible.

Complications

There are several different complications involved with the memory spilling:

  • There will be many different threads allocating rows.
  • Callbacks could be triggered at any time.
  • There is a large scope for deadlock between the callbacks and allocations.
  • It may be better to not resize a large array if rows had to be evicted to resize it.
  • Filtered record streams can cause significant wasted space in the memory blocks.
  • Resizing a multi-page allocation is non trivial.

Callback Rules

Some rules to follow when implementing callbacks:

  • A callback cannot allocate any memory from the memory manager. If it does it is likely to deadlock.

  • You cannot allocate memory while holding a lock if that lock is also required by a callback.

    Again this will cause deadlock. If it proves impossible you can use a try-lock primitive in the callback, but it means you won't be able to spill those rows.

  • If the heaps are fragmented it may be more efficient to repack the heaps than spill to disk.

  • If you're resizing a potentially big block of memory use the resize function with the callback.

Resizing Large memory blocks

Some of the memory allocations cover more than one "page" - e.g., arrays used to store blocks of rows. (These are called huge pages internally, not to be confused with operating system support for huge pages...) When one of these memory blocks needs to be expanded you need to be careful:

  • Allocating a new page, copying, updating the pointer (within a cs) and then freeing is safe. Unfortunately it may involve copying a large chunk of memory. It may also fail if there isn't memory for the new and old block, even if the existing block could have been expanded into an adjacent block.
  • You can't lock, call a resize routine and update the pointer because the resize routine may need to allocate a new memory block- that may trigger a callback, which could in turn deadlock trying to gain the lock. (The callback may be from another thread...)
  • Therefore the memory manager contains a call which allows you to resize a block, but with a callback which is used to atomically update the pointer so it always remains thread safe.

Compacting heaps

Occasionally you have processes which read a large number of rows and then filter them so only a few are still held in memory. Rows tend to be allocated in sequence through the heap pages, which can mean those few remaining rows are scattered over many pages. If they could all be moved to a single page it would free up a significant amount of memory.

The memory manager contains a function to pack a set of rows into a smaller number of pages: IRowManager->compactRows().

This works by iterating through each of the rows in a list. If the row belongs to a heap that could be compacted, and isn't part of a full heaplet, then the row is moved. Since subsequent rows tend to be allocated from the same heaplet this has the effect of compacting the rows.

Shared Memory

Much of the time Thor doesn't uses full memory available to it. If you are running multiple Thor processes on the same machine you may want to configure the system so that each Thor has a private block of memory, but there is also a shared block of memory which can be used by whichever process needs it.

The ILargeMemCallback provides a mechanism to dynamically allocate more memory to a process as it requires it. This could potentially be done in stages rather than all or nothing.

(Currently unused as far as I know... the main problem is that borrowing memory needs to be coordinated.)

Huge pages

When OS processes use a large amount of memory, mapping virtual addresses to physical addresses can begin to take a significant proportion of the execution time. This is especially true once the TLB is not large enough to store all the mappings. Huge pages can significantly help with this problem by reducing the number of TLB entries needed to cover the virtual address space. The memory manager supports huge pages in two different ways:

Huge pages can be preallocated (e.g., with hugeadm) for exclusive use as huge pages. If huge pages are enabled for a particular engine, and sufficient huge pages are available to supply the memory for the memory manager, then they will be used.

Linux kernels from 2.6.38 onward have support for transparent huge pages. These do not need to be preallocated, instead the operating system tries to use them behind the scenes. HPCC version 5.2 and following takes advantage of this feature to significantly speed memory access up when large amounts of memory are used by each process.

Preallocated huge pages tend to be more efficient, but they have the disadvantage that the operating system currently does not reuse unused huge pages for other purposes e.g., disk cache.

There is also a memory manager option to not return the memory to the operating system when it is no longer required. This has the advantage of not clearing the memory whenever it is required again, but the same disadvantage as preallocated huge pages that the unused memory cannot be used for disk cache. We recommend this option is selected when preallocated huge pages are in use - until the kernel allows them to be reused.

Global memory and channels

Changes in 6.x allow Thor to run multiple channels within the same process. This allows data that is constant for all channels to be shared between all slave channels - a prime example is the rhs of a lookup join. For the queries to run efficiently the memory manager needs to ensure that each slave channel has the same amount of memory - especially when memory is being used that is shared between them.

createGlobalRowManager() allows a single global row manager to be created which also provides slave row managers for the different channels via the querySlaveRowManager(unsigned slave) method.

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/devdoc/Metrics.html b/devdoc/Metrics.html new file mode 100644 index 00000000000..205fd4e6867 --- /dev/null +++ b/devdoc/Metrics.html @@ -0,0 +1,40 @@ + + + + + + Metrics Framework Design | HPCC Platform + + + + + + + + + + + + + +
Skip to content

Metrics Framework Design

Introduction

This document describes the design of a metrics framework that allows HPCC Systems components to implement a metric collection strategy. Metrics provide the following functionality:

  • Alerts and monitoring

    An important DevOps function is to monitor the cluster and providing alerts when problems are detected. Aggregated metric values from multiple sources provide the necessary data to build a complete picture of cluster health that drives monitoring and alerts.

  • Scaling

    As described above, aggregated metric data is also used to dynamically respond to changing cluster demands and load. Metrics provide the monitoring capability to react and take action

  • Fault diagnosis and resource monitoring

    Metrics provide historical data useful in diagnosing problems by profiling how demand and usage patterns may change prior to a fault. Predictive analysis can also be applied.

  • Analysis of jobs/workunits and profiling

    With proper instrumentation, a robust dynamic metric strategy can track workunit processing. Internal problems with queries should be diagnosed from deep drill down logging.

The document consists of several sections in order to provide requirements as well as the design of framework components.

Definitions

Some definitions are useful.

Metric

: A measurement defined by a component that represents an internal state that is useful in a system reliability engineering function. In the context of the framework, a metric is an object representing the above.

Metric Value

: The current value of a metric.

Metric Updating

: The component task of updating metric state.

Collection

: A framework process of selecting relevant metrics based on configuration and then retrieving their values.

Reporting

: A framework process of converting values obtained during a collection into a format suitable for ingestion by a collection system.

Trigger

: What causes the collection of metric values.

Collection System

: The store for metric values generated during the reporting framework process.

Use Scenarios

This section describes how components expect to use the framework. It is not a complete list of all requirements but rather a sample.

Roxie

Roxie desires to keep a count of many different internal values. Some examples are

  • Disk type operations such as seeks and reads

  • Execution totals

    Need to track items such as total numbers of items such as success and failures as well as breaking some counts into individual reasons. For example, failures may need be categorized such as as

    • Busy
    • Timeout
    • Bad input

    Or even by priority (high, low, sla, etc.)

  • Current operational levels such as the length of internal queues

  • The latency of operations such as queue results, agent responses, and gateway responses

Roxie also has the need to track internal memory usage beyond the pod/system level capabilities. Tracking the state of its large fixed memory pool is necessary.

The Roxie buddy system also must track how often and who is completing requests. The "I Beat You To It" set of metrics must be collected and exposed in order to detect pending node failure. While specific action on these counts is not known up front, it appears clear that these values are useful and should be collected.

There does not appear to be a need for creating and destroying metrics dynamically. The set of metrics is most likely to be created at startup and remain active through the life of the Roxie. If, however, stats collection seeps into the metrics framework, dynamic creation and destruction of stats metrics is a likely requirement.

ESP

There are some interesting decisions with respect to ESP and collection of metrics. Different applications within ESP present different use cases for collection. Ownership of a given task drives some of these use cases. Take workunit queues. If ownership of the task, with respect to metrics, is WsWorkunits, then use cases are centric to that component. However, if agents listening on the queue are to report metrics, then a different set of use cases emerge. It is clear that additional work is needed to generate clear ownership of metrics gathered by ESP and/or the tasks it performs.

ESP needs to report the activeTransactions value from the TxSummary class(es). This gives an indication of how busy the ESP is in terms of client requests.

Direct measurement of response time in requests may not be useful since the type of request causes different execution paths within ESP that are expected to take widely varying amounts of time. Creation of metrics for each method is not recommended. However, two possible solutions are to a) create a metric for request types, or b) use a histogram to measure response time ranges. Another option mentioned redefines the meaning of a bucket in a histogram. Instead of a numeric distribution, each bucket represents a unique subtask within an overall "metric" representing a measured operation. This should be explored whether for operational or developmental purposes.

For tracking specific queries and their health, the feeling is that logging can accomplish this better than metrics since the list of queries to monitor will vary between clusters. Additionally, operational metrics solving the cases mentioned above will give a view into the overall health of ESP which will affect the execution of queries. Depending on actions taken by these metrics, scaling may solve overload conditions to keep cluster responsiveness acceptable.

For Roxie a workunit operates as a service. Measuring service performance using a histogram to capture response times as a distribution may be appropriate. Extracting the 95th percentile of response time may be useful as well.

There are currently no use cases requiring consistency between values of different metrics.

At this time the only concrete metric identified is the number of requests received. As the framework design progresses and ESP is instrumented, the list will grow.

Dali Use Cases

From information gathered, Dali plans to keep counts and rates for many of the items it manages.

Framework Design

This section covers the design and architecture of the framework. It discusses the main areas of the design, the interactions between each area, and an overall process model of how the framework operates.

The framework consists of three major areas: metrics, sinks, and the glue logic. These areas work together with the platform and the component to provide a reusable metrics collection function.

Metrics represent the quantifiable component state measurements used to track and assess the status of the component. Metrics are typically scalar values that are easily aggregated by a collection system. Aggregated values provide the necessary input to take component and cluster actions such as scaling up and down. The component is responsible for creating metrics and instrumenting the code. The framework provides the support for collecting and reporting the values. Metrics provide the following:

  • Simple methods for the component to update the metric
  • Simple methods for the framework to retrieve metric value(s)
  • Handling of all synchronization between updating and retrieving metric values

In addition, the framework provides the support for retrieving values so that the component does not participate in metric reporting. The component simply creates the metrics it needs, then instruments the component to update the metric whenever its state changes. For example, the component may create a metric that counts the total number of requests received. Then, wherever the component receives a request, a corresponding update to the count is added. Nowhere in the component is any code added to retrieve the count as that is handled by the framework.

Sinks provide a pluggable interface to hide the specifics of collection systems so that the metrics framework is independent of those dependencies. Sinks:

  • Operate independently of other sinks in the system
  • Convert metric native values into collection system specific measurements and reports
  • Drive the collection and reporting processes

The third area of the framework is the glue logic, referred to as the MetricsManager. It manages the metrics system for the component. It provides the following:

  • Handles framework initialization
  • Loads sinks as required
  • Manages the list of metrics for the component
  • Handles collection and reporting with a set of convenience methods used by sinks

The framework is designed to be instantiated into a component as part of its process and address space. All objects instantiated as part of the framework are owned by the component and are not shareable with any other component whether local or remote. Any coordination or consistency requirements that may arise in the implementation of a sink shall be the sole responsibility of the sink.

Framework Implementation

The framework is implemented within jlib. The following sections describe each area of the framework.

Metrics

Components use metrics to measure their internal state. Metrics can represent everything from the number of requests received to the average length some value remains cached. Components are responsible for creating and updating metrics for each measured state. The framework shall provide a set of metrics designed to cover the majority of component measurement requirements. All metrics share a common interface to allow the framework to manage them in a common way.

To meet the requirement to manage metrics independent of the underlying metric state, all metrics implement a common interface. All metrics then add their specific methods to update and retrieve internal state. Generally the component uses the update method(s) to update state and the framework uses retrieval methods to get current state when reporting. The metric insures synchronized access.

For components that already have an implementation that tracks a metric, the framework provides a way to instantiate a custom metric. The custom metric allows the component to leverage the existing implementation and give the framework access to the metric value for collection and reporting. Note that custom metrics only support simple scalar metrics such as a counter or a gauge.

Sinks

The framework defines a sink interface to support the different requirements of collection systems. Examples of collection systems are Prometheus, Datadog, and Elasticsearch. Each has different requirements for how and when measurements are ingested. The following are examples of different collection system requirements:

  • Polled vs Periodic
  • Single measurement vs multiple reports
  • Report format (JSON, text, etc.)
  • Push vs Pull

Sinks are responsible for two main functions: initiating a collection and reporting measurements to the collection system. The Metrics Reporter provides the support to complete these functions.

The sink encapsulates all of the collection system requirements providing a pluggable architecture that isolates components from these differences. The framework supports multiple sinks concurrently, each operating independently.

Instrumented components are not aware of the sink or sinks in use. Sinks can be changed without requiring changes to a component. Therefore, components are independent of the collection system(s) in use.

Metrics Reporter

The metrics reporter class provides all of the common functions to bind together the component, the metrics it creates, and the sinks to which measurements are reported. It is responsible for the following:

  • Initialization of the framework
  • Managing the metrics created by the component
  • Handling collection and reporting as directed by configured sinks

Metrics Implementations

The sections that follow discuss metric implementations.

Counter Metric

A counter metric is a monotonically increasing value that "counts" the total occurrences of some event. Examples include the number of requests received, or the number of cache misses. Once created, the component instruments the code with updates to the count whenever appropriate.

Gauge Metric

A gauge metric is a continuously updated value representing the current state of an interesting value in the component. For example, the amount of memory used in an internal buffer, or the number of requests waiting on a queue. A gauge metric may increase or decrease in value as needed. Reading the value of a gauge is a stateless operation in that there are no dependencies on the previous reading. The value returned shall always be the current state.

Once created, the component shall update the gauge anytime the state of what is measured is updated. The metric shall provide methods to increase and decrease the value. The sink reads the value during collection and reporting.

Custom Metric

A custom metric is a class that allows a component to leverage existing metrics. The component creates an instance of a custom metric (a templated class) and passes a reference to the underlying metric value. When collection is performed, the custom metric simply reads the value of the metric using the reference provided during construction. The component maintains full responsibility for updating the metric value as the custom metric class provides no update methods. The component is also responsible for ensuring atomic access to the value if necessary.

Histogram Metric

Records counts of measurements according to defined bucket limits. When created, the caller defines as set of bucket limits. During event recording, the component records measurements. The metric separates each recorded measurement into its bucket by testing the measurement value against each bucket limit using a less than or equal test. Each bucket contains a count of measurements meeting that criteria. Additionally, the metric maintains a default bucket for measurements outside of the maximum bucket limit. This is sometimes known as the "inf" bucket.

Some storage systems, such as Prometheus, require each bucket to accumulate its measurements with the previous bucket(s). It is the responsibility of the sink to accumulate values as needed.

Scaled Histogram Metric

A histogram metric that allows setting the bucket limit units in one domain, but take measurements in another domain. For example, the bucket limits may represent millisecond durations, yet it is more effecient to use execution cycles to take the measurements. A scaled histogram converts from the the measurement domain (cycles) to the limit units domain using a scale factor provided at initialization. All conversions are encapsulated in the scaled histogram class such that no external scaling is required by any consumer such as a sink.

Configuration

This section discusses configuration. Since Helm charts are capable of combining configuration data at a global level into a component's specific configuration, The combined configuration takes the form as shown below. Note that as the design progresses it is expected that there will be additions.

yaml
    component:
+      metrics:
+        sinks:
+        - type: <sink_type>
+          name: <sink name>
+          settings:
+            sink_setting1: sink_setting_value1
+            sink_setting2: sink_setting_value2

Where (based on being a child of the current component):

metrics

: Metrics configuration for the component

metrics.sinks

: List of sinks defined for the component (may have been combined with global config)

metrics.sinks[].type

: The type for the sink. The type is substituted into the following pattern to determine the lib to load: libhpccmetrics<type><shared_object_extension>

metrics.sinks[].name

: A name for the sink.

metrics.sinks[].settings

: A set of key/value pairs passed to the sink when initialized. It should contain information necessary for the operation of the sink. Nested YML is supported. Example settings are the prometheus server name, or the collection period for a periodic sink.

Metric Naming

Metric names shall follow a convention as outlined in this section. Because different collection systems have different requirements for how metric value reports are generated, naming is split into two parts.

First, each metric is given a base name that describes what the underlying value is. Second, meta data is assigned to each metric to further qualify the value. For example, a set of metrics may count the number of requests a component has received. Each metric would have the same base name, but meta data would separate types of request (GET vs POST), or disposition such as pass or fail.

Base Name

The following convention defines how metric names are formed:

  • Names consist of parts separated by a period (.)

  • Each part shall use snake case (allows for compound names in each part)

  • Each name shall begin with a prefix representing the scop of the metric

  • Names for metric types shall be named as follows (followed by examples):

    Gauges: <scope>.<plural-noun>.<state> esp.requests.waiting, esp.status_requests.waiting

    Counters: <scope>.<plural-noun>.<past-tense-verb> thor.requests.failed, esp.gateway_requests.queued

    Time: <scope>.<singular-noun>.<state or active-verb>.time dali.request.blocked.time, dali.request.process.time

Meta Data

Meta data further qualifies a metric value. This allows metrics to have the same name, but different scopes or categories. Generally, meta data is only used to furher qualify metrics that would have the same base name, but need further distinction. An example best describes a use case for meta data. Consider a component that accepts HTTP requests, but needs to track GET and POST requests separately. Instead of defining metrics with names post_requests.received and get_requests.received, the component creates two metrics with the base name requests.received and attaches meta data describing the request type of POST to one and GET to the other.

Use of meta data allows aggregating both types of requests into a single combined count of received requests while allowing a breakdown by type.

Meta data is represented as a key/value pair and is attached to the metric by the component during metric creation. The sink is responsible for converting meta data into useful information for the collection system during reporting.

The Component Instrumentation section covers how meta data is added to a metric.

Component Instrumentation

In order to instrument a component for metrics using the framework, a component must include the metrics header from jlib (jmetrics.hpp) and add jlib as a dependent lib (if not already doing so).

The general steps for instrumentation are

  1. Create a metrics reporter object
  2. Create metric objects for each internal state to measure and add each to the reporter
  3. Add updates to each metric throughout the component wherever metric state changes

The metrics reporter is a singleton created using the platform defined singleton pattern template. The component must obtain a reference to the reporter. Use the following example:

cpp
using namespace hpccMetrics;
+MetricsManager &metricsManager = queryMetricsManager();

Metrics are wrapped by a standard C++ shared pointer. The component is responsible for maintaining a reference to each shared pointer during the lifetime of the metric. The framework keeps a weak pointer to each metric and thus does not maintain a reference. The following is an example of creating a counter metric and adding it to the reporter. The using namespace eliminates the need to prefix all metrics types with hpccMetrics. Its use is assumed for all code examples that follow.

cpp
std::shared_ptr<CounterMetric> pCounter = std::make_shared<CounterMetric>("metricName", "description");
+metricsManager.add(pCounter);

Note the metric type for both the shared pointer variable and in the make_shared template that creates the metric and returns a shared pointer. Simply substitute other metric types and handle any differences in the constructor arguments as needed.

Once created, add updates to the metric state throughout the component code where required. Using the above example, the following line of code increments the counter metric by 1.

cpp
pCounter->inc(1);

Note that only a single line of code is required to update the metric.

That's it! There are no component requirements related to collection or reporting of metric values. That is handled by the framework and loaded sinks.

For convenience, there are function templates that handle creating the reporter, creating a metric, and adding the metric to the reporter. For example, the above three lines of code that created the reporter, a metric, and added it, can be replaced by the following:

auto pCount = createMetricAndAddToManager<CounterMetric>("metricName", "description");
+

For convenience a similar function template exists for creating custom metrics. For a custom metric the framework must know the metric type and have a reference to the underlying state variable. The following template function handles creating a custom metric and adding it to the reporter (which is created if needed as well):

auto pCustomMetric = createCustomMetricAndAddToManager("customName", "description", metricType, value);
+

Where:

  • metricType

    A defined metric type as defined by the MetricType enum.

  • value

    A reference to the underlying event state which must be a scalar value convertable to a 64bit unsigned integer (__uint64)

Adding Metric Meta Data

A component, depending on requirements, may attach meta data to further qualify created metrics. Meta data takes the form of key value pairs. The base metric class MetricBase constructor defines a parameter for a vector of meta data. Metric subclasses also define meta data as a constructor parameter, however an empty vector is the default. The IMetric interface defines a method for retrieving the meta data.

Meta data is order dependent.

Below are two examples of constructing a metric with meta data. One creates the vector and passes it as a parameter, the other constructs the vector in place.

cpp
MetricMetaData metaData1{{"key1", "value1"}};
+std::shared_ptr<CounterMetric> pCounter1 =
+    std::make_shared<CounterMetric>("requests.completed", "description", SMeasureCount, metaData1);
+
+std::shared_ptr<CounterMetric> pCounter2 =
+    std::make_shared<CounterMetric>("requests.completed", "description", SMeasureCount, MetricMetaData{{"key1", "value2"}});

Metric Units

Metric units are treated separately from the base name and meta data. The reason is to allow the sink to translate based on collection system requirements. The base framework provides a convenience method for converting units into a string. However, the sink is free to do any conversions, both actual units and the string representation, as needed.

Metric units are defined using a subset of the StaticsMeasure enumeration values defined in jstatscodes.h. The current values are used:

  • SMeasureTimeNs - A time measurement in nanoseconds
  • SMeasureCount - A count of events
  • SMeasureSize - Size in bytes

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/devdoc/NewFileProcessing.html b/devdoc/NewFileProcessing.html new file mode 100644 index 00000000000..300032cafc6 --- /dev/null +++ b/devdoc/NewFileProcessing.html @@ -0,0 +1,37 @@ + + + + + + Storage planes | HPCC Platform + + + + + + + + + + + + + +
Skip to content

Documentation about the new file work.

YAML files. The following are the YAML definitions which are used to serialize file information from dali/external store to the engines and if necessary to the worker nodes.

Storage planes

This is already covered in the deployed helm charts. It has been extended and rationalized slightly.

storage:

: hostGroups: - name: <required> hosts: [ .... ] - name: <required> hostGroup: <name> count: <unsigned:#hosts> # how many hosts within the host group are used ?(default is number of hosts) offset: <unsigned:0> # index of first host included in the derived group delta: <unsigned:0> # first host within the range[offset..offset+count-1] in the derived group

planes:
+
+:   name: \<required\> prefix: \<path\> \# Root directory for
+    accessing the plane (if pvc defined), or url to access plane.
+    numDevices: 1 \# number of devices that are part of the plane
+    hostGroup: \<name\> \# Name of the host group for bare metal
+    hosts: \[ host-names \] \# A list of host names for bare metal
+    secret: \<secret-id\> \# what secret is required to access the
+    files. options: \# not sure if it is needed
+

Changes: * The replication information has been removed from the storage plane. It will now be specified on the thor instance indicating where (if anywhere) files are replicated. * The hash character (#) in a prefix or a secret name will be substituted with the device number. This replaces the old includeDeviceInPath property. This allows more flexible device substition for both local mounts and storage accounts. The number of hashes provides the default padding for the device number. (Existing Helm charts will need to be updated to follow these new rules.) * Neither thor or roxie replication is special cased. They are represented as multiple locations that the file lives (see examples below). Existing baremetal environments would be mapped to this new representation with implicit replication planes. (It is worth checking the mapping to roxie is fine.)

Files

file: - name: <logical-file-name> format: <type> # e.g. flat, csv, xml, key, parquet meta: <binary> # (opt) format of the file, (serialized hpcc field information). metaCrc: <unsigned> # hash of the meta numParts # How many file parts. singlePartNoSuffix: <boolean> # Does a single part file include .part_1_of_1? numRows: # total number of rows in the file (if known) rawSize: # total uncompressed size diskSize # is this useful? when binary copying? planes: [] # list of storage planes that the file is stored on. tlk: # ???Should the tlk be stored in the meta and returned? splitType: <split-format> # Are there associated split points, and if so what format? (And if so what variant?)

#options relating to the format of the input file:

: grouped: <boolean> # is the file grouped? compressed: <boolean> blockCompressed: <boolean> formatOptions: # Any options that relate to the file format e.g. csvTerminator. These are nested because they can be completely free format recordSize: # if a fixed size record. Not really sure it is useful

part: \# optional information about each of the file parts (Cannot
+implement virtual file position without this) - numRows: \<count\>
+\# number of rows in the file part rawSize: \<size\> \# uncompressed
+size of the file part diskSize: \<size\> \# size of the part on disk
+

# extra fields that are used to return information from the file lookup service

missing: <boolean> # true if the file could not be found external: <boolean> # filename of the form external:: or plane:

If the information needs to be signed to be passed to dafilesrv for example, the entire structure of (storage, files) is serialized, and compressed, and that then signed.

Functions

Logically executed on the engine, and retrived from dali or in future versions from an esp service (even if for remote reads).

GetFileInfomation(<logical-filename>, <options>)

The logical-filename can be any logical name - including a super file, or an implicit superfile.

options include: * Are compressed sizes needed? * Are signatures required? * Is virtual fileposition (non-local) required? * name of the user

This returns a structure that provides information about a list of files

meta:

: hostGroups: storage: files: secrets: #The secret names are known, how do we know which keys are required for those secrets?

Some key questions: * Should the TLK be in the dali meta information? [Possibly, but not in first phase. ] * Should the split points be in the dali meta information? [Probably not, but the meta should indicate whether they exist, and if so what format they are. ] * Super files (implicit or explicit) can contain the same file information more than once. Should it be duplicated, or have a flag to indicate a repeat. [I suspect this is fairly uncommon, so duplication would be fine for the first version.] * What storage plane information is serialized back? [ all is simplest. Can optimize later. ]

NOTE: This doesn't address the question of writing to a disk file...


Local class for interpreting the results. Logically executed on the manager, and may gather extra information that will be serialized to all workers. The aim is that the same class implementations are used by all the engines (and fileview in esp).

MasterFileCollection : RemoteFileCollection : FileCollection(eclReadOptions, eclFormatOptions, wuid, user, expectedMeta, projectedMeta); MasterFileCollection //Master has access to dali RemoteFileCollection : has access to remote esp // think some more

FileCollection::GatherFileInformation(<logical-filename>, gatherOptions); - potentially called once per query. - class is responsible for optimizing case where it matches the previous call (e.g. in a child query). - possibly responsible for retrieving the split points ()

Following options are used to control whether split points are retrieved when file information is gathered * number of channels reading the data? * number of strands reading each channel? * preserve order?

gatherOptions: * is it a temporary file?

This class serializes all information to every worker, where it is used to recereate a copy of the master filecollection. This will contain information derived from dali, and locally e.g. options specified in the activity helper. Each worker has a complete copy of the file information. (This is similar to dafilesrv with security tokens.)

The files that are actually required by a worker are calculated by calling the following function. (Note the derived information is not serialized.)

FilePartition FileCollection::calculatePartition(numChannels, partitionOptions)

partitionOptions: * number of channels reading the data? * number of strands reading each channel? * which channel? * preserve order? * myIP

A file partition contains a list of file slices:

class FileSlice (not serialized) { IMagicRowStream * createRowStream(filter, ...); // MORE! File * logicalFile; offset_t startOffset; offset_t endOffset; };

Things to bear in mind: - Optimize same file reused in a child query (filter likely to change) - Optimize same format reused in a child query (filename may be dynamic) - Intergrating third party file formats and distributed file systems may require extra information. - optimize reusing the format options. - ideally fail over to a backup copy midstream.. and retry in failed read e.g. if network fault

Examples

Example definition for a thor400, and two thor200s on the same nodes:

hostGroup: - name: thor400Group host: [node400_01,node400_02,node400_03,...node400_400]

storage:

: planes: #Simple 400 way thor - name: thor400 prefix: /var/lib/HPCCSystems/thor400 hosts: thor400Group #The storage plane used for replicating files on thor. - name: thor400_R1 prefix: /var/lib/HPCCSystems/thor400 hosts: thor400Group offset: 1 # A 200 way thor using the first 200 nodes as the thor 400 - name: thor200A prefix: /var/lib/HPCCSystems/thor400 hosts: thor400Group size: 200 # A 200 way thor using the second 200 nodes as the thor 400 - name: thor200B prefix: /var/lib/HPCCSystems/thor400 hosts: thor400Group size: 200 start: 200 # The replication plane for a 200 way thor using the second 200 nodes as the thor 400 - name: thor200B_R1 prefix: /var/lib/HPCCSystems/thor400 hosts: thor400Group size: 200 start: 200 offset: 1 # A roxie storage where 50way files are stored on a 100 way roxie - name: roxie100 prefix: /var/lib/HPCCSystems/roxie100 hosts: thor400Group size: 50 # The replica of the roxie storage where 50way files are stored on a 100 way roxie - name: roxie100_R1 prefix: /var/lib/HPCCSystems/thor400 hosts: thor400Group start: 50 size: 50

device = (start + (part + offset) % size;

size <= numDevices offset < numDevices device <= numDevices;

There is no special casing of roxie replication, and each file exists on multiple storage planes. All of these should be considered when determining which is the best copy to read from a particular engine node.

Creating storage planes from an existing systems [implemented]

Milestones:

a) Create baremetal storage planes [done]

b) [a] Start simplifying information in dali meta (e.g. partmask, remove full path name) c) [a] Switch reading code to use storageplane away from using dali path and environment paths - in ALL disk reading and writing code - change numDevices so it matches the container d) [c] Convert dali information from using copies to multiple groups/planese) [a] Reimplement the current code to create an IPropertyTree from dali file information (in a form that can be reused in dali) *f) [e] Refactor existing PR to use data in an IPropertyTree and cleanly separate the interfaces. g) Switch hthor over to using the new classes by default and work through all issues h) Refactor stream reading code. Look at the spark interfaces for inspiration/compatibility i) Refactor disk writing code into common class? j) [e] create esp service for accessing meta information k) [h] Refactor and review azure blob code l) [k] Re-implement S3 reading and writing code.

m) Switch fileview over to using the new classes. (Great test they can be used in another context + fixes a longstanding bug.)

) Implications for index reading? Will they end up being treated as a normal file? Don't implement for 8.0, although interface may support it.

*) My primary focus for initial work.

File reading refactoring

Buffer sizes: - storage plane specifies an optimal reading minimum - compression may have a requirement - the use for the data may impose a requirement e.g. a subset of the data, or only fetching a single record

  • parallel disk reading may want to read a big chunk, but then process in sections. groan.

Look at lambda functions to create split points for a file. Can we use the java classes to implement it on binary files (and csv/xml)?

****************** Reading classes and meta information****************** meta comes from a combination of the information in dfs and the helper

The main meta information uses the same structure that is return by the function that returns file infromation from dali. The format specific options are contained in a nested attribute so they can be completely arbitrary

The helper class also generates a meta structure. Some options fill in root elements - e.g. compressed. Some fill in a new section (hints: @x=y). The format options are generated from the paramaters to the dataset format.

note normally there is only a single (or very few) files, so merging isn't too painful. queryMeta() queryOptions() rename meta to format? ???

DFU server

Where does DFUserver fit in in a container system?

DFU has the following main functionality in a bare metal system: a) Spray a file from a 1 way landing zone to an N-way thor b) Convert file format when spraying. I suspect utf-16->utf8 is the only option actually used. c) Spray multiple files from a landing zone to a single logical file on an N-way thor d) Copy a logical file from a remote environment e) Despray a logical file to an external landing zone. f) Replicate an existing logical file on a given group. g) Copy logical files between groups h) File monitoring i) logical file operations j) superfile operations

ECL has the ability to read a logical file directly from a landingzone using 'FILE::<ip>' file syntax, but I don't think it is used very frequently.

How does this map to a containerized system? I think the same basic operations are likely to be useful. a) In most scenarios Landing zones are likely to be replaced with (blob) storage accounts. But for security reasons these are likely to remain distinct from the main location used by HPCC to store datasets. (The customer will have only access keys to copy files to and from those storage accounts.) The containerized system has a way for ECL to directly read from a blob storage account ('PLANE::<plane'), but I imagine users will still want to copy the files in many situations to control the lifetime of the copies etc. b) We still need a way to convert from utf16 to utf8, or extend the platform to allow utf16 to be read directly. c) This is still equally useful, allowing a set of files to be stored as a single file in a form that is easy for ECL to process. d) Important for copying data from an existing bare metal system to the cloud, and from a cloud system back to a bare metal system. e) Useful for exporting results to customers f+g) Essentially the same thing in the cloud world. It might still be useful to have h) I suspect we will need to map this to cloud-specific apis. i+j) Just as applicable in the container world.

Broadly, landing zones in bare metal map to special storage planes in containerized, and groups also map to more general storage planes.

There are a couple of complications connected with the implementation:

  1. Copying is currently done by starting an ftslave process on either the source or the target nodes. In the container world there is no local node, and I think we would prefer not to start a process in order to copy each file. 2) Copying between storage groups should be done using the cloud provider api, rather than transferring data via a k8s job.

Suggestions:

  • Have a load balanced dafilesrv which supports multiple replicas. It would have a secure external service, and an internal service for trusted components.
  • Move the ftslave logic into dafilesrv. Move the current code for ftslave actions into dafilesrv with new operations.
  • When copying from/to a bare metal system the requests are sent to the dafilesrv for the node that currently runs ftslave. For a container system the requests are sent to the loadbalanced service.
  • It might be possible to migrate to lamda style functions for some of the work...
  • A later optimization would use a cloud service where it was possible.
  • When local split points are supported it may be better to spray a file 1:1 along with partition information. Even without local split points it may still be better to spray a file 1:1 (cheaper).
  • What are the spray targets? It may need to be storage plane + number of parts, rather than a target cluster. The default number of parts is the #devices on the storage plane.

=> Milestones a) Move ftslave code to dafilesrv (partition, pull, push) [Should be included in 7.12.x stream to allow remote read compatibility?] b) Create a dafilesrv component to the helm charts, with internal and external services. c) use storage planes to determine how files are sprayed etc. (bare-metal, #devices) Adapt dfu/fileservices calls to take (storageplane,number) instead of cluster. There should already be a 1:1 mapping from existing cluster to storage planes in a bare-metal system, so this may not involve much work. [May also need a flag to indicate if ._1_of_1 is appended?] d) Select correct dafilesrv for bare-metal storage planes, or load balanced service for other. (May need to think through how remote files are represented.)

=> Can import from a bare metal system or a containerized system using command line??

: NOTE: Bare-metal to containerized will likely need push operations on the bare-metal system. (And therefore serialized security information) This may still cause issues since it is unlikely containerized will be able to pull from bare-metal. Pushing, but not creating a logical file entry on the containerized system should be easier since it can use a local storage plane definition.

e) Switch over to using the esp based meta information, so that it can include details of storage planes and secrets.

: [Note this would also need to be in 7.12.x to allow remote export to containerized, that may well be a step too far]

f) Add option to configure the number of file parts for spray/copy/despray g) Ensure that eclwatch picks up the list of storage planes (and the default number of file parts), and has ability to specify #parts.

Later: h) plan how cloud-services can be used for some of the copies i) investigate using serverless functions to calculate split points. j) Use refactored disk read/write interfaces to clean up read and copy code. k) we may not want to expose access keys to allow remote reads/writes - in which they would need to be pushed from a bare-metal dafilesrv to a containerized dafilesrv.

Other dependencies: * Refactored file meta information. If this is switching to being plane based, then the meta information should also be plane based. Main difference is not including the path in the meta information (can just be ignored) * esp service for getting file information. When reading remotely it needs to go via this now...

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/devdoc/README.html b/devdoc/README.html new file mode 100644 index 00000000000..95049a10276 --- /dev/null +++ b/devdoc/README.html @@ -0,0 +1,24 @@ + + + + + + Developer Documentation | HPCC Platform + + + + + + + + + + + + + +
Skip to content

Developer Documentation

This directory contains the documentation specifically targeted at developers of the HPCC system.

TIP

These documents are generated from Markdown by VitePress. See VitePress Markdown for more details.

General documentation

Implementation details for different parts of the system

  • Workunit Workflow: An explanation of workunits, and a walk-through of the steps in executing a query.
  • Code Generator: Details of the internals of eclcc.
  • Roxie: History and design details for roxie.
  • Memory Manager: Details of the memory manager (roxiemem) used by the query engines.
  • Metrics: Metrics Framework Design.

Other documentation

The ECL language is documented in the ecl language reference manual (generated as ECLLanguageReference-<version>.pdf).

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/devdoc/SecurityConfig.html b/devdoc/SecurityConfig.html new file mode 100644 index 00000000000..dd81ea22fc4 --- /dev/null +++ b/devdoc/SecurityConfig.html @@ -0,0 +1,24 @@ + + + + + + Security Configuration | HPCC Platform + + + + + + + + + + + + + +
Skip to content

Security Configuration

This document covers security configuration values and meanings. It does not serve as the source for how to configure security, but rather what the different values mean. These are not covered in the docs nor does any reasonable help information exist in the config manager or yaml files.

Supported Configurations

Security is configured either through an LDAP server or a plugin. Additionally, these are supported in both legacy deployments that use environment.xml and containerized deployments using Kubernetes and Helm charts. While these methods differ, the configuration values remain the same. Focus is placed on the different values and not the deployment target. Differences based on deployment can be found in the relevant platform documents.

Security Managers

Security is implemented via a security manager interface. Managers are loaded and used by components within the system to check authorization and authentication. LDAP is an exception to the loadable manager model. It is not a compliant loadable module like other security plugins. For that reason, the configuration for each is separated into two sections below: LDAP and Plugin Security Managers.

LDAP

LDAP is a protocol that connects to an Active Directory server (AD). The term LDAP is used interchangeably with AD. Below are the configuration values for an LDAP connection. These are valid for both legacy (environment.xml) and containerized deployments. For legacy deployments the configuration manager is the primary vehicle for setting these values. However, some values are not available through the tool and must be set manually in the environment.xml if needed for a legacy deployment.

In containerized environments, a LDAP configuration block is required for each component. Currently, this results in a verbose configuration where much of the information is repeated.

LDAP is capable if handling user authentication and feature access authorization (such as filescopes).

ValueExampleMeaning
adminGroupNameHPCCAdminsGroup name containing admin users for the AD
cacheTimeout60Timeout in minutes to keep cached security data
ldapCipherSuiteN/AUsed when AD is not up to date with latest SSL libs.
AD admin must provide
ldapPort389 (default)Insecure port
ldapSecurePort636 (default)Secure port over TLS
ldapProtocolldapldap for insecure (default), using ldapPort
ldaps for secure using ldapSecurePort
ldapTimeoutSec60 (default 5 for debug, 60 otherwise)Connection timeout to an AD before rollint to next AD
serverTypeActiveDirectoryIdentifies the type of AD server. (2)
filesBasednou=files,ou=ecl_kr,DC=z0lpf,DC=onmicrosoft,DC=comDN where filescopes are stored
groupsBasednou=groups,ou=ecl_kr,DC=z0lpf,DC=onmicrosoft,DC=comDN where groups are stored
modulesBaseDnou=modules,ou=ecl_kr,DC=z0lpf,DC=onmicrosoft,DC=comDN where permissions for resource are stored (1)
systemBasednOU=AADDC Users,DC=z0lpf,DC=onmicrosoft,DC=comDN where the system user is stored
usersBasednOU=AADDC Users,DC=z0lpf,DC=onmicrosoft,DC=comDN where users are stored (3)
systemUserhpccAdminAppears to only be used for IPlanet type ADs, but may still be required
systemCommonNamehpccAdminAD username of user to proxy all AD operations
systemPasswordSystem user passwordAD user password
ldapAdminSecretKeynoneKey for Kubernetes secrets (4) (5)
ldapAdminVaultIdnoneVault ID used to load system username and password (5)
ldapDomainnoneAppears to be a comma separated version of the AD domain name components (5)
ldapAddress192.168.10.42IP address to the AD
commonBasednDC=z0lpf,DC=onmicrosoft,DC=comOverrides the domain retrieved from the AD for the system user (5)
templateNamenoneTemplate used when adding resources (5)
authMethodnoneNot sure yet

Notes:

  1. modulesBaseDn is the same as resourcesBaseDn The code looks for first for modulesBaseDn and if not found will search for resourcesBaseDn
  2. Allowed values for serverType are ActiveDirectory, AzureActiveDirectory, 389DirectoryServer, OpenLDAP, Fedora389
  3. For AzureAD, users are managed from the AD dashboard, not via ECLWatch or through LDAP
  4. If present, ldapAdminVaultId is read and systemCommonName and systemPassword are read from the Kubernetes secrets store and not from the LDAP config values
  5. Must be configured manually in the environment.xml in legacy environments

Plugin Security Managers

Plugin security managers are separate shared objects loaded and initialized by the system. The manager interface is passed to components in order to provide necessary security functions. Each plugin has its own configuration. HPCC components can be configured to use a plugin as needed.

httpasswd Security Manager

See documentation for the settings and how to enable.

Single User Security Manager

To be added.

JWT Security Manager

To be added

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/devdoc/SecurityUserAuthentication.html b/devdoc/SecurityUserAuthentication.html new file mode 100644 index 00000000000..886798e1892 --- /dev/null +++ b/devdoc/SecurityUserAuthentication.html @@ -0,0 +1,24 @@ + + + + + + User Authentication | HPCC Platform + + + + + + + + + + + + + +
Skip to content

User Authentication

This document covers user authentication, the process of verifying the identity of the user. Authorization is a separate topic covering whether a user should be allowed to perform a specific operation of access a specific resource.

Each supported security manager is covered.

Generally, when authentication is needed, the security manager client should call the ISecManagerauthenticateUser method. The method also allows the caller to detect if the user being authenticated is a superuser. Use of that feature is beyond the scope of this document. In practice, this method is rarely if ever called. User authentication is generally performed as part of authorization. This is covered in more detail below.

Security Manager User Authentication

This section covers how each supported security manager handles user authentication. As stated above, the method authenticateUser is defined for this purpose. However, other methods also perform user authentication. The sections that follow describe in general how each security manager performs user authentication, whether from directly calling the authenticateUser method, or as an ancillary action taken when another method is called.

LDAP

The LDAP security manager uses the configured Active Directory to authenticate users. Once authenticated, the user is added to the permissions cache, if enabled, to prevent repeated trips to the AD whenever an authentication check is required.

If caching is enabled, a lookup is done to see if the user is already cached. If so, the cached user authentication status is returned. Note that the cached status remains until either the cache time to live expires or is cleared either manually or through some other programmatic action.

If caching is not enabled, a request is sent to the AD to validate the user credentials.

In either case, if digital signatures are configured, the user is also digitally signed using the username. Digitally signing the user allows for quick authentication by validating the signature against the username. During initial authentication, if the digital signature exists, it is verified to provide a fast way to authenticate the user. If the signature is not verified, the user is marked as not authenticated.

Authentication status is stored in the security user object so that further checks are not necessary when the same user object is used in multiple calls to the security manager.

HTPasswd

Authentication in the htpasswd manager does not support singularly authenticating the user without also authorizing resource access. See the special case for authentication with authorization below.

Regardless, the htpasswd manager authenticates users using the .htpasswd file that is installed on the cluster. It does so by finding the user in the file and verifying that the input hashed password matches the stored hashed password in file.

Single User

The single user security manager allows the definition of a single username with a password. The values are set in the environment configuration and are read during the initialization of the manager. All authentication requests validate against the configured username and password. The process is a simple comparison. Note that the password stored in the environment is hashed.

User Authentication During Authorization

Since resource access authorization requires an authenticated user, the authorization process also authenticates the user before checking authorization. There are a couple of advantages to this

  • Two separate calls are not required to check authorization (one to verify the user and one to check authorization)
  • The caller can perform third party authorization for specific user access

The authenticate method, or any of its overloads or derivatives, accepts a resource or resource list and a user. These methods authenticate the user first before checking access to the specified resource.

ECL Watch uses user authentication during authorization during its log in process. Instead of first authenticating the user, it calls an authenticate method passing both the user and the necessary resources for which the user must have access in order to log into ECL Watch.

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/devdoc/StyleGuide.html b/devdoc/StyleGuide.html new file mode 100644 index 00000000000..a747e6c1c09 --- /dev/null +++ b/devdoc/StyleGuide.html @@ -0,0 +1,74 @@ + + + + + + Coding conventions | HPCC Platform + + + + + + + + + + + + + +
Skip to content

Coding conventions

Why coding conventions?

Everyone has their own ideas of what the best code formatting style is, but most would agree that code in a mixture of styles is the worst of all worlds. A consistent coding style makes unfamiliar code easier to understand and navigate.

In an ideal world, the HPCC sources would adhere to the coding standards described perfectly. In reality, there are many places that do not. These are being cleaned up as and when we find time.

C++ coding conventions

Unlike most software projects around, HPCC has some very specific constraints that makes most basic design decisions difficult, and often the results are odd to developers getting acquainted with its code base. For example, when HPCC was initially developed, most common-place libraries we have today (like STL and Boost) weren't available or stable enough at the time.

Also, at the beginning, both C++ and Java were being considered as the language of choice, but development started with C++. So a C++ library that copied most behaviour of the Java standard library (At the time, Java 1.4) was created (see jlib below) to make the transition, if ever taken, easier. The transition never happened, but the decisions were taken and the whole platform is designed on those terms.

Most importantly, the performance constraints in HPCC can make no-brainer decisions look impossible in HPCC. One example is the use of traditional smart pointers implementations (such as boost::shared_ptr or C++'s auto_ptr), that can lead to up to 20% performance hit if used instead of our internal shared pointer implementation.

The last important point to consider is that some libraries/systems were designed to replace older ones but haven't got replaced yet. There is a slow movement to deprecate old systems in favour of consolidating a few ones as the elected official ways to use HPCC (Thor, Roxie) but old systems still could be used for years in tests or legacy sub-systems.

In a nutshell, expect re-implementation of well-known containers and algorithms, expect duplicated functionality of sub-systems and expect to be required to use less-friendly libraries for the sake of performance, stability and longevity.

For the most part out coding style conventions match those described at http://geosoft.no/development/cppstyle.html, with a few exceptions or extensions as noted below.

Source files

We use the extension .cpp for C++ source files, and .h or .hpp for header files. Header files with the .hpp extension should be used for headers that are internal to a single library, while header files with the .h extension should be used for the interface that the library exposes. There will typically be one .h file per library, and one .hpp file per cpp file.

Source file names within a single shared library should share a common prefix to aid in identifying where they belong.

Header files with extension .ipp (i for internal) and .tpp (t for template) will be phased out in favour of the scheme described above.

Java-style

We adopted a Java-like inheritance model, with macro substitution for the basic Java keywords. This changes nothing on the code, but make it clearer for the reader on what's the recipient of the inheritance doing with it's base.

  • interface (struct): declares an interface (pure virtual class)
  • extends (public): One interface extending another, both are pure virtual
  • implements (public): Concrete class implementing an interface

There is no semantic check, which makes it difficult to enforce such scheme, which has led to code not using it intermixed with code using it. You should use it when possible, most importantly on code that already uses it.

We also tend to write methods inline, which matches well with C++ Templates requirements. We, however, do not enforce the one-class-per-file rule.

See the Interfaces section for more information on our implementation of interfaces.

Identifiers

Class and interface names are in CamelCase with a leading capital letter. Interface names should be prefixed capital I followed by another capital. Class names may be prefixed with a C if there is a corresponding I-prefixed interface name, e.g. when the interface is primarily used to create an opaque type, but need not be otherwise.

Variables, function and method names, and parameters use camelCase starting with a lower case letter. Parameters may be prefixed with underscore when the parameter is used to initialize a member variable of the same name. Common cases are constructors and setter methods.

Example:

cpp
class MySQLSuperClass
+{
+    bool haslocalcopy = false;
+    void mySQLFunctionIsCool(int _haslocalcopy, bool enablewrite)
+    {
+        if (enablewrite)
+            haslocalcopy = _haslocalcopy;
+    }
+};

Pointers

Use real pointers when you can, and smart pointers when you have to. Take extra care on understanding the needs of your pointers and their scope. Most programs can afford a few dangling pointers, but a high-performance clustering platform cannot.

Most importantly, use common sense and a lot of thought. Here are a few guidelines:

  • Use real pointers for return values, parameter passing.
  • For .md variables use real pointers if their lifetime is guaranteed to be longer than the function (and no exception is thrown from functions you call), shared pointers otherwise.
  • Use Shared pointers for member variables - unless there is a strong guarantee the object has a longer lifetime.
  • Create Shared<X> with either:
    • Owned<X>: if your new pointer will take ownership of the pointer
    • Linked<X>: if you are sharing the ownership (shared)

Warning: Direct manipulation of the ownership might cause Shared<> pointers to lose the pointers, so subsequent calls to it (like o2->doIt() after o3 gets ownership) will cause segmentation faults.

Refer to [Reference counted objects]{.title-ref} for more information on our smart pointer implementation, Shared<>.

Methods that return pointers to link counted objects, or that use them, should use a common naming standard:

  • Foo * queryFoo() Does not return a linked pointer since lifetime is guaranteed for a set period. Caller should link if it needs to retain it for longer.
  • Foo * getFoo() Returned value is linked and should be assigned to an owned, or returned directly.
  • void setFoo(Foo * x) Generally parameters to functions are assumed to be owned by the caller, the callee needs to link them if they are retained.
  • void setFoo(Foo * ownedX) Some calls do transfer ownership of parameters - the parameter should be named to indicate this. If the function only has a single signficant parameter then sometimes the name of the function indicates the ownership.

Indentation

We use 4 spaces to indent each level. TAB characters should not be used.

The { that starts a new scope and the corresponding } to close it are placed on a new line by themselves, and are not indented. This is sometimes known as the Allman or ANSI style.

Comments

We generally believe in the philosophy that well written code is self-documenting. Comments are also encouraged to describe why something is done, rather than how - which should be clear from the code.

javadoc-formatted comments for classes and interfaces are being added.

Classes

The virtual keyword should be included on the declaration of all virtual functions - including those in derived classes, and the override keyword should be used on all virtual functions in derived classes.

Namespaces

MORE: Update!!!

We do not use namespaces. We probably should, following the Google style guide's guidelines - see http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces

Other

We often pretend we are coding in Java and write all our class members inline.

C++11

Other coding conventions

ECL code

The ECL style guide is published separately.

Javascript, XML, XSL etc

We use the commonly accepted conventions for formatting these files.


Design Patterns

Why Design Patterns?

Consistent use of design patterns helps make the code easy to understand.

Interfaces

While C++ does not have explicit support for interfaces (in the java sense), an abstract class with no data members and all functions pure virtual can be used in the same way.

Interfaces are pure virtual classes. They are similar concepts to Java's interfaces and should be used on public APIs. If you need common code, use policies (see below).

An interface's name must start with an 'I' and the base class for its concrete implementations should start with a 'C' and have the same name, ex:

cpp
CFoo : implements IFoo { };

When an interface has multiple implementations, try to stay as close as possible to this rule. Ex:

cpp
CFooCool : implements IFoo { };
+CFooWarm : implements IFoo { };
+CFooALot : implements IFoo { };

Or, for partial implementation, use something like this:

cpp
CFoo : implements IFoo { };
+CFooCool : public CFoo { };
+CFooWarm : public CFoo { };

Extend current interfaces only on a 'is-a' approach, not to aggregate functionality. Avoid pollution of public interfaces by having only the public methods on the most-base interface in the header, and internal implementation in the source file. Prefer pImpl idiom (pointer-to-implementation) for functionality-only requirements and policy based design for interface requirements.

Example 1: You want to decouple part of the implementation from your class, and this part does not implements the interface your contract requires.:

cpp
interface IFoo
+{
+    virtual void foo()=0;
+};
+
+// Following is implemented in a separate private file...
+class CFoo : implements IFoo
+{
+    MyImpl *pImpl;
+public:
+    virtual void foo() override { pImpl->doSomething(); }
+};

Example2: You want to implement the common part of one (or more) interface(s) in a range of sub-classes.:

cpp
interface ICommon
+{
+    virtual void common()=0;
+};
+interface IFoo : extends ICommon
+{
+    virtual void foo()=0;
+};
+interface IBar : extends ICommon
+{
+    virtual void bar()=0;
+};
+
+template <class IFACE>
+class Base : implements IFACE
+{
+    virtual void common() override { ... };
+}; // Still virtual
+
+class CFoo : public Base<IFoo>
+{
+    void foo() override { 1+1; };
+};
+class CBar : public Base<IBar>
+{
+    void bar() override { 2+2; };
+};

NOTE: Interfaces deliberately do not contain virtual destructors. This is to help ensure that they are never destroyed by calling delete directly.

Reference counted objects

Shared<> is an in-house intrusive smart pointer implementation. It is close to boost's intrusive_ptr. It has two derived implementations: Linked and Owned, which are used to control whether the pointer is linked when a shared pointer is created from a real pointer or not, respectively. Ex:

cpp
Owned<Foo> myFoo = new Foo; // Take owenership of the pointers
+Linked<Foo> anotherFoo = = myFoo; // Shared ownership

Shared<> is thread-safe and uses atomic reference count handled by each object (rather than by the smart pointer itself, like boost's shared_ptr).

This means that, to use Shared<>, your class must implement the Link() and Release() methods - most commonly by extending the CInterfaceOf<> class, or the CInterface class (and using the IMPLEMENT_IINTERFACE macro in the public section of your class declaration).

This interface controls how you Link() and Release() the pointer. This is necessary because in some inner parts of HPCC, the use of a "really smart" smart pointer would add too many links and releases (on temporaries, local variables, members, etc) that could add to a significant performance hit.

The CInterface implementation also include a virtual function beforeDispose() which is called before the object is deleted. This allows resources to be cleanly freed up, with the full class hierarchy (including virtual functions) available even when freeing items in base classes. It is often used for caches that do not cause the objects to be retained.

STL

MORE: This needs documenting

Structure of the HPCC source tree

MORE!

Requiring more work: * namespaces * STL * c++11 * Review all documentation * Better examples for shared

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/devdoc/UserBuildAssets.html b/devdoc/UserBuildAssets.html new file mode 100644 index 00000000000..d06835a71ec --- /dev/null +++ b/devdoc/UserBuildAssets.html @@ -0,0 +1,57 @@ + + + + + + Build Assets for individual developer | HPCC Platform + + + + + + + + + + + + + +
Skip to content

Build Assets for individual developer

Build Assets

The modern tool used for generating all our official assets is the Github Actions build-asset workflow on the hpcc-systems/HPCC-Platform repository, located here. Developers and contributors can utilize this same workflow on their own forked repository. This allows developers to quickly create assets for testing changes and test for errors before the peer review process.

Build assets will generate every available project under the HPCC-Platform namespace. There currently is not an option to control which packages in the build matrix get generated. But most packages get built in parallel, and released after the individual matrix job is completed, so there is no waiting on packages you don't need. Exceptions to this are for packages that require other builds to complete, such as the ECLIDE.

Upon completion of each step and matrix job in the workflow, the assets will be output to the repositories tags tab. An example for the hpcc-systems user repository is hpcc-systems/HPCC-Platform/tags.

Tag tab screenshot

Dependent variables

The build assets workflow requires several repository secrets be available on a developers machine in order to run properly. You can access these secrets and variables by going to the settings tab in your forked repository, and then clicking on the Secrets and Variables - Actions drop down under Security on the lefthand side of the settings screen.

Actions secrets and variables

Create a secret by clicking the green New Repository Secret button. The following secrets are needed;

  • LNB_ACTOR - Your Github username
  • LNB_TOKEN - Classic Github token for your user with LN repo access
  • DOCKER_USERNAME - Your docker.io username
  • DOCKER_PASSWORD - Your docker.io password
  • SIGNING_CERTIFICATE - pks12 self signed cert encoded to base64 for windows signing
  • SIGNING_CERTIFICATE_PASSPHRASE - passphrase for pks12 cert
  • SIGNING_SECRET - ssh-keygen private key for signing linux builds
  • SIGN_MODULES_KEYID - email used to generate key
  • SIGN_MODULES_PASSPHRASE - passphrase for private key

Generating the windows signing certificate

To generate the self signed certificate for windows packages, you will need to do the following steps.

  1. Generate a root certificate authority

openssl req -x509 -sha256 -days 365 -nodes -newkey rsa:2048 -subj "/CN=example.com/C=US/L=Boca Raton" -keyout rootCA.key -out rootCA.crt

  1. Create the server secret key

openssl genrsa -out server.key 2048

  1. generate a csr.conf file
cat > csr.conf <<EOF
+[ req ]
+default_bits = 2048
+prompt = no
+default_md = sha256
+req_extensions = req_ext
+distinguished_name = dn
+
+[ dn ]
+C = US
+ST = Florida
+L = Boca Raton
+O = LexisNexis Risk
+OU = HPCCSystems Development
+CN = example.com
+
+[ req_ext ]
+subjectAltName = @alt_names
+
+[ alt_names ]
+DNS.1 = example.com
+IP.1 = 127.0.0.1
+
+EOF
  1. Generate Cert Signing Request

openssl req -new -key server.key -out server.csr -config csr.conf

  1. Create cert.conf file
cat > cert.conf <<EOF
+
+authorityKeyIdentifier=keyid,issuer
+basicConstraints=CA:FALSE
+keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
+subjectAltName = @alt_names
+
+[ alt_names ]
+DNS.1 = example.com
+
+EOF
  1. Generate SSL cert with self signed CA

openssl x509 -req -in server.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out server.crt -days 365 -sha256 -extfile cert.conf

  1. Use server.crt to generate PKCS12 needed for windows tools

openssl pkcs12 -inkey server.key -in server.crt -export -name "hpcc_sign_cert" -out hpcc_sign_cert.pfx

You will be asked to "enter export password", this will be what goes in the variable SIGNING_CERTIFICATE_PASSPHRASE in Github Actions.

  1. Convert to base64

On linux: base64 hpcc_sign_cert.pfx > hpcc_sign_cert.base64

On MacOS: base64 -i hpcc_sign_cert.pfx -o hpcc_sign_cert.base64

From here you can cat the output of hpcc_sign_cert.base64 and copy the output into the variable SIGNING_CERTIFICATE in Github Actions.

Generating a signing key for linux builds

For linux builds we're going to generate a private key using GnuPG (gpg).

Start the process by entering a terminal and run the command;gpg --full-generate-key

You will be given several options in this process.

For type of key, select RSA and RSA default.

For keysize, enter 4096.

For expiration date, select 0 = key does not expire.

Input your real name.

Input your company email address.

For comment, input something like Github actions key for signing linux builds.

Then it will ask you to enter a passphrase for the key, and confirm the passphrase. Do not leave this blank.

A key should be output and entered into your gpg keychain. Now we need to export the key for use in the github actions secret.

To extract your key run gpg --output private.pgp --armor --export-secret-key <email-address-used>.

Now open private.pgp, copy all, and go to github actions secrets. Paste the output into the secret "SIGNING_SECRET"

Starting a build

The build-asset workflow is kicked off by a tag being pushed to the developers HPCC-Platform repository. Before we push the tag to our HPCC-Platform repository, we will want to have other tags in place if we want LN and ECLIDE builds to function correctly. Suggested tag patterns are community_HPCC-12345-rc1 or HPCC-12345-rc1.

If you choose not to tag the LN and ECLIDE builds, the community builds will generate but errors will be thrown for any build utilizing the LN repository. ECLIDE will not even attempt a build unless you are also successfully building LN due to the dependency scheme we use. The 'Baremetal' builds are designed to generate our clienttools targets for windows-2022 and macos-12 distributions. These jobs contain both the COMMUNITY and LN builds. If the LN build is not tagged, the COMMUNITY section of the job will run, and the assets will be uploaded, but the job will fail when it tries to build LN.

If you choose to precede your Jira number with community_ then you must tag LN with internal_ and ECLIDE with eclide_. Otherwise just use the Jira tag in all three repositories.

Once the LN and ECLIDE repository tags have been created and pushed with the same base branch that your work is based on for the HPCC-Platform, then you are free to push the HPCC-Platform tag which will initiate the build process.

The summary of the build-asset workflow can then be viewed for progress, and individual jobs can be selected to check build outputs. Build Summary HPCC-12345

Asset output

Assets from the workflow will be released into the corresponding tag location, either in the HPCC-Platform repository for all community based builds, or the LN repository for any builds containing proprietary plugins. Simply browse to the releases or tag tab of your repository and select the tag name you just built. The assets will show up there as the build completes. An example of this on the hpcc-systems repository is hpcc-systems/HPCC-Platform/releases.

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/devdoc/VersionSupport.html b/devdoc/VersionSupport.html new file mode 100644 index 00000000000..de3c6215833 --- /dev/null +++ b/devdoc/VersionSupport.html @@ -0,0 +1,24 @@ + + + + + + Current versions | HPCC Platform + + + + + + + + + + + + + +
Skip to content

Current versions

nameversion
current9.6.x
previous9.4.x
critical9.2.x
security9.0.x

Supported versions

We release a new version of the platform every 3 months. If there are major changes in functionality, or significant backward compatibility issues then it will be tagged as a new major version, otherwise a new minor version. We normally maintain 4 versions of the system, which means that each new release will typically be supported for a year. Once a new major or minor version has been tagged gold it should not have any changes that change the behavior of queries.

Which versions should changes be applied to? The following gives some examples of the types of changes and which version they would be most relevant to target.

"master":

  • New features.
  • Bug fixes that will change the semantics of existing queries or processes.
  • Refactoring.
  • Performance improvements (unless simple and safe)

"<current>":

  • Bug fixes that only change behavior where it previously crashes or had undefined behavior (If well defined but wrong need to have very strong justification to change.)
  • Fixes for race conditions (the behavior was previously indeterminate so less of an argument against it changing)
  • Data corruption fixes - on a case by case basis if they change existing query results.
  • Missing functionality that prevents features from working.
  • Changes for tech-preview work that only effect those who are using it.
  • Regressions.
  • Improvements to logging and error messages (possibly in "previous" if simple and added to help diagnose problems).
  • Occasional simple refactoring that makes up-merging simpler..
  • Changes to improve backward compatibility of new features. (E.g. adding an ignored syntax to the compiler.)
  • Performance improvements - if simple and safe

"<previous>":

  • Simple bug fixes that do not change behavior
  • Simple changes for missing functionality
  • Regressions with simple fixes (but care is needed if it caused a change in behavior)
  • Serious regressions
  • Complex security fixes

"<critical>" fixes only:

  • Simple security fixes
  • Complex security fixes if sufficiently serious

"<security>" fixes only:

  • Serious security fixes

Occasionally earlier branches will be chosen, (e.g. security fixes to even older versions) but they should always be carefully discussed (and documented).

Patches and images

We aim to produce new point releases once a week. The point releases will contain

a) Any changes to the code base for that branch. b) Any security fixes for libraries that are project dependencies. We will upgrade to the latest point release for the library that fixes the security issue. c) For the cloud any security fixes in the base image or the packages installed in that image.

If there are no changes in any of those areas for a particular version then a new point release will not be created.

If you are deploying a system to the cloud you have one of two options

a) Use the images that are automatically built and published as part of the build pipeline. This image is currently based on ubuntu 22.04 and contains the majority of packages users will require.

b) Use your own hardened base image, and install the containerized package that we publish into that image.

Package versions.

We currently generate the following versions of the package and images:

  • debug
  • release with symbols
  • release without symbols.

It is recommended that you deploy the "release with symbols" version to all bare-metal and non-production cloud deployments. The extra symbols allow the system to generate stack backtraces which make it much easier to diagnose problems if they occur. The "release without symbols" version is recommended for Kubernetes production deployments. Deploying a system without symbols reduces the size of the images. This reduces the time it takes Kubernetes to copy the image before provisioning a new node.

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/devdoc/Workunits.html b/devdoc/Workunits.html new file mode 100644 index 00000000000..b4ff0e89511 --- /dev/null +++ b/devdoc/Workunits.html @@ -0,0 +1,426 @@ + + + + + + Understanding workunits | HPCC Platform + + + + + + + + + + + + + +
Skip to content

Understanding workunits

Introduction

A workunit contains all the information that the system requires about a query - including the parameters it takes, how to execute it, and how to format the results. Understanding the contents of a workunit is a key step to understanding how the HPCC system fits together. This document begins with an overview of the different elements in a workunit. That is then followed by a walk-through executing a simple query, with a more detailed description of some of the workunit components to show how they all tie together.

Before looking at the contents of a workunit it is important to understand one of the design goals behind the HPCC system. The HPCC system logically splits the code that is needed to execute a query in two. On the one hand there are the algorithms that are used to perform different dataset operations (e.g. sorting, deduping). The same algorithms are used by all the queries that execute on the system. On the other hand there is the meta-data that describes the columns present in the datasets, which columns you need to sort by, and the order of operations required by the query. These are typically different for each query. This "meta-data" includes generated compare functions, classes that describe the record formats, serialized data and graphs.

A workunit only contains data and code relating to the meta data for the query, i.e. "what to do", while the different engines (hthor, roxie, and thor) implement the algorithms - "how to do it". If you look at a workunit for an ECL query that sorts a dataset you will not find code to perform the sort itself in the workunit - you will not even find a call to a sort library function - that logic is contained in the engine that executes the query.

One consequence of this split, which can be initially confusing, is that execution continually passes back and forth between the generated code and the engines. By the end of this document you should have a better understanding of how the generated code is structured and the part it plays in executing a query.

Note the term "Query" is used as a generic term to cover read-only queries (typically run in roxie) and ETL (Extract, Transform, and Load) style queries that create lots of persistent datafiles (typically run in Thor). Also, the term "workunit" is used ambiguously. The dll created from a query is called a workunit (which is static), but "workunit" is also used to describe a single execution of a query (which includes the parameters and results). It should be clear from the context which of these is meant.

Throughout this document "dll" is a generic term used to refer to a dynamically loaded library. These correspond to shared objects in Linux (extension '.so'), dynamic libraries in Max OS X ('.dylib'), and dynamic link libraries in windows ('.dll').

Contents of a workunit

A workunit is generated by the ecl compiler, and consists of a single dll. That dll contains several different elements:

  • Various C++ helper classes, and exported factory methods are used to create instances of those classes.
  • An XML resource containing different pieces of information about a workunit, including workflow and graphs.
  • Other user-defined resources included in the manifest.

A workunit dll contains everything that the engines need to execute the query. When a workunit is executed, key elements of the xml information are cloned from the workunit dll and copied into a database. This is then augmented with other information as the query is executed - e.g. input parameters, results, statistics, etc.. The contents of the workunit are accessed through an "IWorkUnit" interface (defined in common/workunit/workunit.hpp) that hides the implementation details.

(Workunit information is currently stored in the Dali database - one of the components within the HPCC platform. Work is in-progress to allow the bulk of this workunit data to be stored in Cassandra or another third-party database instead.)

How is the workunit used?

The workunit information is used by most of the components in the system. The following is a quick outline:

  • eclcc

    Creates a workunit dll from an ecl query.

  • eclccserver

    Executes eclcc to create a workunit dll, and then clones some of the information into dali to create an active instance, ready to execute.

  • esp

    Uses information in the workunit dll to publish workunits. This includes details of the parameters that the query takes, how they should be formatted, and the results it returns.

  • eclscheduler

    Monitors workunits that are waiting for events, and updates them when those events occur.

  • eclagent/Roxie

    Process the different workflow actions, and workflow code.

  • hThor/Roxie/Thor

    Execute graphs within the workflow items.

  • Dali

    This database is used to store the state of the workunit state.

Example

The following ECL will be used as an example for the rest of the discussion. It is a very simple search that takes a string parameter 'searchName', which is the name of the person to search for, and returns the matching records from an index. It also outputs the word 'Done!' as a separate result.

ecl
STRING searchName := 'Smith' : STORED('searchName');
+nameIndex := INDEX({ STRING40 name, STRING80 address }, 'names');
+results := nameIndex(KEYED(name = searchName));
+OUTPUT(results);
+OUTPUT('Done!');

Extracts from the XML and C++ that are generated from this example will be included in the following discussion.

Workunit Main Elements

This section outlines the different sections in a workunit. This is followed by a walk-through of the stages that take place when a workunit is executed, together with a more detailed explanation of the workunit contents.

Workflow

The workflow is the highest level of control within a workunit. It is used for two related purposes:

  • Scheduling The HPCC system allows ECL code to be executed when certain events occur - e.g. every hour or when files are uploaded to a landing zone. Each piece of ECL code that is triggered by an external event creates a separate workflow action. This allows each of those events to be processed independently.
  • Splitting up queries. There are situations where it is useful to break parts of an ECL query into independent sections. The simplest example is the PERSIST workflow operation, which allows results to be shared between different work units. Each workflow operation creates one (or sometimes more) independent workflow items, which are then connected together.

Each piece of independent ECL is given a unique workflow id (wfid). Often workflow items need to be executed in a particular order, e.g. ensuring a persist exists before using it, which is managed with dependencies between different workflow items.

Our example above generates the following XML entry in the workunit:

xml
<Workflow>
+    <Item .... wfid="1"/>
+    <Item .... wfid="2">
+    <Dependency wfid="1"/>
+    <Schedule/>
+    </Item>
+</Workflow>

This contains two workflow items. The first workflow item (wfid=1) ensures that the stored value has a default value if it has not been supplied. The second item (with wfid=2) is the main code for the query. This has a dependency on the first workflow item because the stored variable needs to be intialised before it is executed.

MyProcess

The generated code contains a class instance that is used for executing the code associated with the workflow items. It is generated at the end of the main C++ module. E.g.:

cpp
struct MyEclProcess : public EclProcess {
+    virtual int perform(IGlobalCodeContext * gctx, unsigned wfid) {
+        ....
+        switch (wfid) {
+            case 1U:
+                ... code for workflow item 1 ...
+            case 2U:
+                ... code for workflow item 2 ...
+            break;
+        }
+        return 2U;
+    }
+};

The main element is a switch statement inside the perform() function that allows the workflow engines to execute the code associated with a particular workflow item.

There is also an associated factory function that is exported from the dll, and is used by the engines to create instances of the class:

cpp
extern "C" ECL_API IEclProcess* createProcess()
+{
+    return new MyEclProcess;
+}

Graph

Most of the work executing a query involves processing dataset operations, which are implemented as a graph of activities. Each graph is represented in the workunit as an xml graph structure (currently it uses the xgmml format). The graph xml includes details of which types of activities are required to be executed, how they are linked together, and any other dependencies.

The graph in our example is particularly simple:

xml
<Graph name="graph1" type="activities">
+    <xgmml>
+    <graph wfid="2">
+    <node id="1">
+    <att>
+        <graph>
+        <att name="rootGraph" value="1"/>
+        <edge id="2_0" source="2" target="3"/>
+        <node id="2" label="Index Read&#10;&apos;names&apos;">
+        ... attributes for activity 2 ...
+        </node>
+        <node id="3" label="Output&#10;Result #1">
+        ... attributes for activity 3 ...
+        </node>
+        </graph>
+    </att>
+    </node>
+    </graph>
+    </xgmml>
+</Graph>

This graph contains a single subgraph (node id=2) that contains two activities - an index read activity and an output result activity. These activities are linked by a single edge (id "2_0"). The details of the contents are covered in the section on executing graphs below.

Generated Activity Helpers

Each activity has a corresponding class instance in the generated code, and a factory function for creating instances of that class:

cpp
struct cAc2 : public CThorIndexReadArg {
+    ... Implementation of the helper for activity #2 ...
+};
+extern "C" ECL_API IHThorArg * fAc2() { return new cAc2; }
+
+struct cAc3 : public CThorWorkUnitWriteArg {
+    ... Implementation of the helper for activity #3 ...
+};
+extern "C" ECL_API IHThorArg * fAc3() { return new cAc3; }

The helper class for an activity implements the interface that is required for that particular kind. (The interfaces are defined in rtl/include/eclhelper.hpp - further details below.)

Other

The are several other items, detailed below, that are logically associated with a workunit. The information may be stored in the workunit dll or in various other location e.g. Dali, Sasha or Cassandra. It is all accessed through the IWorkUnit interface in common/workunit/workunit.hpp that hides the implementation details. For instance information generated at runtime cannot by definition be included in the workunit dll.

Options

Options that are supplied to eclcc via the -f command line option, or the #option statement are included in the <Debug> section of the workunit xml:

xml
<Debug>
+    <addtimingtoworkunit>0</addtimingtoworkunit>
+    <noterecordsizeingraph>1</noterecordsizeingraph>
+    <showmetaingraph>1</showmetaingraph>
+    <showrecordcountingraph>1</showrecordcountingraph>
+    <spanmultiplecpp>0</spanmultiplecpp>
+    <targetclustertype>hthor</targetclustertype>
+</Debug>

Note, the names of workunit options are case insensitive, and converted to lower case.

Input Parameters

Many queries contain input parameters that modify their behaviour. These correspond to STORED definitions in the ECL. Our example contains a single string "searchName", so the workunit contains a single input parameter:

xml
<Variables>
+    <Variable name="searchname">
+    <SchemaRaw xsi:type="SOAP-ENC:base64">
+        searchname&#xe000;&#xe004;&#241;&#255;&#255;&#255;&#xe001;ascii&#xe000;&#xe001;ascii&#xe000;&#xe000;&#xe018;&#xe000;&#xe000;&#xe000;&#xe000;   
+    </SchemaRaw>
+    </Variable>
+</Variables>

The implementation details of the schema information is encapsulated by the IConstWUResult interface in workunit.hpp.

Results

The workunit xml also contains details of each result that the query generates, including a serialized description of the output record format:

xml
<Results>
+    <Result isScalar="0"
+            name="Result 1"
+            recordSizeEntry="mf1"
+            rowLimit="-1"
+            sequence="0">
+    <SchemaRaw xsi:type="SOAP-ENC:base64">
+    name&#xe000;&#xe004;(&#xe000;&#xe000;&#xe000;&#xe001;ascii&#xe000;&#xe001;ascii&#xe000;address&#xe000;&#xe004;P&#xe000;&#xe000;&#xe000;&#xe001;ascii&#xe000;&#xe001;ascii&#xe000;&#xe000;&#xe018;%&#xe000;&#xe000;&#xe000;{ string40 name, string80 address };&#10;   </SchemaRaw>
+    </Result>
+    <Result name="Result 2" sequence="1">
+    <SchemaRaw xsi:type="SOAP-ENC:base64">
+    Result_2&#xe000;&#xe004;&#241;&#255;&#255;&#255;&#xe001;ascii&#xe000;&#xe001;ascii&#xe000;&#xe000;&#xe018;&#xe000;&#xe000;&#xe000;&#xe000;   </SchemaRaw>
+    </Result>
+</Results>

in our example there are two - the dataset of results and the text string "Done!". The values of the results for a query are associated with the workunit. (They are currently saved in dali, but this may change in version 6.0.)

Timings and Statistics

Any timings generated when compiling the query are included in the workunit dll:

xml
<Statistics>
+    <Statistic c="eclcc"
+            count="1"
+            creator="eclcc"
+            kind="SizePeakMemory"
+            s="compile"
+            scope=">compile"
+            ts="1428933081084000"
+            unit="sz"
+            value="27885568"/>
+</Statistics>

Other statistics and timings created when running the query are stored in the runtime copy of the workunit. (Statistics for graph elements are stored in a different format from global statistics, but the IWorkUnit interface ensures the implementation details are hidden.)

Manifests

It is possible to include other user-defined resources in the workunit dll - e.g. web pages, or dashboard layouts. I have to confess I do not understand them... ??Tony please provide some more information....!

Stages of Execution

Once a workunit has been compiled to a dll it is ready to be executed. Execution can be triggered in different ways, E.g.:

  • The ECL for a query is submitted to esp
    • A workunit entry, containing the ECL, is created in dali and added to an eclccserver queue.
    • An eclccserver instance removes the workunit form the queue, and compiles the ECL to a workunit dll.
    • The dali workunit entry is updated with the information from the workunit dll.
    • The dali workunit is added to the agent execution queue associated with the target cluster.
    • The associated engine (actually agentexec for hThor and Thor) pulls a query form the queue and executes it.
  • A query is submitted and published with a name. Another request is then submitted to execute this previously compiled query.
    • A workunit entry, containing the ECL, is created in dali and added to an eclccserver queue.
    • An eclccserver instance removes the workunit form the queue, and compiles the ECL to a workunit dll.
    • There is a 'query set' for each combination of query name and the target cluster. The new workunit dll is added to the appropriate query set, and marked as the current active implementation.
    • Later, a query that references a named query is submitted to esp.
    • The name and target cluster are mapped via the query set to the active implementation, and a workunit instance is created from the active workunit dll.
    • The workunit is added to a roxie or eclagentexec queue ready to be executed.
    • The associated engine pulls a query form the queue and executes it.
  • A query is compiled as a stand alone executable. The executable is then run.
    • eclcc is executed on the command line without the -shared command line option.
    • The resulting executable is run. The engine used to execute the query depends on the -platform parameter supplied to eclcc.

Most queries create persistent workunits in dali and then update those workunits with results as they are calculated, however for some roxie queries (e.g. in a production system) the execution workunits are transient.

The following walk-through details the main stages executing a query, and the effect each of the query elements has.

Queues

The system uses several inter-process queues to communicate between the different components in the system. These queues are implemented by dali. Components can subscribe to one or more queues, and receive notifications when entries are avaialable.

Some example queues are:

  • <cluster>.eclserver - workunits to be compiled
  • <cluster>.roxie - workunits to execute on roxie
  • <cluster>.thor - graphs to execute on thor
  • <cluster>.eclscheduler - workunits that need to wait for events
  • <cluster>.agent - workunits to be executed on hthor or thor.
  • dfuserver_queue - dfu workunits for sprays/file copies etc.

Workflow

When a workunit is ready to be run, the workflow controls the flow of execution. The workflow engine (defined in common/workunit/workflow.cpp) is responsible for determining which workflow item should be executed next.

The workflow for Thor and hThor jobs is coordinated by eclagent, while roxie includes the workflow engine in its process. The eclscheduler also uses the workflow engine to process events and mark workflow items ready for execution.

eclagent, or roxie calls the createProcess() function from the workunit dll to create an instance of the generated workflow helper class, and passes it to the workflow engine. The workflow engine walks the workflow items to find any items that are ready to be executed (have the state "reqd" - i.e. required). If a required workflow item has dependencies on other child workflow items then those children are executed first. Once all dependencies have executed successfully the parent workflow item is executed. The example has the following workflow entries:

xml
<Workflow>
+    <Item mode="normal"
+        state="null"
+        type="normal"
+        wfid="1"/>
+    <Item mode="normal"
+        state="reqd"
+        type="normal"
+        wfid="2">
+        <Dependency wfid="1"/>
+        <Schedule/>
+    </Item>
+</Workflow>

Item 2 has a state of "reqd", so it should be evaluated now. Item 2 has a dependency on item 1, so that must be evaluated first. This is achieved by calling MyEclProcess::perform() on the object that was previously created from the workunit dll, passing in wfid = 1. That will execute the following code:

cpp
switch (wfid) {
+    case 1U:
+        if (!gctx->isResult("searchname",4294967295U)) {
+            ctx->setResultString("searchname",4294967295U,5U,"Smith");
+        }
+        break;
+    break;
+}

This checks if a value has been provided for the input parameter, and if not assigns a default value of "Smith". The function returns control to the workflow engine. With the dependencies for wfid 2 now satisfied, the generated code for that workflow id is now executed:

cpp
switch (wfid) {
+    case 2U: {
+        ctx->executeGraph("graph1",false,0,NULL);
+        ctx->setResultString(0,1U,5U,"Done!");
+    }
+    break;
+}

Most of the work for this workflow item involves executing graph1 (by calling back into eclagent/roxie). However, the code also directly sets another result. This is fairly typical - the code inside MyProcess::perform often combines evaluating scalar results, executing graphs, and calling functions that cannot (currently) be called inside a graph (e.g. those involving superfile transactions).

Once all of the required workflow items are executed, the workunit is marked as completed. Alternatively, if there are workflow items that are waiting to be triggered by an event, the workunit will be passed to the scheduler, which will keep monitoring for events.

Note that most items in the xml workflow begin in the state WFStateNull. This means that it is valid to execute them, but they haven't been executed yet. Typically, only a few items begin with the state WFStateReqd.

There are various specialised types of workflow items - e.g. sequential, wait, independent, but they all follow the same basic approach of executing dependencies and then executing that particular item.

Most of the interesting work in an ECL query is done within a graph. The call ctx->executeGraph will either execute the graph locally (in the case of hthor and roxie), or add the workunit onto a queue (for Thor). Whichever happens, control will pass to that engine.

Specialised Workflow Items

Each item mode/type can affect the dependency structure of the workflow:

  • Sequential/Ordered

    The workflow structure for sequential and ordered is the same. An item is made to contain all of the actions in the statement. This is achieved by making each action a dependency of this item. The dependencies, and consequently the actions, must be executed in order. An extra item is always inserted before each dependency. This means that if other statements reference the same dependency, it will only be performed once.

  • Persist

    When the persist workflow service is used, two items are created. One item contains the graphs that perform the expression defined in ECL. It also stores the wfid for the second item. The second item is used to determine whether the persist is up to date.

  • Condition (IF)

    The IF function has either 2 or 3 arguments: the expression, the trueresult, and sometimes the falseresult. For each argument, a workflow item is created. These items are stored as dependencies to the condition, in the order stated above.

  • Contingency (SUCCESS/FAILURE)

    When a contingency clause is defined for an attribute, the attribute item stores the wfid of the contingency. If both success and failure are used, then the item will store the wfid of each contingency. The contingency is composed of items, just like the larger query.

  • Recovery

    When a workflow item fails, if it has a recovery clause, the item will be re-executed. The clause contains actions defined by the programmer to remedy the problem. This clause is stored differently to SUCCESS/FAILURE, in that the recovery clause is a dependency of the failed item. In order to stop the recovery clause from being executed like the other dependencies, it is marked with WFStateSkip.

  • Independent

    This specifier is used when a programmer wants to common up code for the query. It prevents the same piece of code from being executed twice in different contexts. To achieve this, an extra item is inserted between the expression and whichever items depend on it. This means that although the attribute can be referenced several times, it will only be executed once.

Graph Execution

All the engines (roxie, hThor, Thor) execute graphs in a very similar way. The main differences are that hThor and Thor execute a sub graph at a time, while roxie executes a complete graph as one. Roxie is also optimized to minimize the overhead of executing a query - since the same query tends to be run multiple times. This means that roxie creates a graph of factory objects and those are then used to create the activities. The core details are the same for each of them though.

Details of the graph structure

First, a recap of the structure of the graph together with the full xml for the graph definition in our example:

xml
<Graph name="graph1" type="activities">
+    <xgmml>
+        <graph wfid="2">
+            <node id="1">
+                <att>
+                    <graph>
+                        <att name="rootGraph" value="1"/>
+                        <edge id="2_0" source="2" target="3"/>
+                        <node id="2" label="Index Read&#10;&apos;names&apos;">
+                            <att name="definition" value="workuniteg1.ecl(3,1)"/>
+                            <att name="name" value="results"/>
+                            <att name="_kind" value="77"/>
+                            <att name="ecl" value="INDEX({ string40 name, string80 address }, &apos;names&apos;, fileposition(false));&#10;FILTER(KEYED(name = STORED(&apos;searchname&apos;)));&#10;"/>
+                            <att name="recordSize" value="120"/>
+                            <att name="predictedCount" value="0..?[disk]"/>
+                            <att name="_fileName" value="names"/>
+                        </node>
+                        <node id="3" label="Output&#10;Result #1">
+                            <att name="definition" value="workuniteg1.ecl(4,1)"/>
+                            <att name="_kind" value="16"/>
+                            <att name="ecl" value="OUTPUT(..., workunit);&#10;"/>
+                            <att name="recordSize" value="120"/>
+                        </node>
+                    </graph>
+                </att>
+            </node>
+        </graph>
+    </xgmml>
+</Graph>

Each graph (e.g. graph1) consists of 1 or more subgraphs (in the example above, node id=1). Each of those subgraphs contains 1 or more activities (node id=2, node id=3).

The xml for each activity might contain the following information:

  • A unique id (e.g. id="2").
  • The "kind" of the activity, e.g. <att name="_kind" value="77"/>. The value is an item from the enum ThorActivityKind in eclhelper.hpp.
  • The ECL that created the activity. E.g. <att name="ecl" value="...">
  • The identifier of the ecl definition. E.g. <att name="name" value="results"/>
  • Location (e.g. file, line number, column) of the original ECL. E.g. <att name="definition" value="workuniteg1.ecl(3,1)"/>
  • Meta information the code generator has deduced about the activity output. Examples include the record size, expected number of rows, sort order etc. E.g. <att name="recordSize" value="120"/>
  • Hints, which are used for fine control of options for a particular activity (e.g,, the number of threads to use while sorting).
  • Record counts and stats once the job has executed. (These are logically associated with the activities in the graph, but stored separately.)

Graphs also contain edges that can take one of 3 forms:

Edges within graphs

: These are used to indicate how the activities are connected. The source activity is used as the input to the target activity. These edges have the following format:

    <edge id="<source-activity-id>_<output-count>" source="<source-activity-id>" target="<target-activity-id">
+
+There is only one edge in our example workunit: \<edge id="2\_0"
+source="2" target="3"/\>.
+

Edges between graphs

: These are used to indicate direct dependencies between activities. For instance there will be an edge connecting the activity that writes a spill file to the activity that reads it. These edges have the following format:

    <edge id="<source-activity-id>_<target-activity-id>" source="<source-subgraph-id>" target="<target-subgraph-id>"
+       <att name="_sourceActivity" value="<source-activity-id>"/>
+       <att name="_targetActivity" value="<target-activity-id>"/>
+    </edge>
+
+Roxie often optimizes spilled datasets and treats these edges as
+equivalent to the edges between activities.
+

Other dependencies.

: These are similar to the edges between graphs, but they are used for values that are used within an activity. For instance one part of the graph may calculate the maximum value of a column, and another activity may filter out all records that do not match that maximum value. The format is the same as the edges between graphs except that the edge contains the following attribute:

    <att name="_dependsOn" value="1"/>
+

Each activity in a graph also has a corresponding helper class instance in the generated code. (The name of the class is "cAc" followed by the activity number, and the exported factory method is "fAc" followed by the activity number.) Each helper class implements a specialised interface (derived from IHThorArg) - the particular interface is determined by the value of the "_kind" attribute for the activity.

The contents of file rtl/include/eclhelper.hpp is key to understanding how the generated code relates to the activities. Each kind of activity requires a helper class that implements a specific interface. The helpers allow the engine to tailor the generalised activity implementation to the the particular instance - e.g. for a filter activity whether a row should be included or excluded. The appendix at the end of this document contains some further information about this file.

The classes in the generated workunits are normally derived from base implementations of those interfaces (which are implemented in rtl/include/eclhelper_base.hpp). This reduces the size of the generated code by providing default implementations for various functions.

For instance the helper for the index read (activity 2) is defined as:

cpp
struct cAc2 : public CThorIndexReadArg {
+    virtual unsigned getFormatCrc() {
+        return 470622073U;
+    }
+    virtual bool getIndexLayout(size32_t & __lenResult, void * & __result) { getLayout5(__lenResult, __result, ctx); return true; }
+    virtual IOutputMetaData * queryDiskRecordSize() { return &mx1; }
+    virtual IOutputMetaData * queryOutputMeta() { return &mx1; }
+    virtual void onCreate(ICodeContext * _ctx, IHThorArg *, MemoryBuffer * in) {
+        ctx = _ctx;
+        ctx->getResultString(v2,v1.refstr(),"searchname",4294967295U);
+    }
+    rtlDataAttr v1;
+    unsigned v2;
+    virtual const char * getFileName() {
+        return "names";
+    }
+    virtual void createSegmentMonitors(IIndexReadContext *irc) {
+        Owned<IStringSet> set3;
+        set3.setown(createRtlStringSet(40));
+        char v4[40];
+        rtlStrToStr(40U,v4,v2,v1.getstr());
+        if (rtlCompareStrStr(v2,v1.getstr(),40U,v4) == 0) {
+            set3->addRange(v4,v4);
+        }
+        irc->append(createKeySegmentMonitor(false, set3.getClear(), 0, 40));
+        irc->append(createWildKeySegmentMonitor(40, 80));
+    }
+    virtual size32_t transform(ARowBuilder & crSelf, const void * _left) {
+        crSelf.getSelf();
+        unsigned char * left = (unsigned char *)_left;
+        memcpy(crSelf.row() + 0U,left + 0U,120U);
+        return 120U;
+    }
+};

Some of the methods to highlight are:

a) onCreate() - common to all activities. It is called by the engine when the helper is first created, and allows the helper to cache information that does not change - in this case the name that is being searched for. b) getFileName() - determines the name of the index being read. c) createSegmentMonitors() - defines which columns to filter, and which values to match against. d) transform() - create the record to return from the activity. It controls which columns should be included from the index row in the output. (In this case all.)

Executing the graph

To execute a graph, the engine walks the activities in the graph xml and creates, in memory, a graph of implementation activities.

For each activity, the name of the helper factory is calculated from the activity number (e.g. fAc2 for activity 2). That function is imported from the loaded dll, and then called to create an instance of the generated helper class - in this case cAc2.

The engine then creates an instance of the class for implementing the activity, and passes the previously created helper object to the constructor. The engine uses the _kind attribute in the graph to determine which activity class should be used. E.g. In the example above activity 2 has a _kind of 77, which corresponds to TAKindexread. For an index-read activity roxie will create an instance of CRoxieServerIndexReadActivity. (The generated helper that is passed to the activity instance will implement IHThorIndexReadArg). The activity implementations may also extract other information from the xml for the activity - e.g. hints. Once all the activities are created the edge information is used to link inputs activities to output activities and add other dependencies.

Note: Any subgraph that is marked with <att name="rootGraph" value="1"/> is a root subgraph. An activity within a subgraph that has no outputs is called a 'sink' (and an activity without any inputs is called a 'source').

Executing a graph involves executing all the root subgraphs that it contains. All dependencies of the activities within the subgraph must be executed before a subgraph is executed. To execute a subgraph, the engine executes each of the sink activities on separate threads, and then waits for each of those threads to complete. Each sink activity lazily pulls input rows on demand from activities further up the graph, processes them and returns when complete.

(If you examine the code you will find that this is a simplification. The implementation for processing dependencies is more fine grained to ensure IF datasets, OUPUT(,UPDATE) and other ECL constructs are executed correctly.)

In our example the execution flows as follows:

  1. Only a single root subgraph, so need to execute that.
  2. The engine will execute activity 3 - the workunit-write activity (TAKworkunitwrite).
  3. The workunit-write activity will start its input.
  4. The index-read activity will call the helper functions to obtain the filename, resolve the index, and create the filter.
  5. The workunit-write activity requests a row from its input.
  6. The index-read finds the first row, and calls the helper's transform() method to create an output row.
  7. The workunit-write activity persists the row to a buffer (using the serializer provided by the IOutputMetaData interface implemented by the class mx1).
  8. Back to step 5, workunit-write reading a row from its input, until end of file is returned (notified as two consecutive NULL rows.
  9. Workunit-write commits the results and finishes.

The execution generally switches back and forth between the code in the engines, and the members of the generated helper classes.

There are some other details of query execution that are worth highlighting:

Child Queries

: Some activities perform complicated operations on child datasets of the input rows. E.g. remove all duplicate people who are marked as living at this address. This will create a "child query" in the graph - i.e. a nested graph within a subgraph, which may be executed each time a new input row is processed by the containing activity. (The graph of activities for each child query is created at the same time as the parent activity. The activity instances are reinitialised and re-executed for each input row processed by the parent activity to minimise the create-time overhead.)

Other helper functions

: The generated code contains other functions that are used to describe the meta information for the rows processed within the graph. E.g. the following class describes the output from the index read activity:

cpp
struct mi1 : public CFixedOutputMetaData {
+    inline mi1() : CFixedOutputMetaData(120) {}
+    virtual const RtlTypeInfo * queryTypeInfo() const { return &ty1; }
+} mx1;
This represents a fixed size row that occupies 120 bytes. The object
+returned by the queryTypeInfo() function provides information about
+the types of the fields:
+
cpp
const RtlStringTypeInfo ty2(0x4,40);
+const RtlFieldStrInfo rf1("name",NULL,&ty2);
+const RtlStringTypeInfo ty3(0x4,80);
+const RtlFieldStrInfo rf2("address",NULL,&ty3);
+const RtlFieldInfo * const tl4[] = { &rf1,&rf2, 0 };
+const RtlRecordTypeInfo ty1(0xd,120,tl4);
I.e. a string column, length 40 called "name", followed by a
+string column, length 80 called "address". The interface
+IOutputMetaData in eclhelper.hpp is key to understanding how the
+rows are processed.
+

Inline dataset operations

: The rule mentioned at the start - that the generated code does not contain any knowledge of how to perform a particular dataset operation - does have one notable exception. Some operations on child datasets are very simple to implement, and more efficient if they are implemented using inline C++ code. (The generated code is smaller, and they avoid the overhead of setting up a child graph.) Examples include filtering and aggregating column values from a child dataset.

The full code in the different engines is more complicated than the simplified process outlined above, especially when it comes to executing dependencies, but the broad outline is the same.

Appendix

More information on the work done in the code generator to create the workunit can be found in ecl/eclcc/DOCUMENTATION.rst.

The C++ code can be generated as a single C++ file or multiple files. The system defaults to multiple C++ files, so that they can be compiled in parallel (and to avoid problems some compilers have with very large files). When multipe C++ files are generated the metadata classes and workflow classes are generated in the main module, and the activities are generated in the files suffixed with a number. It may be easier to understand the generated code if it is in one place. In which case, compile your query with the option -fspanMultipleCpp=0. Use -fsaveCppTempFiles to ensure the C++ files are not deleted (the C++ files will appear as helpers in the workunit details).

Key types and interfaces from eclhelper.hpp

IEclProcess

: The interface that is used by the workflow engine to execute the different workflow items in the generated code.

ThorActivityKind

: This enumeration contains one entry for each activity supported by the engines.

ICodeContext

: This interface is implemented by the engine, and provides a mechanism for the generated code to call back into the engine. For example resolveChildQuery() is used to obtain a reference to a child query that can then be executed later.

IOutputMetaData

: This interface is used to describe any meta data associated with the data being processed by the queries.

IHThorArg

: The base interface for defining information about an activity. Each activity has an associated interface that is derived from this interface. E.g. each instance of the sort activity will have a helper class implementing IHThorSortArg in the generated query. There is normally a corresponding base class for each interface in eclhelper_base.hpp that is used by the generated code e.g. CThorSortArg.

ARowBuilder

: This abstract base class is used by the transform functions to reserve memory for the rows that are created.

IEngineRowAllocator

: Used by the generated code to allocate rows and rowsets. Can also be used to release rows (or call the global function rtlReleaseRow()).

IGlobalCodeContext

: Provides access to functions that cannot be called inside a graph - i.e. can only be called from the global workflow code. Most functions are related to the internal implementation of particular workflow item types (e.g. persists).

Glossary

activity

: An activity is the basic unit of dataset processing implemented by the engines. Each activity corresponds to a node in the thor execution graph. Instances of the activities are connnected together to create the graph.

dll

: A dynamically loaded library. These correspond to shared objects in Linux (extension '.so'), dynamic libraries in Max OS X ('.dylib'), and dynamic link libraries in windows ('.dll').

superfile

: A composite file which allows a collection of files to be treated as a single compound file.

?What else should go here?

Full text of the workunit XML

The XML for a workunit can be viewed on the XML tag in eclwatch, or generated by compiling the ECL using the -wu option with eclcc. Alternatively eclcc -b -S can be used to generate the XML and the C++ at the same time (the output filenames are derived from the input name).

xml
<W_LOCAL buildVersion="internal_5.3.0-closedown0"
+    cloneable="1"
+    codeVersion="157"
+    eclVersion="5.3.0"
+    hash="2344844820"
+    state="completed"
+    xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance">
+    <Debug>
+        <addtimingtoworkunit>0</addtimingtoworkunit>
+        <debugnlp>1</debugnlp>
+        <expandpersistinputdependencies>1</expandpersistinputdependencies>
+        <forcegenerate>1</forcegenerate>
+        <noterecordsizeingraph>1</noterecordsizeingraph>
+        <regressiontest>1</regressiontest>
+        <showmetaingraph>1</showmetaingraph>
+        <showrecordcountingraph>1</showrecordcountingraph>
+        <spanmultiplecpp>0</spanmultiplecpp>
+        <targetclustertype>hthor</targetclustertype>
+    </Debug>
+    <Graphs>
+        <Graph name="graph1" type="activities">
+            <xgmml>
+                <graph wfid="2">
+                    <node id="1">
+                        <att>
+                            <graph>
+                                <att name="rootGraph" value="1" />
+                                <edge id="2_0" source="2" target="3" />
+                                <node id="2" label="Index Read&#10;&apos;names&apos;">
+                                    <att name="definition" value="workuniteg1.ecl(3,1)" />
+                                    <att name="name" value="results" />
+                                    <att name="_kind" value="77" />
+                                    <att name="ecl"
+                                        value="INDEX({ string40 name, string80 address }, &apos;names&apos;, fileposition(false));&#10;FILTER(KEYED(name = STORED(&apos;searchname&apos;)));&#10;" />
+                                    <att name="recordSize" value="120" />
+                                    <att name="predictedCount" value="0..?[disk]" />
+                                    <att name="_fileName" value="names" />
+                                </node>
+                                <node id="3" label="Output&#10;Result #1">
+                                    <att name="definition" value="workuniteg1.ecl(4,1)" />
+                                    <att name="_kind" value="16" />
+                                    <att name="ecl" value="OUTPUT(..., workunit);&#10;" />
+                                    <att name="recordSize" value="120" />
+                                </node>
+                            </graph>
+                        </att>
+                    </node>
+                </graph>
+            </xgmml>
+        </Graph>
+    </Graphs>
+    <Query fetchEntire="1">
+        <Associated>
+            <File desc="workuniteg1.ecl.cpp"
+                filename="c:\regressout\workuniteg1.ecl.cpp"
+                ip="10.121.159.73"
+                type="cpp" />
+        </Associated>
+    </Query>
+    <Results>
+        <Result isScalar="0"
+            name="Result 1"
+            recordSizeEntry="mf1"
+            rowLimit="-1"
+            sequence="0">
+            <SchemaRaw xsi:type="SOAP-ENC:base64">
+                name&#xe000;&#xe004;(&#xe000;&#xe000;&#xe000;&#xe001;ascii&#xe000;&#xe001;ascii&#xe000;address&#xe000;&#xe004;P&#xe000;&#xe000;&#xe000;&#xe001;ascii&#xe000;&#xe001;ascii&#xe000;&#xe000;&#xe018;%&#xe000;&#xe000;&#xe000;{
+                string40 name, string80 address };&#10; </SchemaRaw>
+        </Result>
+        <Result name="Result 2" sequence="1">
+            <SchemaRaw xsi:type="SOAP-ENC:base64">
+                Result_2&#xe000;&#xe004;&#241;&#255;&#255;&#255;&#xe001;ascii&#xe000;&#xe001;ascii&#xe000;&#xe000;&#xe018;&#xe000;&#xe000;&#xe000;&#xe000;
+            </SchemaRaw>
+        </Result>
+    </Results>
+    <Statistics>
+        <Statistic c="eclcc"
+            count="1"
+            creator="eclcc"
+            kind="SizePeakMemory"
+            s="compile"
+            scope=">compile"
+            ts="1428933081084000"
+            unit="sz"
+            value="27885568" />
+    </Statistics>
+    <Variables>
+        <Variable name="searchname">
+            <SchemaRaw xsi:type="SOAP-ENC:base64">
+                searchname&#xe000;&#xe004;&#241;&#255;&#255;&#255;&#xe001;ascii&#xe000;&#xe001;ascii&#xe000;&#xe000;&#xe018;&#xe000;&#xe000;&#xe000;&#xe000;
+            </SchemaRaw>
+        </Variable>
+    </Variables>
+    <Workflow>
+        <Item mode="normal"
+            state="null"
+            type="normal"
+            wfid="1" />
+        <Item mode="normal"
+            state="reqd"
+            type="normal"
+            wfid="2">
+            <Dependency wfid="1" />
+            <Schedule />
+        </Item>
+    </Workflow>
+</W_LOCAL>

Full contents of the generated C++ (as a single file)

cpp
/* Template for generating thor/hthor/roxie output */
+#include "eclinclude4.hpp"
+#include "eclrtl.hpp"
+#include "rtlkey.hpp"
+
+extern RTL_API void rtlStrToStr(size32_t lenTgt,void * tgt,size32_t lenSrc,const void * src);
+extern RTL_API int rtlCompareStrStr(size32_t lenL,const char * l,size32_t lenR,const char * r);
+
+
+const RtlStringTypeInfo ty2(0x4,40);
+const RtlFieldStrInfo rf1("name",NULL,&ty2);
+const RtlStringTypeInfo ty3(0x4,80);
+const RtlFieldStrInfo rf2("address",NULL,&ty3);
+const RtlFieldInfo * const tl4[] = { &rf1,&rf2, 0 };
+const RtlRecordTypeInfo ty1(0xd,120,tl4);
+void getLayout5(size32_t & __lenResult, void * & __result, IResourceContext * ctx) {
+    rtlStrToDataX(__lenResult,__result,87U,"\000R\000\000\000\001x\000\000\000\002\000\000\000\003\004\000\000\000name\004(\000\000\000\001ascii\000\001ascii\000\000\000\000\000\000\003\007\000\000\000address\004P\000\000\000\001ascii\000\001ascii\000\000\000\000\000\000\002\000\000\000");
+}
+struct mi1 : public CFixedOutputMetaData {
+    inline mi1() : CFixedOutputMetaData(120) {}
+    virtual const RtlTypeInfo * queryTypeInfo() const { return &ty1; }
+} mx1;
+extern "C" ECL_API IOutputMetaData * mf1() { mx1.Link(); return &mx1; }
+
+struct cAc2 : public CThorIndexReadArg {
+    virtual unsigned getFormatCrc() {
+        return 470622073U;
+    }
+    virtual bool getIndexLayout(size32_t & __lenResult, void * & __result) { getLayout5(__lenResult, __result, ctx); return true; }
+    virtual IOutputMetaData * queryDiskRecordSize() { return &mx1; }
+    virtual IOutputMetaData * queryOutputMeta() { return &mx1; }
+    virtual void onCreate(ICodeContext * _ctx, IHThorArg *, MemoryBuffer * in) {
+        ctx = _ctx;
+        ctx->getResultString(v2,v1.refstr(),"searchname",4294967295U);
+    }
+    rtlDataAttr v1;
+    unsigned v2;
+    virtual const char * getFileName() {
+        return "names";
+    }
+    virtual void createSegmentMonitors(IIndexReadContext *irc) {
+        Owned<IStringSet> set3;
+        set3.setown(createRtlStringSet(40));
+        char v4[40];
+        rtlStrToStr(40U,v4,v2,v1.getstr());
+        if (rtlCompareStrStr(v2,v1.getstr(),40U,v4) == 0) {
+            set3->addRange(v4,v4);
+        }
+        irc->append(createKeySegmentMonitor(false, set3.getClear(), 0, 40));
+        irc->append(createWildKeySegmentMonitor(40, 80));
+    }
+    virtual size32_t transform(ARowBuilder & crSelf, const void * _left) {
+        crSelf.getSelf();
+        unsigned char * left = (unsigned char *)_left;
+        memcpy(crSelf.row() + 0U,left + 0U,120U);
+        return 120U;
+    }
+};
+extern "C" ECL_API IHThorArg * fAc2() { return new cAc2; }
+struct cAc3 : public CThorWorkUnitWriteArg {
+    virtual int getSequence() { return 0; }
+    virtual IOutputMetaData * queryOutputMeta() { return &mx1; }
+    virtual void serializeXml(const byte * self, IXmlWriter & out) {
+        mx1.toXML(self, out);
+    }
+};
+extern "C" ECL_API IHThorArg * fAc3() { return new cAc3; }
+
+
+struct MyEclProcess : public EclProcess {
+    virtual unsigned getActivityVersion() const { return ACTIVITY_INTERFACE_VERSION; }
+    virtual int perform(IGlobalCodeContext * gctx, unsigned wfid) {
+        ICodeContext * ctx;
+        ctx = gctx->queryCodeContext();
+        switch (wfid) {
+            case 1U:
+                if (!gctx->isResult("searchname",4294967295U)) {
+                    ctx->setResultString("searchname",4294967295U,5U,"Smith");
+                }
+                break;
+            case 2U: {
+                ctx->executeGraph("graph1",false,0,NULL);
+                ctx->setResultString(0,1U,5U,"Done!");
+            }
+            break;
+        }
+        return 2U;
+    }
+};
+
+
+extern "C" ECL_API IEclProcess* createProcess()
+{
+
+    return new MyEclProcess;
+}

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/devdoc/hpcc-icon.png b/devdoc/hpcc-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9ab1596012219f8fb0cd7c38cfd3c7ca36f042d3 GIT binary patch literal 6513 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 HTZR4)kG_?K literal 0 HcmV?d00001 diff --git a/devdoc/hpccsystems.png b/devdoc/hpccsystems.png new file mode 100644 index 0000000000000000000000000000000000000000..2fd32ce97367a2b0f0ad8b324cd67da63239cdfe GIT binary patch literal 28462 zcmXtfWmFtn(=P7rE`z(fI|&X6?(P=c-6gm~2yTPB4KPS>NpKk)f&>c^?9O?=b$@h^ ztkrwhuBtA5Dqd4v2@{PB4F(1V^MkUyHVh1$G4%TY3KH}V!Ct_DexSN58+?I*!2k^!zb1o*uQdT?D<26i~%(Y0q3jfC!DX03Iyag zI$HIsO^KMFn`5!(20Q3!bH?z@W{Ch5J#jYdZg;}4gfonjl8w)Z|Ha$e-WfN2`%pV`W< zbV2UOuUz{I>8qzket9Hwy#BMZH?dKt$xMJ24e)4dE)Fv}>8|Kvvn{hDRv={Hkyk-% zn&6))6HqI_rf|qV!cbche;toXsEMLhT6KdiRqL=W20~imUvbRuAoH8zpSes+Xq0w> z4^L5K7=SJ+TS{WJXzcFL#pc94S+`FFpxv-_%=ck+Pj|>y;a98sy(8&pL3rvBF?YtN zcB`hgqzl89Tlth;>CkZ9H*3)-0vQ<$MSojHn!eP1=8BXpg_4?3U0wlM{G#GCFw8M#lz{|$4<5BI5}tBbNn-P{A#_hxjFJfe9I9|C;O&qOT{pkylS`I zZ#rWHwQtJ-L4OM}ueARY&oHd^CwEFb!|OQ2CH^YM{CiS}=o0_!xc*gAJA^F%smpY! ztt7AV>1(CK7SFbvhXB>4!cK~SpotPx2+-}nMntI0u?(3QM6y+p#IfKwh8d|Y^%*C& zdW~4ncf}Ncsj74sgt-({Yo)6U!+bocF)FP$`A>kFtMX{5oS1HZ7yaP(?4ZJdN=Xud zq=wUP?B9!A$Ay5}EmMBwE73`U!-cl1;{SGYre#3LDK<**9ZUB!j;+YA+(HYQ&P2O% zp#AS5q75II9e%ajx%|qfvVmh*YN$|NkULUtSlRw#vB#rj(#2`4QTulYtSiC*62YBY z;y(n1PnEC_uqAK40DG!2{{3~IY1ylq|V@|VSB5T5V z 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 literal 0 HcmV?d00001 diff --git a/devdoc/hpccsystemsdark.png b/devdoc/hpccsystemsdark.png new file mode 100644 index 0000000000000000000000000000000000000000..642e1ad6670865e03ab1fa715c561038740adb90 GIT binary patch literal 21701 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 + + + + + Quantile 1 - What is it? | HPCC Platform + + + + + + + + + + + + + +
Skip to content

Quantile 1 - What is it?

This series of blog posts started life as a series of walk-throughs and brainstorming sessions at a team offsite. This series will look at adding a new activity to the system. The idea is to give an walk through of the work involved, to highlight the different areas that need changing, and hopefully encourage others to add their own activities. In parallel with the description in this blog there is a series of commits to the github repository that correspond to the different stages in adding the activity. Once the blog is completed, the text will also be checked into the source control tree for future reference.

The new activity is going to be a QUANTILE activity, which can be used to find the records that split a dataset into equal sized blocks. Two common uses are to find the median of a set of data (split into 2) or percentiles (split into 100). It can also be used to split a dataset for distribution across the nodes in a system. One hope is that the classes used to implement quantile in Thor can also be used to improve the performance of the global sort operation.

It may seem fatuous, but the first task in adding any activity to the system is to work out what that activity is going to do! You can approach this in an iterative manner - starting with a minimal set of functionality and adding options as you think of them - or start with a more complete initial design. We have used both approaches in the past to add capabilities to the HPCC system, but on this occasion we will be starting from a more complete design - the conclusion of our initial design discussion:

"What are the inputs, options and capabilities that might be useful in a QUANTILE activity?"

The discussion produced the following items:

  • Which dataset is being processed?

    This is always required and should be the first argument to the activity.

  • How many parts to split the dataset into?

    This is always required, so it should be the next argument to the activity.

  • Which fields are being used to order (and split) the dataset?

    Again this is always required, so the list of fields should follow the number of partitions.

  • Which fields are returned?

    Normally the input row, but often it would be useful for the output to include details of which quantile a row corresponds to. To allow this an optional transform could be passed the input row as LEFT and the quantile number as COUNTER.

  • How about first and last rows in the dataset?

    Sometimes it is also useful to know the first and last rows. Add flags to allow them to be optionally returned.

  • How do you cope with too few input rows (including an empty input)?

    After some discussion we decided that QUANTILE should always return the number of parts requested. If there were fewer items in the input they would be duplicated as appropriate. We should provide a DEDUP flag for the situations when that is not desired. If there is an empty dataset as input then the default (blank) row will be created.

  • Should all rows have the same weighting?

    Generally you want the same weighting for each row. However, if you are using QUANTILE to split your dataset, and the cost of the next operation depends on some feature of the row (e.g., the frequency of the firstname) then you may want to weight the rows differently.

  • What if we are only interested in the 5th and 95th centiles?

    We could optionally allow a set of values to be selected from the results.

There were also some implementation details concluded from the discussions:

  • How accurate should the results be?

    The simplest implementation of QUANTILE (sort and then select the correct rows) will always produce accurate results. However, there may be some implementations that can produce an approximate answer more quickly. Therefore we could add a SKEW attribute to allow early termination.

  • Does the implementation need to be stable?

    In other words, if there are rows with identical values for the ordering fields, but other fields not part of the ordering with different values, does it matter which of those rows are returned? Does the relative order within those matching rows matter?

    The general principle in the HPCC system is that sort operations should be stable, and that where possible activities return consistent, reproducible results. However, that often has a cost - either in performance or memory consumption. The design discussion highlighted the fact that if all the fields from the row are included in the sort order then the relative order does not matter because the duplicate rows will be indistinguishable. (This is also true for sorts, and following the discussion an optimization was added to 5.2 to take advantage of this.) For the QUANTILE activity we will add an ECL flag, but the code generator should also aim to spot this automatically.

  • Returning counts of the numbers in each quantile might be interesting.

    This has little value when the results are exact, but may be more useful when a SKEW is specified to allow an approximate answer, or if a dataset might have a vast numbers of duplicates. It is possibly something to add to a future version of the activity. For an approximate answer, calculating the counts is likely to add an additional cost to the implementation, so the target engine should be informed if this is required.

  • Is the output always sorted by the partition fields?

    If this naturally falls out of the implementations then it would be worth including it in the specification. Initially we will assume not, but will revisit after it has been implemented.

After all the discussions we arrived at the following syntax:

QUANTILE(<dataset>, <number-of-ranges>, { sort-order } [, <transform>(LEFT, COUNTER)]
+        [,FIRST][,LAST][,SKEW(<n>)][,UNSTABLE][,SCORE(<score>)][,RANGE(set)][,DEDUP][,LOCAL]
+
+FIRST - Match the first row in the input dataset (as quantile 0)
+LAST -  Match the last row in the input dataset (as quantile <n>)
+SKEW -  The maximum deviation from the correct results allowed.  Defaults to 0.
+UNSTABLE - Is the order of the original input values unimportant?
+SCORE - What weighting should be applied for each row.  Defaults to 1.
+RANGE - Which quantiles should actually be returned.  (Defaults to ALL).
+DEDUP - Avoid returning a match for an input row more than once.
+

We also summarised a few implementation details:

  • The activity needs to be available in GLOBAL, LOCAL and GROUPED variants.
  • The code generator should derive UNSTABLE if no non-sort fields are returned.
  • Flags to indicate if a score/range is required.
  • Flag to indicate if a transform is required.

Finally, deciding on the name of the activity took almost as long as designing it!

The end result of this process was summarised in a JIRA issue: https://track.hpccsystems.com/browse/HPCC-12267, which contains details of the desired syntax and semantics. It also contains some details of the next blog topic - test cases.

Incidentally, a question that arose from of the design discussion was "What ECL can we use if we want to annotate a dataset with partition points?". Ideally the user needs a join activity which walks through a table of rows, and matches against the first row that contains key values less than or equal to the values in the search row. There are other situations where that operation would also be useful. Our conclusion was that the system does not have a simple way to achieve that, and that it was a deficiency in the current system, so another JIRA was created (see https://track.hpccsystems.com/browse/HPCC-13016). This is often how the design discussions proceed, with discussions in one area leading to new ideas in another. Similarly we concluded it would be useful to distribute rows in a dataset based on a partition (see https://track.hpccsystems.com/browse/HPCC-13260).

Quantile 2 - Test cases

When adding new features to the system, or changing the code generator, the first step is often to write some ECL test cases. They have proved very useful for several reasons:

  • Developing the test cases can help clarify issues, and other details that the implementation needs to take into account. (E.g., what happens if the input dataset is empty?)
  • They provide something concrete to aim towards when implementing the feature.
  • They provide a set of milestones to show progress.
  • They can be used to check the implementation on the different engines.

As part of the design discussion we also started to create a list of useful test cases (they follow below in the order they were discussed). The tests perform varying functions. Some of the tests are checking that the core functionality works correctly, while others check unusual situations and that strange boundary cases are covered. The tests are not exhaustive, but they are a good starting point and new tests can be added as the implementation progresses.

The following is the list of tests that should be created as part of implementing this activity:

  1. Compare with values extracted from a SORT. Useful to check the implementation, but also to ensure we clearly define which results we are expecting.
  2. QUANTILE with a number-of-ranges = 1, 0, and a very large number. Should also test the number of ranges can be dynamic as well as a constant.
  3. Empty dataset as input.
  4. All input entries are duplicates.
  5. Dataset smaller than number of ranges.
  6. Input sorted and reverse sorted.
  7. Normal data with small number of entries.
  8. Duplicates in the input dataset that cause empty ranges.
  9. Random distribution of numbers without duplicates.
  10. Local and grouped cases.
  11. SKEW that fails.
  12. Test scoring functions.
  13. Testing different skews that work on the same dataset.
  14. An example that uses all the keywords.
  15. Examples that do and do not have extra fields not included in the sort order. (Check that the unstable flag is correctly deduced.)
  16. Globally partitioned already (e.g., globally sorted). All partition points on a single node.
  17. Apply quantile to a dataset, and also to the same dataset that has been reordered/distributed. Check the resulting quantiles are the same.
  18. Calculate just the 5 and 95 centiles from a dataset.
  19. Check a non constant number of splits (and also in a child query where it depends on the parent row).
  20. A transform that does something interesting to the sort order. (Check any order is tracked correctly.)
  21. Check the counts are correct for grouped and local operations.
  22. Call in a child query with options that depend on the parent row (e.g., num partitions).
  23. Split points that fall in the middle of two items.
  24. No input rows and DEDUP attribute specified.

Ideally any test cases for features should be included in the runtime regression suite, which is found in the testing/regress directory in the github repository. Tests that check invalid syntax should go in the compiler regression suite (ecl/regress). Commit https://github.com/ghalliday/HPCC-Platform/commit/d75e6b40e3503f851265670a27889d8adc73f645 contains the test cases so far. Note, the test examples in that commit do not yet cover all the cases above. Before the final pull request for the feature is merged the list above should be revisited and the test suite extended to include any missing tests.

In practice it may be easier to write the test cases in parallel with implementing the parser -since that allows you to check their syntax. Some of the examples in the commit were created before work was started on the parser, others during, and some while implementing the feature itself.

Quantile 3 - The parser

The first stage in implementing QUANTILE will be to add it to the parser. This can sometimes highlight issues with the syntax and cause revisions to the design. In this case there were two technical issues integrating the syntax into the grammar. (If you are not interested in shift/reduce conflicts you may want to skip a few paragraphs and jump to the walkthrough of the changes.)

Originally, the optional transform was specified inside an attribute, e.g., something like OUTPUT(transform). However, this was not very consistent with the way that other transforms were implemented, so the syntax was updated so it became an optional transform following the partition field list.

When the syntax was added to the grammar we hit another problem: Currently, a single production (sortList) in the grammar is used for matching sort orders. As well as accepting fields from a dataset the sort order production has been extended to accept any named attribute that can follow a sort order (e.g., LOCAL). This is because (with one token lookahead) it is ambiguous where the sort order finishes and the list of attributes begins. Trying to include transforms in those productions revealed other problems:

  • If a production has a sortList followed by a transform (or attribute) then it introduces a shift/reduce error on ','. To avoid the ambiguity all trailing attributes or values need to be included in the sortList.
  • Including a transform production in the sortList elements causes problems with other transform disambiguation (e.g., DATASET[x] and AGGREGATE).
  • We could require an attribute around the transform e.g., OUTPUT(transform), but that does not really fit in with other activities in the language.
  • We could change the parameter order, e.g., move the transform earlier, but that would make the syntax counter-intuitive.
  • We could require { } around the list - but this is inconsistent with some of the other sort orders.

In order to make some progress I elected to choose the last option and require the sort order to be included in curly braces. There are already a couple of activities - subsort and a form of atmost that similarly require them (and if redesigning ECL from scratch I would be tempted to require them everywhere). The final syntax is something that will need further discussion as part of the review of the pull request though, and may need to be revisited.

Having decided how to solve the ambiguities in the grammar, the following is a walkthrough of the changes that were made as part of commit https://github.com/ghalliday/HPCC-Platform/commit/3d623d1c6cd151a0a5608aa20ae4739a008f6e44:

  • no_quantile in hqlexpr.hpp

    The ECL query is represented by a graph of "expression" nodes - each has a "kind" that comes from the enumeration _node_operator. The first requirement is to add a new enumeration to represent the new activity - in this case we elected to reuse an unused placeholder. (These placeholders correspond to some old operators that are no longer supported. They have not been removed because the other elements in the enumeration need to keep the same values since they are used for calculating derived persistent values e.g., the hashes for persists.)

  • New attribute names in hqlatoms.

    The quantile activity introduces some new attribute names that have not been used before. All names are represented in an atom table, so the code in hqlatoms.hpp/cpp is updated to define the new atoms.

  • Properties of no_quantile

    There are various places that need to be updated to allow the system to know about the properties of the new operator:

    • hqlattr

      This contains code to calculate derived attributes. The first entry in the case statement is currently unused (the function should be removed). The second, inside calcRowInformation(), is used to predict how many rows are generated by this activity. This information is percolated through the graph and is used for optimizations, and input counts can be used to select the best implementation for a particular activity.

    • hqlexpr

      Most changes are relatively simple including the text for the operator, whether it is constant, and the number of dataset arguments it has. One key function is getChildDatasetType() that indicates the kind of dataset arguments the operator has, which in turn controls how LEFT/RIGHT are interpreted. In this case some of the activity arguments (e.g., the number of quantiles) implicitly use fields within the parent dataset, and the transform uses LEFT, so the operator returns childdataset_datasetleft.

    • hqlir

      This entry is used for generating an intermediate representation of the graph. This can be useful for debugging issues. (Running eclcc with the logging options "--logfile xxx" and "--logdetail 999" will include details of the expression tree at each point in the code generation process in the log file. Also defining -ftraceIR will output the graphs in the IR format.)

    • hqlfold

      This is the constant folder. At the moment the only change is to ensure that fields that are assigned constants within the transform are processed correctly. Future work could add code to optimize quantile applied to an empty dataset, or selecting 1 division.

    • hqlmeta

      Similar to the functions in hqlattr that calculate derived attributes, these functions are used to calculate how the rows coming out of an activity are sorted, grouped and distributed. It is vital to only preserve information that is guaranteed to be true - otherwise invalid optimizations might be performed on the rest of the expression tree.

    • reservedwords.cpp

      A new entry indicating which category the keyword belongs to.

Finally we have the changes to the parser to recognise the new syntax:

  • hqllex.l

    This file contains the lexer that breaks the ecl file into tokens. There are two new tokens - QUANTILE and SCORE.

  • Hqlgram.y

    This file contains the grammar that matches the language. There are two productions - one that matches the version of QUANTILE with a transform and one without. (Two productions are used instead of an optional transform to avoid shift/reduce errors.)

  • hqlgram2.cpp

    This contains the bulk of the code that is executed by the productions in the grammar. Changes here include new entries added to a case statement to get the text for the new tokens, and a new entry in the simplify() call. This helps reduce the number of valid tokens that could follow when reporting a syntax error.

Looking back over those changes, one reflection is that there are lots of different places that need to be changed. How does a programmer know which functions need to change, and what happens if some are missed? In this example, the locations were found by searching for an activity with a similar syntax e.g., no_soapcall_ds or no_normalize.

It is too easy to miss something, especially for somebody new to the code - although if you do then you will trigger a runtime internal error. It would be much better if the code was refactored so that the bulk of the changes were in one place. (See JIRA https://track.hpccsystems.com/browse/HPCC-13434 that has been added to track improvement of the situation.)

With these changes implemented the examples from the previous pull request now syntax check. The next stage in the process involves thinking through the details of how the activity will be implemented.

Quantile 4 - The engine interface.

The next stage in adding a new activity to the system is to define the interface between the generated code and the engines. The important file for this stage is rtl/include/eclhelper.hpp, which contains the interfaces between the engines and the generated code. These interfaces define the information required by the engines to customize each of the different activities. The changes that define the interface for quantile are found in commit https://github.com/ghalliday/HPCC-Platform/commit/06534d8e9962637fe9a5188d1cc4ab32c3925010.

Adding a quantile activity involves the following changes:

  • ThorActivityKind - TAKquantile

    Each activity that the engines support has an entry in this enumeration. This value is stored in the graph as the _kind attribute of the node.

  • ActivityInterfaceEnum - TAIquantilearg_1

    This enumeration in combination with the selectInterface() member of IHThorArg provides a mechanism for helper interfaces to be extended while preserving backwards compatibility with older workunits. The mechanism is rarely used (but valuable when it is), and adding a new activity only requires a single new entry.

  • IHThorArg

    This is the base interface that all activity interfaces are derived from. This interface does not need to change, but it is worth noting because each activity defines a specialized version of it. The names of the specialised interfaces follow a pattern; in this case the new interface is IHThorQuantileArg.

  • IHThorQuantileArg

    The following is an outline of the new member functions, with comments on their use:

    • getFlags()

      Many of the interfaces have a getFlags() function. It provides a concise way of returning several Boolean options in a single call - provided those options do not change during the execution of the activity. The flags are normally defined with explicit values in an enumeration before the interface. The labels often follow the pattern T<First-letter-of-activity>F<lowercase-name>, i.e. TQFxxx ~= Thor-Quantile-Flag-XXX.

    • getNumDivisions()

      Returns how many parts to split the dataset into.

    • getSkew()

      Corresponds to the SKEW() attribute.

    • queryCompare()

      Returns an implementation of the interface used to compare two rows.

    • createDefault(rowBuilder)

      A function used to create a default row - used if there are no input rows.

    • transform(rowBuilder, _left, _counter)

      The function to create the output record from the input record and the partition number (passed as counter).

    • getScore(_left)

      What weighting should be given to this row?

    • getRange(isAll, tlen, tgt)

      Corresponds to the RANGE attribute.

Note that the different engines all use the same specialised interface - it contains a superset of the functions required by the different targets. Occasionally some of the engines do not need to use some of the functions (e.g., to serialize information between nodes) so the code generator may output empty implementations.

For each interface defined in eclhelper.hpp there is a base implementation class defined in eclhelper_base.hpp. The classes generated for each activity in a query by the code generator are derived from one of these base classes. Therefore we need to create a corresponding new class CThorQuantileArg. It often provides default implementations for some of the helper functions to help reduce the size of the generated code (e.g., getScore returning 1).

Often the process of designing the helper interface is dynamic. As the implementation is created, new options or possibilities for optimizations appear. These require extensions and changes to the helper interface in order to be implemented by the engines. Once the initial interface has been agreed, work on the code generator and the engines can proceeded in parallel. (It is equally possible to design this interface before any work on the parser begins, allowing more work to overlap.)

There are some more details on the contents of thorhelper.hpp in the documentation ecl/eclcc/WORKUNIT.rst within the HPCC repository.

Quantile 5 - The code generator

Adding a new activity to the code generator is (surprisingly!) a relatively simple operation. The process is more complicated if the activity also requires an implementation that generates inline C++, but that only applies to a small subset of very simple activities, e.g., filter, aggregate. Changes to the code generator also tend to be more substantial if you add a new type, but that is also not the case for the quantile activity.

For quantile, the only change required is to add a function that generates an implementation of the helper class. The code for all the different activities follows a very similar pattern - generate input activities, generate the helper for this activity, and link the input activities to this new activity. It is often easiest to copy the boiler-plate code from a similar activity (e.g., sort) and then adapt it. (Yes, some of this code could also be refactored... any volunteers?) There are a large number of helper functions available to help generate transforms and other member functions, which also simplifies the process.

The new code is found in commit https://github.com/ghalliday/HPCC-Platform/commit/47f850d827f1655fd6a78fb9c07f1e911b708175.

Most of the code should be self explanatory, but one item is worth highlighting. The code generator builds up a structure in memory that represents the C++ code that is being generated. The BuildCtx class is used to represent a location within that generated code where new code can be inserted. The instance variable contains several BuildCtx members that are used to represent locations to generate code within the helper class (classctx, nestedctx, createctx and startctx). They are used for different purposes:

  • classctx

    Used to generate any member functions that can be called as soon as the helper object has been created, e.g., getFlags().

  • nestedctx

    Used to generate nested member classes and objects - e.g., comparison classes.

  • startctx

    Any function that may return a value that depends on the context/parent activity. For example if QUANTILE is used inside the TRANSFORM of a PROJECT, the number of partition points may depend on a field in the LEFT row of the PROJECT. Therefore the getNumDivisions() member function needs to be generated inside instance->startctx. These functions can only be called by the engine after onCreate() and onStart() have been called to set up the current context.

  • createctx

    Really, this is a historical artefact from many years ago. It was originally used for functions that could be dependent on a global expression, but not a parent row. Almost all such restrictions have since been removed, and those that remain should probably be replaced with either classctx or startctx.

The only other change is to extend the switch statement in common/thorcommon/thorcommon.cpp to add a text description of the activity.

Quantile 6 - Roxie

With the code generator outputting all the information we need, we can now implement the activity in one of the engines. (As I mentioned previously, in practice this is often done in parallel with adding it to the code generator.) Roxie and hThor are the best engines to start with because most of their activities run on a single node - so the implementations tend to be less complicated. It is also relatively easy to debug them, by compiling to create a stand-alone executable, and then running that executable inside a debugger. The following description walks-through the roxie changes:

The changes have been split into two commits to make the code changes easier to follow. The first commit (https://github.com/ghalliday/HPCC-Platform/commit/30da006df9ae01c9aa784e91129457883e9bb8f3) adds the simplest implementation of the activity:

Code is added to ccdquery to process the new TAKquantile activity kind, and create a factory object of the correct type. The implementation of the factory class is relatively simple - it primarily contains a method for creating an instance of the activity class. Some factories create instances of the helper and cache any information that never changes (in this case the value returned by getFlags(), which is a very marginal optimization).

The classes that implement the existing sort algorithms are extended to return the sorted array in a single call. This allows the quicksort variants to be implemented more efficiently.

The class CRoxieServerQuantileActivity contains the code for implementing the quantile activity. It has the following methods:

  • Constructor

    Extracts any information from the helper that does not vary, and initializes all member variables.

  • start()

    This function is called before the graph is executed. It evaluates any helper methods that might vary from execution to execution (e.g., getRange(), numDivisions()), but which do not depend on the current row.

  • reset()

    Called when a graph has finished executing - after an activity has finished processing all its records. It is used to clean up any variables, and restore the activity ready for processing again (e.g., if it is inside a child query).

  • needsAllocator()

    Returns true if this activity creates new rows.

  • nextInGroup()

    The main function in the activity. This function is called by whichever activity is next in the graph to request a new row from the quantile activity. The functions should be designed so they return the next row as quickly as possible, and delay any processing until it is needed. In this case the input is not read and sorted until the first row is requested.

    Note, the call to the helper.transform() returns the size of the resulting row, and returns zero if the row should be skipped. The call to finaliseRowClear() after a successful row creation is there to indicate that the row can no longer be modified, and ensures that any child rows will be correctly freed when the row is freed.

    The function also contains extra logic to ensure that groups are implemented correctly. The end of a group is marked by returning a single NULL row, the end of the dataset by two contiguous NULL rows. It is important to ensure that a group that has all its output rows skipped doesn't return two NULLs in a row - hence the checks for anyThisGroup.

With those changes in place, the second commit https://github.com/ghalliday/HPCC-Platform/commit/aeaa209092ea1af9660c6908062c1b0b9acff36b adds support for the RANGE, FIRST, and LAST attributes. It also optimizes the cases where the input is already sorted, and the version of QUANTILE which does not include a transform. (If you are looking at the change in github then it is useful to ignore whitespace changes by appending ?w=1 to the URL). The main changes are

  • Extra helper methods called in start() to obtain the range.
  • Optimize the situation where the input is known to be sorted by reading the input rows directly into the "sorted" array.
  • Extra checks to see if this quantile should be included in the output (FIRST,LAST,RANGE,DEDUP)
  • An optimization to link the incoming row if the transform does not modify it, by testing the TQFneedtransform flag.

Quantile 7 - Possible roxie improvements

TBD...

hthor - trivial,sharing code and deprecated.

Discussion, of possible improvements.

Hoares' algorithm.

Ln2(n) < 4k?

SKEW and Hoares

Ordered RANGE. Calc offsets from the quantile (see testing/regress/ecl/xxxxx?)

SCORE

Quantile 8 - Thor

TBD

Basic activity structure

Locally sorting and allowing the inputs to spill.

The partitioning approach

Classes

Skew

Optimizations

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/devdoc/roxie.html b/devdoc/roxie.html new file mode 100644 index 00000000000..5fb4a476a64 --- /dev/null +++ b/devdoc/roxie.html @@ -0,0 +1,31 @@ + + + + + + Everything you ever wanted to know about Roxie | HPCC Platform + + + + + + + + + + + + + +
Skip to content

Everything you ever wanted to know about Roxie

Why did I create it?

Because I could. Many of the pieces needed for Roxie were already created for use in other systems – ECL language, code generator, index creation in Thor, etc. Indexes could be used by Moxie, but that relied on monolithic single-part indexes, was single-threaded (forked a process per query) and had limited ability to do any queries beyond simple index lookups. ECL had already proved itself as a way to express more complex queries concisely, and the concept of doing the processing next to the data had been proved in hOle and Thor, so Roxie – using the same concept for online queries using indexes – was a natural extension of that, reusing the existing index creation and code generation, but adding a new run-time engine geared towards pre-deployed queries and sending index lookup requests to the node holding the index data.

The code generator creates a graph (DAG) representing the query, with one node per activity and links representing the inputs and dependencies. There is also a helper class for each activity.

Roxie loads this graph for all published queries, creating a factory for each activity and recording how they are linked. When a query is executed, the factories create the activity instances and link them together. All activities without output activities (known as ‘sinks’) are then executed (often on parallel threads), and will typically result in a value being written to a workunit, to the socket that the query was received in, or to a global “context” area where subsequent parts of the query might read it.

Data is pulled through the activity graph, by any activity that wants a row from its input requesting it. Evaluation is therefore lazy, with data only calculated as needed. However, to reduce latency in some cases activities will prepare results ahead of when they are requested – for example an index read activity will send the request to the agent(s) as soon as it is started rather than waiting for the data to be requested by its downstream activity. This may result in wasted work, and in some cases may result in data coming back from an agent after the requesting query has completed after discovering it didn’t need it after all – this results in the dreaded “NO msg collator found – using default” tracing (not an error but may be indicative of a query that could use some tuning).

Before requesting rows from an input, it should be started, and when no more rows are required it should be stopped. It should be reset before destruction or reuse (for example for the next row in a child query).

Balancing the desire to reduce latency with the desire to avoid wasted work can be tricky. Conditional activities (IF etc) will not start their unused inputs, so that queries can be written that do different index reads depending on the input. There is also the concept of a “delayed start” activity – I would need to look at the code to remind myself of how those are used.

Where are the Dragons?

Splitter activities are a bit painful – they may result in arbitrary buffering of the data consumed by one output until another output is ready to request a row. It’s particularly complex when some of the outputs don’t start at all – the splitter needs to keep track of how many of the inputs have been started and stopped (an input that is not going to be used must be stopped, so that splitters know not to keep data for them). Tracking these start/stop/reset calls accurately is very important otherwise you can end up with weird bugs including potential crashes when activities are destroyed. Therefore we report errors if the counts don’t tally properly at the end of a query – but working out where a call was missed is often not trivial. Usually it’s because of an exception thrown from an unexpected place, e.g. midway through starting.

Note that row requests from the activities above a splitter may execute on whichever thread downstream from the splitter happens to need that particular row first.

The splitter code for tracking whether any of the downstream activities still need a row is a bit hairy/inefficient, IIRC. There may be scope to optimize (but I would recommend adding some good unit test cases first!)

How does “I beat you to it” work?

When there are multiple agents fulfilling data on a channel, work is shared among them via a hash of the packet header, which is used to determine which agent should work on that packet. However, if it doesn’t start working on it within a short period (either because the node is down, or because it is too busy on other in-flight requests), then another node may take over. The IBYTI messages are used to indicate that a node has started to work on a packet and therefore there is no need for a secondary to take over.

The priority of agents as determined by the packet hash is also used to determine how to proceed if an IBYTI is received after starting to work on a request. If the IBYTI is from a lower priority buddy (sub-channel) then it is ignored, if it’s from a higher priority one then the processing will be abandoned.

When multicast is enabled, the IBYTI is sent on the same multicast channel as the original packet (and care is needed to ignore ones sent by yourself). Otherwise it is sent to all buddy IPs.

Nodes keep track of how often they have had to step in for a supposedly higher priority node, and reduce their wait time before stepping in each time this happens, so if a node has crashed then the buddy nodes will end up taking over without every packet being delayed.

(QUESTION – does this result in the load on the first node after the failed node getting double the load?)

Newer code for cloud systems (where the topology may change dynamically) send the information about the buddy nodes in the packet header rather than assuming all nodes already have a consistent version of that information. This ensures that all agents are using the same assumptions about buddy nodes and their ordering.

All about index compression

An index is basically a big sorted table of the keyed fields, divided into pages, with an index of the last row from each page used to be able to locate pages quickly. The bottom level pages (‘leaves’) may also contain payload fields that do not form part of the lookup but can be returned with it.

Typical usage within LN Risk tends to lean towards one of two cases:

  • Many keyed fields with a single “ID” field in the payload
  • A single “ID” field in the key with many “PII” fields in the payload.

There may be some other cases of note too though – e.g. an error code lookup file which heavily, used, or Boolean search logic keys using smart-stepping to implement boolean search conditions.

It is necessary to store the index pages on disk compressed – they are very compressible – but decompression can be expensive. For this reason traditionally we have maintained a cache of decompressed pages in addition to the cache of compressed pages that can be found in the Linux page cache. However, it would be much preferred if we could avoid decompressing as much as possible, ideally to the point where no significant cache of the decompressed pages was needed.

Presently we need to decompress to search, so we’ve been looking at options to compress the pages in such a way that searching can be done using the compressed form. The current design being played with here uses a form of DFA to perform searching/matching on the keyed fields – the DFA data is a compact representation of the data in the keyed fields but is also efficient to use as for searching. For the payload part, we are looking at several options (potentially using more than one of them depending on the exact data) including:

  • Do not compress (may be appropriate for ID case, for example)
  • Compress individual rows, using a shared dictionary (perhaps trained on first n rows of the index)
  • Compress blocks of rows (in particular, rows that have the same key value)

A fast (to decompress) compression algorithm that handles small blocks of data efficiently is needed. Zstd may be one possible candidate.

Preliminary work to enable the above changes involved some code restructuring to make it possible to plug in different compression formats more easily, and to vary the compression format per page.

What is the topology server for?

It’s used in the cloud to ensure that all nodes can know the IP addresses of all agents currently processing requests for a given channel. These addresses can change over time due to pod restarts or scaling events. Nodes report to the topology server periodically, and it responds to them with the current topology state. There may be multiple topology servers running (for redundancy purposes). If so all reports should go to all, and it should not matter which one’s answer is used. (QUESTION – how is the send to all done?)

Lazy File IO

All IFileIO objects used to read files from Roxie are instantiated as IRoxieLazyFileIO objects, which means:

  • The underlying file handles can be closed in the background, in order to handle the case where file handles are a limited resource. The maximum (and minimum) number of open files can be configured separately for local versus remote files (sometimes remote connections are a scarcer resource than local, if there are limits at the remote end).

  • The actual file connected to can be switched out in the background, to handle the case where a file read from a remote location becomes unavailable, and to switch to reading from a local location after a background file copy operation completes.

New IBYTI mode

Original IBYTI implementation allocated a thread (from the pool) to each incoming query packet, but some will block for a period to allow an IBYTI to arrive to avoid unnecessary work. It was done this way for historical reasons - mainly that the addition of the delay was after the initial IBYTI implementation, so that in the very earliest versions there was no priority given to any particular subchannel and all would start processing at the same time if they had capacity to do so.

This implementation does not seem particularly smart - in particular it's typing up worker threads even though they are not actually working, and may result in the throughput of the Roxie agent being reduced. For that reason an alternative implementation (controlled by the NEW_IBYTI flag) was created during the cloud transition which tracks what incoming packets are waiting for IBYTI expiry via a separate queue, and they are only allocated to a worker thread once the IBYTI delay times out.

So far the NEW_IBYTI flag has only been set on containerized systems (simply to avoid rocking the boat on the bare-metal systems), but we may turn on in bare metal too going forward (and if so, the old version of the code can be removed sooner or later).

Testing Roxie code

Sometimes when developing/debugging Roxie features, it's simplest to run a standalone executable. Using server mode may be useful if wanting to debug server/agent traffic messaging.

For example, to test IBYTI behaviour on a single node, use

./a.out --server --port=9999 --traceLevel=1 --logFullQueries=1 --expert.addDummyNode --roxieMulticastEnabled=0 --traceRoxiePackets=1
+

Having first compiled a suitable bit of ECL into a.out. I have found a snippet like this quite handy:

rtl := SERVICE
+ unsigned4 sleep(unsigned4 _delay) : eclrtl,action,library='eclrtl',entrypoint='rtlSleep';
+END;
+
+d := dataset([{rtl.sleep(5000)}], {unsigned a});
+allnodes(d)+d;
+

Cache prewarm

Roxie (optionally) maintains a list of the most recently accessed file pages (in a circular buffer), and flushes this information periodically to text files that will persist from one run of Roxie to the next. On startup, these files are processed and the relevant pages preloaded into the linux page cache to ensure that the "hot" pages are already available and maximum performance is available immediately once the Roxie is brought online, rather than requiring a replay of a "typical" query set to heat the cache as used to be done. In particular this should allow a node to be "warm" before being added to the cluster when autoscaling.

There are some questions outstanding about how this operates that may require empirical testing to answer. Firstly, how does this interact with volumes mounted via k8s pvc's, and in particular with cloud billing systems that charge per read. Will the reads that are done to warm the cache be done in large chunks, or will they happen one linux page at a time? The code at the Roxie level operates by memory-mapping the file then touching a byte within each linux page that we want to be "warm", but does the linux paging subsystem fetch larger blocks? Do huge pages play a part here?

Secondly, the prewarm is actually done by a child process (ccdcache), but the parent process is blocked while it happens. It would probably make sense to at allow at least some of the other startup operations of the parent process to proceed in parallel. There are two reasons why the cache prewarm is done using a child process. Firstly is to allow there to be a standalone way to prewarm prior to launching a Roxie, which might be useful for automation in some bare-metal systems. Secondly, because there is a possibility of segfaults resulting from the prewarm if the file has changed size since the cache warming was done, it is easier to contain, capture, and recover from such faults in a child process than it would be inside Roxie. However, it would probably be possible to avoid these segfaults (by checking more carefully against file size before trying to warm a page, for example) and then link the code into Roxie while still keeping the code common with the standalone executable version.

Thirdly, need to check that the prewarm is complete before adding a new agent to the topology. This is especially relevant if we make any change to do the prewarm asynchronously.

Fourthly, there are potential race conditions when reading/writing the file containing cache information, since this file may be written by any agent operating on the same channel, at any time.

Fifthly, how is the amount of information tracked decided? It should be at least related to the amount of memory available to the linux page cache, but that's not a completely trivial thing to calculate. Should we restrict to the most recent N when outputting, where N is calculated from, for example /proc/meminfo's Active(file) value? Unfortunately on containerized sytems that reflects the host, but perhaps /sys/fs/cgroup/memory.stat can be used instead?

When deciding how much to track, we can pick an upper limit from the pod's memory limit. This could be read from /sys/fs/cgroup/memory.max though we currently read from the config file instead. We should probably (a) subtract the roxiemem size from that and (b) think about a value that will work on bare-metal and fusion too. However, because we don't dedup the entries in the circular buffer used for tracking hot pages until the info is flushed, the appropriate size is not really the same as the memory size.

We track all reads by page, and before writing also add all pages in the jhtree cache with info about the node type. Note that a hit in the jhtree page cache won't be noted as a read OTHER than via this last-minute add.

Blacklisting sockets

This isn't really specific to Roxie, but was originally added for federated Roxie systems...

When a Roxie query (or hthor/thor) makes a SOAPCALL, there is an option to specify a list of target gateway IPs, and failover to the next in the list if the first does not respond in a timely fashion. In order to avoid this "timely fashion" check adding an overhead to every query made when a listed gateway is unavailable, we maintain a "blacklist" of nodes that have been seen to fail, and do not attempt to connect to them. There is a "deblacklister" thread that checks periodically whether it is now possible to connect to a previously-blacklisted gateway, and removes it from the list if so.

There are a number of potential questions and issues with this code:

  1. It would appear that a blacklist is applied even when there is only one gateway listed. In this case, the blacklist may be doing more harm than good? I'm not sure that is true - it is still causing rapid failures in cases that are never going to work...
  2. Even when there is only one gateway listed, a blacklist MIGHT still be useful (you don't really want EVERY query to block trying to connect, if the gateway is down - may prefer a fast failure). Also applies when there are multiple records, all being passed to a gateway, and with an ONFAIL.
  3. Is the blacklist shared between all queries? I'm pretty sure it is NOT shared across Roxie nodes... Looks like it is a global object, shared between all queries and activities. However, connections that were started in parallel will all be reported as failed rather than blacklisted, which can make it look like it is maintained per-activity.
  4. Is it only a failed connect that leads to blacklisting, or does a slow/error response also cause a gateway endpoint to be blacklisted? It's only a failed connect.
  5. When deblacklisting, can we check any condition other than "Successfully connected"? If not, blacklisting for any reason other than "Did not connect" feels like a recipe for problems. We only check for a connection (and correspondingly only blacklist for a failed connection).
  6. Are we ever using the functionality where there are more than one gateway listed? Most of the time a load-balancer is a preferable solution...
  7. The "deblacklister" thread seems to add an escalating delay between attempts. Is this delay ever reset? Is it configurable? Is it appropriate?
  8. There's a thread (in the blacklister's pool) for each blacklisted endpoint. These threads will never go away if the endpoint does not recover...
  9. There's a delay of up to 10 seconds in terminating caused by the deblacklister's connect having to timeout before it notices that we are stopping. Can we close the socket as well as interrupting the semaphore?
  10. Does the "reconnect" attempt from the deblacklister cause any pain for the server it is connecting to? Lots of connect attempts without any data could look like a DoS attack...
  11. Retries/timeout seems to translate to Owned<ISocketConnectWait> scw = nonBlockingConnect(ep, timeoutMS == WAIT_FOREVER ? 60000 : timeoutMS*(retries+1)); I am not sure that is correct (a single attempt to connect with a long timeout doesn't feel like it is the same as multiple attempts with shorter timeouts, for example if there is a load balancer in the mix).
  12. Perhaps an option to not use blacklister would solve the immediate issue?
  13. The blacklister uses an array of endpoints - If there were a lot blacklisted, a hash table would be better
  14. Hints to control behaviour of deblacklister would behave unpredictably if multiple activities connected to the same endpoint with different hints unless we make the blacklist lookup match the hint values too.
  15. Deblacklister should use nonBlockingConnect too.

Should the scope of the blacklist be different? Possible scopes are:

  1. Shared across all queries/activities (current behaviour)
  2. Specific to an activity, but shared across queries (i.e. owned by the activity factory)
  3. Specific to all activities in a deployed query (i.e. owned by the query factory)
  4. Specific to a particular activity instance (i.e. owned by the activity object)
  5. Specific to a particular query instance (i.e. owned by the query object)

Options 2 and 4 above would allow all aspects of the blacklisting behaviour to be specified by options on the SOAPCALL. We could control whether or not the blacklister is to be used at all via a SOAPCALL option with any of the above...

perftrace options

The HPCC Platform includes a rudimentary performance tracing feature using periodic stack capture to generate flame graphs. Roxie supports this in 3 ways:

  1. If expert/@profileStartup is set in roxie config, a flame graph is generated for operations during Roxie startup phase.
  2. If @perf is set on an incoming query, a flame graph is generated for the lifetime of that query's execution, and returned along with the query results
  3. If expert/perftrace is set in roxie config, one-shot roxie queries (e.g. eclagent mode) generate a flame graph (currently just to a text file).

The perf trace operates as follows:

  1. A child process is launched that runs the doperf script. This samples the current stack(s) every 0.2s (configurable) to a series of text files.
  2. When tracing is done, these text files are "folded" via a perl script that notes every unique stack and how many times it was seen, one line per unique stack
  3. This folded stack list is filtered to suppress some stacks that are not very interesting
  4. The filtered folded stack list is passed to another perl script that generates an svg file.

The basic info captured at step 1 (or maybe 2) could also be analysed to give other insights, such as:

  1. A list of "time in function, time in children of function".
  2. An expanded list of callers to __GI___lll_lock_wait and __GI___lll_lock_wake, to help spot contended critsecs.

Unfortunately some info present in the original stack text files is lost in the folded summary - in particular related to the TID that the stack is on. Can we spot lifetimes of threads and/or should we treat "stacks" on different threads as different? Thread pools might render this difficult though. There is an option in stack-collapse-elfutils.pl to include the TID when considering whether stacks match, so perhaps we should just (optionally) use that.

Some notes on LocalAgent mode

In localAgent mode, the global queueManager object (normally a RoxieUdpSocketQueueManager) is replaced by a RoxieLocalQueueManager. Outbound packets are added directly to target queue, inbound are packed into DataBuffers.

There is also "local optimizations" mode where any index operation reading a one-part file (does the same apply to one-part disk files?) just reads it directly on the server (regardless of localAgent setting). Typically still injected into receiver code though as otherwise handling exception cases, limits etc would all be duplicated/messy. Rows created in localOptimization mode are created directly in the caller's row manager, and are injected in serialized format.

Why are inbound not created directly in the desired destination's allocator and then marked as serialized? Some lifespan issues... are they insurmountable? We do pack into dataBuffers rather than MemoryBuffers, which avoids a need to copy the data before the receiver can use it. Large rows get split and will require copying again, but we could set dataBufferSize to be bigger in localAgent mode to mitigate this somewhat.

What is the lifespan issue? In-flight queries may be abandoned when a server-side query fails, times out, or no longer needs the data. Using DataBuffer does not have this issue as they are attached to the query's memory manager/allocation once read. Or we could bypass the agent queue altogether, but rather more refactoring needed for that (might almost be easier to extent the "local optimization" mode to use multiple threads at that point)

abortPending, replyPending, and abortPendingData methods are unimplemented, which may lead to some inefficiencies?

Some notes on UDP packet sending mechanism

Requests from server to agents are send via UDP (and have a size limit of 64k as a result). Historically they were sent using multicast to go to all agents on a channel at the same time, but since most cloud providers do not support multicast, there has long been an option to avoid multicast and send explicitly to the agent IPs. In bare metal systems these IPs are known via the topology file, and do not change. In cloud systems the topology server provides the IPs of all agents for a channel.

In cloud systems, the list of IPs that a message was sent to is included in the message header, so that the IBYTI messages can be sent without requiring that all agents/servers have the same topology information at any given moment (they will stay in sync because of topology server, but may be temporarily out of sync when nodes are added/removed, until next time topology info is retrieved). This is controled by the SUBCHANNELS_IN_HEADER define.

Packets back from agents to server go via the udplib message-passing code. This can best be described by looking at the sending and receiving sides separately.

When sending, results are split into individual packets (DataBuffers), each designed to be under 1 MTU in size. Traditionally this meant they were 1k, but they can be set larger (8k is good). They do have to be a power of 2 because of how they are allocated from the roxiemem heap. The sender maintains a set of UdpReceiverEntry objects, one for each server that it is conversing with. Each UdpReceiverEntry maintains multiple queues of data packets waiting to be sent, one queue for each priority. The UdpReceiverEntry maintains a count of how many packets are contained across all its queues in packetsQueued, so that it knows if there is data to send.

The priority levels are: 0: Out Of Band 1: Fast lane 2: Standard

This is designed to allow control information to be sent without getting blocked by data, and high priority queries to avoid being blocked by data going to lower priority ones. The mechanism for deciding what packet to send next is a little odd though - rather than sending all higher-priorty packets before any lower-priority ones, it round robins across the queues sending up to N^2 from queue 0 then up to N from queue 1 then 1 from queue 2, where N is set by the UdpOutQsPriority option, or 1 if not set. This may be a mistake - probably any from queue 0 should be sent first, before round-robining the other queues in this fashion.

UdpReceiverEntry objects are also responsible for maintaining a list of packets that have been sent but receiver has not yet indicated that they have arrived.

If an agent has data ready for a given receiver, it will send a requestToSend to that receiver, and wait for a permitToSend response. Sequence numbers are used to handle situations where these messages get lost. A permitToSend that does not contain the expected sequence number is ignored.

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/devdoc/userdoc/AzureTipsTricks.html b/devdoc/userdoc/AzureTipsTricks.html new file mode 100644 index 00000000000..3264cbe3530 --- /dev/null +++ b/devdoc/userdoc/AzureTipsTricks.html @@ -0,0 +1,24 @@ + + + + + + HPCC Platform + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/devdoc/userdoc/Blogs.html b/devdoc/userdoc/Blogs.html new file mode 100644 index 00000000000..bae2ca91058 --- /dev/null +++ b/devdoc/userdoc/Blogs.html @@ -0,0 +1,24 @@ + + + + + + HPCC Platform + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/devdoc/userdoc/README.html b/devdoc/userdoc/README.html new file mode 100644 index 00000000000..ca79b4d0b93 --- /dev/null +++ b/devdoc/userdoc/README.html @@ -0,0 +1,24 @@ + + + + + + User Documentation | HPCC Platform + + + + + + + + + + + + + +
Skip to content

User Documentation

Documentation in this directory is targeted to end-users of the HPCC Systems Platform. This is less-formal documentation intended to be produced and released more quickly than the published HPCC documentation. See HPCC documentation if you would like to contribute to our official docs.

Directory structure under devdoc

INFO

  • userdoc/troubleshoot: Information related to troubleshooting particular components
  • userdoc/azure: Useful information about Azure Cloud portal and cli
  • userdoc/roxie: Useful information for running roxie
  • userdoc/thor: COMING SOON: Useful information for running thor
  • userdoc/blogs: COMING SOON: Location and instructions for all Blogs

General documentation

HPCC Website documentation

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/devdoc/userdoc/WikiGuidelines.html b/devdoc/userdoc/WikiGuidelines.html new file mode 100644 index 00000000000..9fe5bae9e51 --- /dev/null +++ b/devdoc/userdoc/WikiGuidelines.html @@ -0,0 +1,24 @@ + + + + + + HPCC Platform + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/devdoc/userdoc/azure/TipsAndTricks.html b/devdoc/userdoc/azure/TipsAndTricks.html new file mode 100644 index 00000000000..27a64827bf4 --- /dev/null +++ b/devdoc/userdoc/azure/TipsAndTricks.html @@ -0,0 +1,77 @@ + + + + + + Azure Portal FAQs | HPCC Platform + + + + + + + + + + + + + +
Skip to content

Azure Portal FAQs

  1. How do I see who created a resource group?

Answer:

1. go to the resource group service page (https://portal.azure.com/#view/HubsExtension/BrowseResourceGroups)
+2. "Add filter"
+3. Filter on "Admin"
+4. Set the Value to "ALL" or select the names you are interested in (the names in the list are the only ones that have the Admin tag set)  This works for all other fields that you can filter on
  1. How do I create a dashboard in Azure?

Answer:

Login to the Azure portal.
+
+Click on the `hamburger icon`, located at the top left corner of the page.
+
+Click on `Dashboard` to go to your dashboards.
+
+Click on `Create`, located at the top left corner.
+
+Click on the `Custom` tile.
+
+Edit the input box to name your dashboard.
+
+Click on `Resource groups` in the tile gallery.
+
+Click `Add`.
+
+Click and drag the `lower right corner` of the tile to resize it to your likings.
+
+Click `Save` to save your settings.
+
+You should now be taken to your new dashboard.
+
+Click on your new dashboard tile.
+
+Click on `Add filter`, located at the top center of the page.
+
+Click on the `Filter` input box to reveal the tags.
+
+Select the `Admin` tag.
+
+Click on the `Value` input box.
+
+Click on `Select all` to unselect all.
+
+Select your name.
+
+Click on `Apply`.
+
+Next, click on `Manage view`, located at the top left of the page.
+
+Select `Save view`.
+
+Enter a name for the view in the input box.
+
+Click `Save`
+
+ 
+
+ 
+
+Click on `Manage View`, located at the top left of the page

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/devdoc/userdoc/roxie/FAQ.html b/devdoc/userdoc/roxie/FAQ.html new file mode 100644 index 00000000000..36c750cb9b8 --- /dev/null +++ b/devdoc/userdoc/roxie/FAQ.html @@ -0,0 +1,47 @@ + + + + + + ROXIE FAQs | HPCC Platform + + + + + + + + + + + + + +
Skip to content

ROXIE FAQs

  1. How I can compile a query on a containerized or cloud-based system?

Answer:

Same way as bare metal. Command line, or with the IDE, or from ECL Watch. Just point to the HPCC Systems instance to compile.
+For Example:
+ecl deploy <target> <file>
  1. How do I copy queries from an on-prem cluster to Azure?

Answer:

The copy query command – use the Azure host name or IP address for the target.
+For example:
+ecl queries copy <source_query_path> <target_queryset>
  1. How can I get the IP address for the Azure target cluster?

Answer:

Use the "kubectl get svc" command. Use the external IP address listed for ECL Watch.
+kubectl get svc
  1. Do we have to have use the DNSName or do we need to use the IP address?

Answer:

If you can reach ECL Watch with the DNS Name then it should also work for the command line.
  1. How can I find the ECL Watch or Dali hostname?

Answer:

If you did not set up the containerized instance, then you need to ask your Systems Administrator or whomever set it up..
  1. How do I publish a package file?

Answer:

Same way as bare metal.
+To add a new package file: ecl packagemap add or
+To copy exisitng package file : ecl packagemap copy
  1. How do I check the logs?

Answer:

kubectl log <podname>
+in addition you can use -f (follow) option to tail the logs. Optionally you can also issue the <namespace> parameter.
+For example:
+kbectl log roxie-agent-1-3b12a587b –namespace MyNameSpace
+Optionally, you may have implemented a log-processing solution such as the Elastic Stack (elastic4hpcclogs).
  1. How do I get the data on to Azure?

Answer:

Use the copy query command and copy or add the Packagemap.
+With data copy start in the logs…copy from remote location specified if data doesn’t exist on the local system.
+The remote location is the remote Dali (use the --daliip=<daliIP> parameter to specify the remote Dali)
+You can also use ECL Watch.
  1. How can I start a cloud cluster? (akin to the old Virtual Box image)?

Answer:

Can use Docker Desktop, or Azure or any cloud provider and install the HPCC Systems Cloud native helm
+charts
  1. How can I show the ECL queries that are published to a given Roxie?

Answer:

Can use WUListQueries
+For example:
+https://[eclwatch]:18010/WsWorkunits/WUListQueries.json?ver_=1.86&ClusterName=roxie&CheckAllNodes=0
  1. I set up persistent storage on my containerized HPCC Systems, and now it won't start. Why?

Answer:

One possible reason may be that all of the required storage directories are not present. The directories for ~/
+hpccdata/dalistorage, hpcc-data, debug, queries, sasha, and dropzone are all required to exist or your cluster may not start.
  1. Are there any new methods available to work with queries?

Answer:

Yes. There is a new method available ServiceQuery.
+https://[eclwatch]:18010/WsResources/ServiceQuery?ver_=1.01&
+For example Roxie Queries:
+https://[eclwatch]:18010/WsResources/ServiceQuery?ver_=1.01&Type=roxie
+or WsECL (eclqueries)
+https://[eclwatch]:18010/WsResources/ServiceQuery?ver_=1.01&Type=eclqueries

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/devdoc/userdoc/troubleshoot/ClientsToolIssues.html b/devdoc/userdoc/troubleshoot/ClientsToolIssues.html new file mode 100644 index 00000000000..4fd2f296871 --- /dev/null +++ b/devdoc/userdoc/troubleshoot/ClientsToolIssues.html @@ -0,0 +1,24 @@ + + + + + + How to resolve issues installing the ECL IDE / Client tools | HPCC Platform + + + + + + + + + + + + + +
Skip to content

How to resolve issues installing the ECL IDE / Client tools

Problem

Some users may experience an issue when trying to install the ECL IDE / client tools version 8.10 and later.
If you get an error saying something like, Windows Defender SmartScreen prevented access and there is no option other than "Don't Run":

  • Go to the folder where you downloaded the file.
  • Once there go to the app file properties click unblock

alt-text

alt-text

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/ecl/ecl-bundle/DOCUMENTATION.html b/ecl/ecl-bundle/DOCUMENTATION.html new file mode 100644 index 00000000000..2c9b94d990d --- /dev/null +++ b/ecl/ecl-bundle/DOCUMENTATION.html @@ -0,0 +1,24 @@ + + + + + + Ecl-bundle source documentation | HPCC Platform + + + + + + + + + + + + + +
Skip to content

Ecl-bundle source documentation

Introduction

Purpose

The ecl-bundle executable (normally executed from the ecl executable by specifying 'ecl bundle XXX' is designed to manipulate ecl bundle files

  • these are self-contained parcels of ECL code, packaged into a compressed file like a zip or .tar.gz file, that can be downloaded, installed, removed etc from an ECL installation.

Design

The metadata for ECL bundles is described using an exported module called Bundle within the bundle's source tree - typically, this means that a file called Bundle.ecl will be added to the highest level of the bundle's directory tree. In order to extract the information from the Bundle module, eclcc is run in 'evaluate' mode (using the -Me option) which will parse the bundle module and output the required fields to stdout.

ecl-bundle also executes eclcc (using the --showpaths option) to determine where bundle files are to be located.

Directory structure

In order to make versioning easier, bundle files are not copied directly into the bundles directory. A bundle called "MyBundle" that announces itself as version "x.y.z" will be installed to the directory

$BUNDLEDIR/_versions/MyBundle/x.y.z

A "redirect" file called MyBundle.ecl is then created in $BUNDLEDIR, which redirects any IMPORT MyBundle statement to actually import the currently active version of the bundle in _versions/MyBundle/x.y.z

By rewriting this redirect file, it is possible to switch to using a different version of a bundle without having to uninstall and reinstall.

In a future release, we hope to make it possible to specify that bundle A requires version X of bundle B, while bundle C requires version Y of bundle B. That will require the redirect files to be 'local' to a bundle (and will require that bundle B uses a redirect file to ensure it picks up the local copy of B when making internal calls).

Key classes

An IBundleInfo represents a specific copy of a bundle, and is created by explicitly parsing a snippet of ECL that imports it, with the ECL include path set to include only the specified bundle.

An IBundleInfoSet represents all the installed versions of a particular named bundle.

An IBundleCollection represents all the bundles on the system.

Every individual subcommand is represented by a class derived (directly or indirectly) from EclCmdCommon. These classes are responsible for command-line parsing, usage text output, and (most importantly) execution of the desired outcomes.

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/ecllibrary/StyleGuide.html b/ecllibrary/StyleGuide.html new file mode 100644 index 00000000000..e7e972b2f81 --- /dev/null +++ b/ecllibrary/StyleGuide.html @@ -0,0 +1,43 @@ + + + + + + ECL standard library style guide | HPCC Platform + + + + + + + + + + + + + +
Skip to content

ECL standard library style guide

The ECL code in the standard library should follow the following style guidelines:

  • All ECL keywords in upper case
  • ECL reserved types in upper case
  • Public attributes in camel case with leading upper case
  • Private attributes in lower case with underscore as a separator
  • Field names in lower case with underscore as a separator
  • Standard indent is 2 spaces (no tabs)
  • Maximum line length of 120 characters
  • Compound statements have contents indented, and END is aligned with the opening statement
  • Field names are not indented to make them line up within a record structure
  • Parameters are indented as necessary
  • Use javadoc style comments on all functions/attributes (see Writing Javadoc Comments)

For example:

ecl
my_record := RECORD
+    INTEGER4 id;
+    STRING firstname{MAXLENGTH(40)};
+    STRING lastname{MAXLENGTH(50)};
+END;
+
+/**
+  * Returns a dataset of people to treat with caution matching a particular lastname.  The
+  * names are maintained in a global database of undesirables.
+  *
+  * @param  search_lastname    A lastname used as a filter
+  * @return                    The list of people
+  * @see                       NoFlyList
+  * @see                       MorePeopleToAvoid
+  */
+
+EXPORT DodgyCharacters(STRING search_lastname) := FUNCTION
+    raw_ds := DATASET(my_record, 'undesirables', THOR);
+    RETURN raw_ds(last_name = search_lastname);
+END;

Some additional rules for attributes in the library:

  • Services should be SHARED and EXPORTed via intermediate attributes
  • All attributes must have at least one matching test. If you're not on the test list you're not coming in.

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/hashmap.json b/hashmap.json new file mode 100644 index 00000000000..29207ecd673 --- /dev/null +++ b/hashmap.json @@ -0,0 +1 @@ +{"devdoc_codesubmissions.md":"wf0ooHZx","devdoc_ldapsecuritymanager.md":"6QV0TOn6","devdoc_codereviews.md":"yXOcGqwe","devdoc_devdocs.md":"p38z6BoL","index.md":"gO4OaP_B","devdoc_development.md":"qUtv1gjz","devdoc_codegenerator.md":"Sr_00DEY","devdoc_userdoc_azure_tipsandtricks.md":"LEeeHH_U","cmake_modules_documentation.md":"PBiJbB4I","system_masking_plugins_datamasker_readme.md":"iLx0XZB6","devdoc_metrics.md":"ipIaUVOL","devdoc_userbuildassets.md":"5pBqOi7J","testing_regress_ecl_analyzers_corporate_tmp_readme.md":"QgDQ6IeH","devdoc_securityconfig.md":"z0iGl4iN","system_httplib_readme.md":"ayRNZub-","devdoc_styleguide.md":"Mjr9vf6y","devdoc_userdoc_readme.md":"D4Kid-3h","system_security_plugins_jwtsecurity_readme.md":"1wwh5vak","system_masking_include_readme.md":"i5B-dOzb","testing_regress_cleanupreadme.md":"WHcEY6YL","devdoc_userdoc_troubleshoot_clientstoolissues.md":"Oxqyv8dQ","devdoc_versionsupport.md":"deGoLLFC","devdoc_userdoc_roxie_faq.md":"nQti9zzo","build_me.md":"VZvnVhVL","devdoc_userdoc_azuretipstricks.md":"aFaptA0d","ecllibrary_styleguide.md":"gc1ZYvqu","tools_tagging_readme.md":"aY1N0cJO","devdoc_newactivity.md":"SP0TgdQz","devdoc_newfileprocessing.md":"sdNSfUu7","readme.md":"7Qva37_C","devdoc_gitauthenticate.md":"BC7Bmr5A","devdoc_userdoc_blogs.md":"-JKS2x0f","devdoc_readme.md":"bTEFReKq","devdoc_securityuserauthentication.md":"q0qlsyXA","devdoc_roxie.md":"ucbDhtEB","ecl_ecl-bundle_documentation.md":"Z4TCf1dS","devdoc_memorymanager.md":"WzZxL7Sr","devdoc_userdoc_wikiguidelines.md":"QHJ-8Jox","tools_esdlcmd_readme.md":"kugmSdg_","devdoc_workunits.md":"jFcYNljW"} diff --git a/index.html b/index.html new file mode 100644 index 00000000000..db59bfcbfda --- /dev/null +++ b/index.html @@ -0,0 +1,24 @@ + + + + + + HPCC Platform + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/system/httplib/README.html b/system/httplib/README.html new file mode 100644 index 00000000000..044e8106a56 --- /dev/null +++ b/system/httplib/README.html @@ -0,0 +1,310 @@ + + + + + + cpp-httplib | HPCC Platform + + + + + + + + + + + + + +
Skip to content

cpp-httplib

A C++11 single-file header-only cross platform HTTP/HTTPS library.

It's extremely easy to setup. Just include httplib.h file in your code!

Server Example

c++
#include <httplib.h>
+
+int main(void)
+{
+  using namespace httplib;
+
+  Server svr;
+
+  svr.Get("/hi", [](const Request& req, Response& res) {
+    res.set_content("Hello World!", "text/plain");
+  });
+
+  svr.Get(R"(/numbers/(\d+))", [&](const Request& req, Response& res) {
+    auto numbers = req.matches[1];
+    res.set_content(numbers, "text/plain");
+  });
+
+  svr.Get("/body-header-param", [](const Request& req, Response& res) {
+    if (req.has_header("Content-Length")) {
+      auto val = req.get_header_value("Content-Length");
+    }
+    if (req.has_param("key")) {
+      auto val = req.get_param_value("key");
+    }
+    res.set_content(req.body, "text/plain");
+  });
+
+  svr.Get("/stop", [&](const Request& req, Response& res) {
+    svr.stop();
+  });
+
+  svr.listen("localhost", 1234);
+}

Post, Put, Delete and Options methods are also supported.

Bind a socket to multiple interfaces and any available port

cpp
int port = svr.bind_to_any_port("0.0.0.0");
+svr.listen_after_bind();

Static File Server

cpp
// Mount / to ./www directory
+auto ret = svr.set_mount_point("/", "./www");
+if (!ret) {
+  // The specified base directory doesn't exist...
+}
+
+// Mount /public to ./www directory
+ret = svr.set_mount_point("/public", "./www");
+
+// Mount /public to ./www1 and ./www2 directories
+ret = svr.set_mount_point("/public", "./www1"); // 1st order to search
+ret = svr.set_mount_point("/public", "./www2"); // 2nd order to search
+
+// Remove mount /
+ret = svr.remove_mount_point("/");
+
+// Remove mount /public
+ret = svr.remove_mount_point("/public");
cpp
// User defined file extension and MIME type mappings
+svr.set_file_extension_and_mimetype_mapping("cc", "text/x-c");
+svr.set_file_extension_and_mimetype_mapping("cpp", "text/x-c");
+svr.set_file_extension_and_mimetype_mapping("hh", "text/x-h");

The followings are built-in mappings:

ExtensionMIME Type
txttext/plain
html, htmtext/html
csstext/css
jpeg, jpgimage/jpg
pngimage/png
gifimage/gif
svgimage/svg+xml
icoimage/x-icon
jsonapplication/json
pdfapplication/pdf
jsapplication/javascript
wasmapplication/wasm
xmlapplication/xml
xhtmlapplication/xhtml+xml

NOTE: These the static file server methods are not thread safe.

Logging

cpp
svr.set_logger([](const auto& req, const auto& res) {
+  your_logger(req, res);
+});

Error handler

cpp
svr.set_error_handler([](const auto& req, auto& res) {
+  auto fmt = "<p>Error Status: <span style='color:red;'>%d</span></p>";
+  char buf[BUFSIZ];
+  snprintf(buf, sizeof(buf), fmt, res.status);
+  res.set_content(buf, "text/html");
+});

'multipart/form-data' POST data

cpp
svr.Post("/multipart", [&](const auto& req, auto& res) {
+  auto size = req.files.size();
+  auto ret = req.has_file("name1");
+  const auto& file = req.get_file_value("name1");
+  // file.filename;
+  // file.content_type;
+  // file.content;
+});

Receive content with Content receiver

cpp
svr.Post("/content_receiver",
+  [&](const Request &req, Response &res, const ContentReader &content_reader) {
+    if (req.is_multipart_form_data()) {
+      MultipartFormDataItems files;
+      content_reader(
+        [&](const MultipartFormData &file) {
+          files.push_back(file);
+          return true;
+        },
+        [&](const char *data, size_t data_length) {
+          files.back().content.append(data, data_length);
+          return true;
+        });
+    } else {
+      std::string body;
+      content_reader([&](const char *data, size_t data_length) {
+        body.append(data, data_length);
+        return true;
+      });
+      res.set_content(body, "text/plain");
+    }
+  });

Send content with Content provider

cpp
const size_t DATA_CHUNK_SIZE = 4;
+
+svr.Get("/stream", [&](const Request &req, Response &res) {
+  auto data = new std::string("abcdefg");
+
+  res.set_content_provider(
+    data->size(), // Content length
+    "text/plain", // Content type
+    [data](size_t offset, size_t length, DataSink &sink) {
+      const auto &d = *data;
+      sink.write(&d[offset], std::min(length, DATA_CHUNK_SIZE));
+      return true; // return 'false' if you want to cancel the process.
+    },
+    [data] { delete data; });
+});

Without content length:

cpp
svr.Get("/stream", [&](const Request &req, Response &res) {
+  res.set_content_provider(
+    "text/plain", // Content type
+    [&](size_t offset, size_t length, DataSink &sink) {
+      if (/* there is still data */) {
+        std::vector<char> data;
+        // prepare data...
+        sink.write(data.data(), data.size());
+      } else {
+        done(); // No more data
+      }
+      return true; // return 'false' if you want to cancel the process.
+    });
+});

Chunked transfer encoding

cpp
svr.Get("/chunked", [&](const Request& req, Response& res) {
+  res.set_chunked_content_provider(
+    [](size_t offset, DataSink &sink) {
+      sink.write("123", 3);
+      sink.write("345", 3);
+      sink.write("789", 3);
+      sink.done(); // No more data
+      return true; // return 'false' if you want to cancel the process.
+    }
+  );
+});

'Expect: 100-continue' handler

As default, the server sends 100 Continue response for Expect: 100-continue header.

cpp
// Send a '417 Expectation Failed' response.
+svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
+  return 417;
+});
cpp
// Send a final status without reading the message body.
+svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
+  return res.status = 401;
+});

Keep-Alive connection

cpp
svr.set_keep_alive_max_count(2); // Default is 5

Timeout

c++
svr.set_read_timeout(5, 0); // 5 seconds
+svr.set_write_timeout(5, 0); // 5 seconds
+svr.set_idle_interval(0, 100000); // 100 milliseconds

Set maximum payload length for reading request body

c++
svr.set_payload_max_length(1024 * 1024 * 512); // 512MB

Server-Sent Events

Please see Server example and Client example.

Default thread pool support

ThreadPool is used as a default task queue, and the default thread count is set to value from std::thread::hardware_concurrency().

You can change the thread count by setting CPPHTTPLIB_THREAD_POOL_COUNT.

Override the default thread pool with yours

cpp
class YourThreadPoolTaskQueue : public TaskQueue {
+public:
+  YourThreadPoolTaskQueue(size_t n) {
+    pool_.start_with_thread_count(n);
+  }
+
+  virtual void enqueue(std::function<void()> fn) override {
+    pool_.enqueue(fn);
+  }
+
+  virtual void shutdown() override {
+    pool_.shutdown_gracefully();
+  }
+
+private:
+  YourThreadPool pool_;
+};
+
+svr.new_task_queue = [] {
+  return new YourThreadPoolTaskQueue(12);
+};

Client Example

c++
#include <httplib.h>
+#include <iostream>
+
+int main(void)
+{
+  httplib::Client cli("localhost", 1234);
+
+  if (auto res = cli.Get("/hi")) {
+    if (res->status == 200) {
+      std::cout << res->body << std::endl;
+    }
+  } else {
+    auto err = res.error();
+    ...
+  }
+}

NOTE: Constructor with scheme-host-port string is now supported!

c++
httplib::Client cli("localhost");
+httplib::Client cli("localhost:8080");
+httplib::Client cli("http://localhost");
+httplib::Client cli("http://localhost:8080");
+httplib::Client cli("https://localhost");

GET with HTTP headers

c++
httplib::Headers headers = {
+  { "Accept-Encoding", "gzip, deflate" }
+};
+auto res = cli.Get("/hi", headers);

or

c++
cli.set_default_headers({
+  { "Accept-Encoding", "gzip, deflate" }
+});
+auto res = cli.Get("/hi");

POST

c++
res = cli.Post("/post", "text", "text/plain");
+res = cli.Post("/person", "name=john1&note=coder", "application/x-www-form-urlencoded");

POST with parameters

c++
httplib::Params params;
+params.emplace("name", "john");
+params.emplace("note", "coder");
+
+auto res = cli.Post("/post", params);

or

c++
httplib::Params params{
+  { "name", "john" },
+  { "note", "coder" }
+};
+
+auto res = cli.Post("/post", params);

POST with Multipart Form Data

c++
httplib::MultipartFormDataItems items = {
+  { "text1", "text default", "", "" },
+  { "text2", "aωb", "", "" },
+  { "file1", "h\ne\n\nl\nl\no\n", "hello.txt", "text/plain" },
+  { "file2", "{\n  \"world\", true\n}\n", "world.json", "application/json" },
+  { "file3", "", "", "application/octet-stream" },
+};
+
+auto res = cli.Post("/multipart", items);

PUT

c++
res = cli.Put("/resource/foo", "text", "text/plain");

DELETE

c++
res = cli.Delete("/resource/foo");

OPTIONS

c++
res = cli.Options("*");
+res = cli.Options("/resource/foo");

Timeout

c++
cli.set_connection_timeout(0, 300000); // 300 milliseconds
+cli.set_read_timeout(5, 0); // 5 seconds
+cli.set_write_timeout(5, 0); // 5 seconds

Receive content with Content receiver

c++
std::string body;
+
+auto res = cli.Get("/large-data",
+  [&](const char *data, size_t data_length) {
+    body.append(data, data_length);
+    return true;
+  });
cpp
std::string body;
+
+auto res = cli.Get(
+  "/stream", Headers(),
+  [&](const Response &response) {
+    EXPECT_EQ(200, response.status);
+    return true; // return 'false' if you want to cancel the request.
+  },
+  [&](const char *data, size_t data_length) {
+    body.append(data, data_length);
+    return true; // return 'false' if you want to cancel the request.
+  });

Send content with Content provider

cpp
std::string body = ...;
+
+auto res = cli_.Post(
+  "/stream", body.size(),
+  [](size_t offset, size_t length, DataSink &sink) {
+    sink.write(body.data() + offset, length);
+    return true; // return 'false' if you want to cancel the request.
+  },
+  "text/plain");

With Progress Callback

cpp
httplib::Client client(url, port);
+
+// prints: 0 / 000 bytes => 50% complete
+auto res = cli.Get("/", [](uint64_t len, uint64_t total) {
+  printf("%lld / %lld bytes => %d%% complete\n",
+    len, total,
+    (int)(len*100/total));
+  return true; // return 'false' if you want to cancel the request.
+}
+);

progress

Authentication

cpp
// Basic Authentication
+cli.set_basic_auth("user", "pass");
+
+// Digest Authentication
+cli.set_digest_auth("user", "pass");
+
+// Bearer Token Authentication
+cli.set_bearer_token_auth("token");

NOTE: OpenSSL is required for Digest Authentication.

Proxy server support

cpp
cli.set_proxy("host", port);
+
+// Basic Authentication
+cli.set_proxy_basic_auth("user", "pass");
+
+// Digest Authentication
+cli.set_proxy_digest_auth("user", "pass");
+
+// Bearer Token Authentication
+cli.set_proxy_bearer_token_auth("pass");

NOTE: OpenSSL is required for Digest Authentication.

Range

cpp
httplib::Client cli("httpbin.org");
+
+auto res = cli.Get("/range/32", {
+  httplib::make_range_header({{1, 10}}) // 'Range: bytes=1-10'
+});
+// res->status should be 206.
+// res->body should be "bcdefghijk".
cpp
httplib::make_range_header({{1, 10}, {20, -1}})      // 'Range: bytes=1-10, 20-'
+httplib::make_range_header({{100, 199}, {500, 599}}) // 'Range: bytes=100-199, 500-599'
+httplib::make_range_header({{0, 0}, {-1, 1}})        // 'Range: bytes=0-0, -1'

Keep-Alive connection

cpp
httplib::Client cli("localhost", 1234);
+
+cli.Get("/hello");         // with "Connection: close"
+
+cli.set_keep_alive(true);
+cli.Get("/world");
+
+cli.set_keep_alive(false);
+cli.Get("/last-request");  // with "Connection: close"

Redirect

cpp
httplib::Client cli("yahoo.com");
+
+auto res = cli.Get("/");
+res->status; // 301
+
+cli.set_follow_location(true);
+res = cli.Get("/");
+res->status; // 200

Use a specitic network interface

NOTE: This feature is not available on Windows, yet.

cpp
cli.set_interface("eth0"); // Interface name, IP address or host name

OpenSSL Support

SSL support is available with CPPHTTPLIB_OPENSSL_SUPPORT. libssl and libcrypto should be linked.

NOTE: cpp-httplib currently supports only version 1.1.1.

c++
#define CPPHTTPLIB_OPENSSL_SUPPORT
+
+SSLServer svr("./cert.pem", "./key.pem");
+
+SSLClient cli("localhost", 8080);
+cli.set_ca_cert_path("./ca-bundle.crt");
+cli.enable_server_certificate_verification(true);

Compression

The server can applie compression to the following MIME type contents:

  • all text types except text/event-stream
  • image/svg+xml
  • application/javascript
  • application/json
  • application/xml
  • application/xhtml+xml

Zlib Support

'gzip' compression is available with CPPHTTPLIB_ZLIB_SUPPORT. libz should be linked.

Brotli Support

Brotli compression is available with CPPHTTPLIB_BROTLI_SUPPORT. Necessary libraries should be linked. Please see https://github.com/google/brotli for more detail.

Compress request body on client

c++
cli.set_compress(true);
+res = cli.Post("/resource/foo", "...", "text/plain");

Compress response body on client

c++
cli.set_decompress(false);
+res = cli.Get("/resource/foo", {{"Accept-Encoding", "gzip, deflate, br"}});
+res->body; // Compressed data

Split httplib.h into .h and .cc

bash
> python3 split.py
+> ls out
+httplib.h  httplib.cc

NOTE

g++

g++ 4.8 and below cannot build this library since <regex> in the versions are broken.

Windows

Include httplib.h before Windows.h or include Windows.h by defining WIN32_LEAN_AND_MEAN beforehand.

cpp
#include <httplib.h>
+#include <Windows.h>
cpp
#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <httplib.h>

Note: Cygwin on Windows is not supported.

License

MIT license (© 2020 Yuji Hirose)

Special Thanks To

These folks made great contributions to polish this library to totally another level from a simple toy!

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/system/masking/include/readme.html b/system/masking/include/readme.html new file mode 100644 index 00000000000..a6fae64d9bb --- /dev/null +++ b/system/masking/include/readme.html @@ -0,0 +1,116 @@ + + + + + + Data Masking Framework | HPCC Platform + + + + + + + + + + + + + +
Skip to content

Data Masking Framework

This is a high level description of the data obfuscation framework defined in system/masking/include. The framework includes a platform component, the engine, that exposes obfuscation logic supplied by dynamically loaded plugin libraries. The framework can be used to obfuscate sensitive data, such as passwords and PII. Possible use cases including preventing trace output from revealing sensitive values and partially masking values in situations where a user needs to see "enough" of a value to confirm its correctness without seeing all of the value (e.g., when a user is asked to confirm the last four digits of an account number).

FileContents
datamasking.hDeclares the core framework interfaces. Most are used by the engine and plugins.
datamaskingengine.hppDefines the engine component from the core engine interface.
datamaskingplugin.hppDefines a set of classes derived from the core plugin interfaces that can be used to provide rudimentary obfuscation. It is expected that the some or all classes will be subclassed, if not replaced outright, in new plugins.
datamaskingshared.hppDefines utilities shared by engine and plugin implementations.

Glossary

Domain

A domain is a representation of the obfuscation requirements applicable to a set of data.

Consider that the data used to represent individuals likely differs between countries. With different data, requirements for obfuscation may reasonably be expected to vary. Assuming that requirements do change between countries, each country's requirements could logically constitute a separate domain. The capacity to define multiple domains does not create a requirement to do so.

Requirements can change over time. To support this, a domain can be seen as a collection of requirement snapshots where each snapshot defines the complete set of requirements for the domain at a point in time. Snapshots are referenced by unique version numbers, which should be sequential starting at 1.

Obfuscation is always applied based on a single snapshot of a domain's requirements.

Domains are represented in the framework interface as text identifiers. Each distinct domain is identified by at least one unique identifier.

Masker

A masker is a provider of obfuscation for a single snapshot of a domain's requirements. There are three masking operations defined in this framework. Each instance decides which of the three it will support, and how it will support them. The three operations are:

  • maskValue obfuscates individual values based on snapshot-defined meanings. For example, a value identified as a password might require complete obfuscation anywhere it appears, or an account number may require complete obfuscation in some cases and partial obfuscation in others.
  • maskContent obfuscates a variable number of values based on context provided by surrounding text. For example, the value of an HTTP authentication header or the text between <Password> and </Password> might require obfuscation. This operation can apply to both structured and unstructured text content.
  • maskMarkupValue obfuscates individual values based on their locationa within an in-memory representation of a structured document, such as XML or JSON. For example, the value of element Password might require obfuscation unconditionally, while the value of element Value might require obfuscation only if a sibling element named Name exists with value password. This operation relies on the caller's ability to supply context parsed from structured content.

A masker may be either stateless or stateful. With a stateless masker, identical input will produce identical output for every requested operation. A stateful masker, however, enables its user to affect operation outputs (i.e., identical input may not produce identical output for each operation).

Maskers are represented in the framework interface using IDataMasker, with IDataMaskerInspector providing access to less frequently used information about the domain.

Profile

A profile is a stateless masker. Each instance defines the requirements of one or more snapshots of a single domain.

Snapshots are versioned. Each profile declares a minimum, maximum, and default version. Masker operations apply to the default version. Other declared versions may be accessed using a stateful context, which the profile can create on demand.

Each instance must support at least one version of a domain's requirements. Whether an instance supports more than one version depends on the implementation and on user preference. A domain can be viewed as a collection of one or more profiles where each profile defines a unique set of requirement snapshots applicable to the same underlying data.

Refer to IDataMaskingProfile (extending IDataMasker) and IDataMaskingProfileInspector (extending IDataMaskerInspector) for additional information.

Context

A context is a stateful masker. Instantiated by and tightly coupled to a profile, it provides some user control over how masking operations are completed.

  • For a profile supporting multiple versions, the requirements of a non-default version may be applied.
  • Custom properties, defined by a profile, may be managed.
    • valuetype-set is a pre-defined property used to select the group of value types that may be masked by any operation.
    • rule-set is a pre-defined property used to select the group of rules that will be applied for maskContent requests.
    • Profile implementations may define additional properties as needed. For example, one might define mask-pattern to override the default obfuscation pattern to avoid replicating (and complicating) configurations just to change the appearance of obfuscated data.
  • Trace output produced by operation requests, including errors and warnings encountered during request processing, can be controlled per context. This does not affect the operation output, per-se, but provides compatibility with transactional trace output control.

Refer to IDataMaskingProfileContext (extending IDataMasker) and IDataMaskingProfileContextInspector (extending IDataMaskerInspector) for additional information.

Value Type

Each snapshot defines one or more value types. A value type is a representation of the requirements pertaining to a particular concept of a domain datum. Requirements include:

  • instructions for identifying occurrences based on contextual clues found in a body of text; and
  • instructions for applying obfuscation to an identified value.

A Social Security Number, or SSN, is a U.S.-centric datum that requires obfuscation and for which a value type may be defined. Element names associated with an SSN may include, but are not limited to, SSN and SOCS; the value type is expected to identify all such names used within the domain. SSN occurrences are frequently partially masked, with common formats being to mask only the first four or the last five digits of the nine digit number; the value type defines which formats are available besides the default of masking all characters.

Value types are represented in the framework using IDataMaskingProfileValueType. They are accessed through the IDataMaskerInspector interface.

Mask Style

A mask style describes how obfuscation is applied to a value. It is always defined in the context of a value type, and a value type may define multiple.

A value type is not required to define any mask styles. If none are defined, all value characters are obfuscated. If the requested mask style is not defined, the default obfuscation occurs; the value type will not attempt to guess which of the defined styles is appropriate.

Mask styles are represented in the framework using IDataMaskingProfileMaskStyle. They are accessed through the IDataMaskingProfileValueType interface.

Rule

A rule contains the information necessary to locate at least one occurrence of a value type datum to be obfuscated. It is always defined in the context of a value type, and a value type may define multiple.

maskValue requests do not use rules. maskContent requests rely on rules for locating affected values, with the relationship between a profile and its rules an implementation detail. maskMarkupValue requests, when implemented, may also use rules or may take an entirely different approach.

Rules are represented in the framework as an abastract concept that cannot be inspected individually. Inspection may be used to establish the presence of rules, but not to examine individual instances.

Plugin

The combination of a shared library and entry point function describes a plugin. The input to a plugin is a property tree describing one or more profiles. The output of an entry point function is an iterator of profiles. The profiles created by the function may all be associated with the same domain, but this is not required.

Plugin results are represented in the framework using IDataMaskingProfileIterator.

Engine

An engine is the platform's interface to obfuscation. It loads domains by loading one or more plugins. Plugins yield profiles, from which domains are inferred.

Once configured with at least one domain, a caller can obtain obfuscation in multiple ways:

  1. As an instance of a stateless masker, an engine can provide obfuscation. The default version of the default profile of the default domain will be used, and no custom context properties can be used. This is the simplest integration, appropriate for use when the host process implicitly trusts that the default configuration is sufficient.
  2. An engine may be used to obtain a stateless profile for a specific domain. If no version is requested, the default profile of the requested domain will be used. If a version is specified, the domain profile supporting the requested version is used. This usage supports host processes that are aware of domains and need to use specific instances.
  3. An engine may be used to obtain a context tied to a specific version of a domain. The default domain may be requested with an empty string. The default version may be requested by passing 0. With a context, a caller may manipulate further the environment for obfuscation requests.

Engines are represented in the framework using IDataMaskingEngine (extending IDataMasker) and IDataMaskingEngineInspector for additional information.

Environment

This section assumes a context is in use. Absent a context, only the default state of the default version of a profile can be used.

The framework reserves multiple custom property names, which are described in subsequent subsections. It also allows implementations to define additional properties using any non-reserved name.

Suppose an implementation defines a property to override the default obfuscation pattern. Let's call this property default-pattern. A caller could interrogate a masker to know if default-pattern is accepted. If accepted, setProperty("default-patterns", "#") can be used to register an override.

All custom properties, whether defined in the framework or in third party libraries, are managed using a generic context interface. This interface includes:

bool hasProperties() cosnt
+bool hasProperty(const char* name) const
+const char* queryProperty(const char* name) const
+bool setProperty(const char* name, const char* value)
+bool removeProperty(const char* name)

Value Type Sets

bool setProperty(const char* name, const char* value);

Overview

The abstraction includes the concept of sets of related value types. All value types should be assigned membership in at least one set. One set should be selected by default. The custom context property valuetype-set is used to select a different set.

The rationale for this is an expectation that certain data always requires obfuscation. A password, for example, would never not require obfuscation. Other data may only require obfuscacation in certain situations, such as when required by individual customer agreements. Callers should not be required to complete additional steps to act on data that must always be obfuscated, and should not need to know which data falls in which category.

The set name "*" is reserved to select all value types regardless of their defined set membership. This mechanism is intended to assist with compatibility checks, and should be used with care in other situations.

Runtime Compatibility

Use acceptsProperty to determine whether a snapshot recognizes a custom property name. Use usesProperty to learn if the snapshot includes references to the custom property name. Acceptance implies awareness of the set (and what it represents) even if selecting it will not change the outcome of any masking request. Usage implies that selecting the set will change the outcome of at least one masking operation.

The property valuetype-set is used to select a named set. A check of this property addresses whether the concept of a set is accepted or used by the snapshot.

For improved compatibility checks, implementations are encouraged to reserve additional property names that enable checks for individual set names. For each set name, foo, the included implementations report property valuetype-set:foo as used.

Implementation

The included implementations allow membership in either a default, unnamed set or in any number of named sets. Absent a contextual request for a named set, the unnamed set is selected by default. With a contextual set request, the members of the named set are selected in addition to members of the unnamed set. Members of the unnamed set are never not selected.

Unnamed set membership cannot be defined explicitly. Because this set is always selected, assigning a value type to both a named and the unnamed set is redundant - the type will be selected whether the named set is requested or not.

Example 1a: default value type

profile:
+  valueType:
+    - name: type1

Value type type1 belongs to the default, unnamed value type set. Property valuetype-set is accepted, but not used; an attempt to use it will change no results.

Example 1b: sample set memberships

profile:
+  valueType:
+    - name: type1
+    - name: type2
+      memberOf:
+        - name: set1
+        - name: set2
+    - name: type3
+      memberOf:
+        - name: set2
+        - name: set3
+    - name: type4
+      memberOf:
+        - name: set1
+    - name: type5
+      memberOf:
+        - name: set3

Extending the previous example, five types and four sets are in use. Property valuetype-set is both accepted and used, as it can now impact results. Properties valuetype-set:set1, valuetype-set:set2 and valuetype-set:set3 are also both accepted and used, but are intended only for use with compatibility checks.

This table shows which types are selected for each requested set:

unnamedset1set2set3*
type1YYYYY
type2NYYNY
type3NNYYY
type4NYNNY
type5NNNYY

Example 1c: sample accepted sets

profile:
+  valueType:
+    - name: type1
+    - name: type2
+      memberOf:
+        - name: set1
+        - name: set2
+    - name: type3
+      memberOf:
+        - name: set2
+        - name: set3
+    - name: type4
+      memberOf:
+        - name: set1
+    - name: type5
+      memberOf:
+        - name: set3
+  property:
+    - name: 'valuetype-set:set4'
+    - name: 'valuetype-set:set5'

Continuing the previous example, the profile has declared acceptance of two additional sets using properties valuetype-set:set4 and valuetype-set:set5. Selection of these sets will not change results, but a caller might accept acceptance of a set as sufficient to establish compatibility.

Rule Sets

bool setProperty(const char* name, const char* value);

Overview

The abstraction includes the concept of sets of related rules. All rules should be assigned membership in at least one set. One set should be selected by default. The custom context property rule-set is used to select a different set.

The rationale for this is similar to yet different than that of value type sets. Instead of one set of rules that are always selected, backward compatibility with a proprietary legacy implementation requires that the default set be replaced by an alternate collection.

The set name "*" is reserved to select all rules regardless of their defined set membership. This does not override constraints imposed by the current value type set. This mechanism is intended to assist with compatibility checks, and should be used with care in other situations.

Runtime Compatibility

Use acceptsProperty to determine whether a snapshot recognizes a custom property name. Use usesProperty to learn if the snapshot includes references to the custom property name. Acceptance implies awareness of the set (and what it represents) even if selecting it will select no rules. Usage implies that selecting the set will select at least one rule.

The property rule-set is used to select a named set. A check of this property addresses whether the concept of a set is accepted or used by the snapshot.

For improved compatibility checks, implementations are encouraged to reserve additional property names that enable checks for individual set names. For each set name, foo, the included implementations report property rule-set:foo as used.

Implementation

The included implementations allow membership in any number of named sets as well as a default, unnamed set. Absent a contextual request for a named set, the unnamed set is selected by default. With a contextual set request, only the members of the requested set are selected.

Unlike value type sets, the unnamed set is not always selected. Because of this difference, a rule may be assigned explicit membership in the unnamed set. This is optional for rules that belong only to the unnamed set and is required for rules meant to be selected as part of the unnamed set and one or more named sets.

Example 2: rule set memberships

profile:
+  valueType:
+    name: type1
+    rule:
+      - name: foo
+      - memberOf:
+          - name: ''
+      - memberOf:
+          - name: ''
+          - name: set1
+      - memberOf:
+          - name: set1
+  property:
+    name: 'rule-set:set2'

The rules described here are intentionally incomplete, showing only what is necessary for this example. Four rules are defined:

  1. The rule named foo implicitly belongs to the unnamed set.
  2. The second rule explicitly belongs to the unnamed set.
  3. The third rule explicitly belongs to the unnamed set and to set1.
  4. The fourth rule belongs to set1.

property rule-set is both accepted and used. Properties rule-set: and rule-set:set1 are both accepted and used, intended for use with compatibility checks. Property rule-set:set2 is accepted.

Operations

maskValue

bool maskValue(const char* valueType, const char* maskStyle, char* buffer, size_t offset, size_t length, bool conditionally) const;
+bool maskValueConditionally(const char* valueType, const char* maskStyle, char* buffer, size_t offset, size_t length) const;
+bool maskValueUnconditionally(const char* valueType, const char* maskStyle, char* buffer, size_t offset, size_t length) const;

Overview

Given a single domain datum, obfuscate the content "as needed". The framework anticipates two interpretations of "as needed", either conditional or unconditional:

  • Conditional means that values of a named type are only obfuscated if the named type is known by and selected in the snapshot. This usage assumes that the profile is the authoritative source for obfuscation requirements. The profile's consumer may ask for any value to be obfuscated, but the profile is not required to act.
  • Unconditional means that all value obfuscation requests will result in obfuscation. The named type is not required to be known by or selected in the snapshot. This usage assumes that the profile's consumer is the authoritative source for obfuscation requirements. If a consumer asks for a value to be obfuscated, the profile must act.

Each plugin will define its own interpretation of "as needed". The API distinction between conditional and unconditional is a hint intended to guide implementations capable of both, and should be ignored by implementations that are not.

The buffer, offset, and length parameters define what is assumed to be a single domain datum. That is, every character in the given character range is subject to obfuscation.

The valueType parameter determines if obfuscation is required, with conditionally controlling whether an unrecognized valueType is ignored (true) or forced to obfuscate (false).

The maskStyle parameter may be used to affect the nature of obfuscation to be applied. If the parameter names a defined mask style, that obfuscation format is applied. If the parameter does not name a define mask style, the value type's default obfuscation format is applied.

Runtime Compatibility

The canMaskValue method of IDataMasker can be used to establish whether a snapshot supports this operation. A result of true indicates that the plugin is capable of performing obfuscation in response to a request. Whether a combination of input parameters exists that results in obfuscation depends on the definition of the snapshot.

The hasValueType method of IDataMaskerInspector can be used to check if a given name is selected in the snapshot. The reserved name "*" may be used to detect unconditional obfuscation support when available in the snapshot. Names defined yet unselected in the snapshot are not directly detectable, but can be detected by comparing results of using multiple context configurations.

The name "*" is reserved by the abstraction for compatibility checks.

To confirm the availability of a specific mask style first requires confirmation of the value type to which the mask style is related. Use queryValueType to obtain the defined instance and, from the result, call queryMaskStyle to confirm the existence of the mask stye. For each of these, corresponding get... methods are defined to obtain new references to the objects. The methods are declared by IDataMaskerInspector.

Implementations

The included implementations are inherently conditional. Obfuscation depends on matching valueType with a selected value type instance in the snapshot, where an instance is selected if it belongs to the currently selected value type set.

Unconditional obfuscation is enabled in profiles that include a value type instance named "*". If an instance with this name is selected by the currently selected value type set, all values for undefined value types will be obfuscated. Values for defined but unselected value types will not be obfuscated.

  • A value of type "foo" will be obfuscated when type "foo" is selected by the currently selected value type set.
  • A value of type "foo" will be obfuscated when type "foo" is undefined and type "*" is selected by the currently selected value type set.
  • A value of type "foo" will not be obfuscated when type "foo" is defined but unselected by the currently selected value type set.

Example 3a: conditional obfuscation

valueType:
+  - name: type1
+  - name: type2
+    memberOf:
+      - name: set1

This snippet declares two value types, with two total sets. The table shows which values will be conditionally obfuscated based on a the combination of valueType and the currently selected value type set.

valueTypeSelected SetObfuscated
type1N/AYes
type1set1Yes
type2N/ANo
type2set1Yes
typeNN/ANo
typeNset1No

"typeN" in the table represents any named type, excluding the reserved "*", not explicitly defined in the profile.

Example 3b: unconditional obfuscation

valueType:
+  - name: type1
+  - name: type2
+    memberOf:
+      - name: set1
+  - name: *

Extending the previous example with a third value type, values of unknown type are now obfuscated. Values of known but unselected type remain unobfuscated.

valueTypeSelected SetObfuscated
type1N/AYes
type1set1Yes
type2N/ANo
type2set1Yes
typeNN/AYes
typeNset1Yes

The name reserved by the abstraction to detect support for unconditional obfuscation is the same name reserved by the implementation to define that support.

"typeN" in the table represents any named type, excluding the reserved "*", not explicitly defined in the profile.

maskContent

bool maskContent(const char* contentType, char* buffer, size_t offset, size_t length) const;

Overview

The buffer, offset, and length parameters define what is assumed to be a blob of content that may contain zero or more occurrences of domain data. The blob is expected to contain sufficient context allowing a snapshot's rules to locate any included occurrences.

The context parameter optionally influences which rules are selected in a snapshot, while the contentType parameter offers a hint as to the blob's data format. Each snapshot rule may be assigned a format to which it applies. Operation requests may be optimized by limiting the number of selected rules applied by describing the format. Selected rules not assigned a format will be applied in all requests.

There is no equivalent to the unconditional mode offered by maskValue. Content obfuscation is always conditional on matching rules.

Runtime Compatibility

The canMaskContent method of IDataMasker can be used to establish whether a snapshot supports this operation. A result of true indicates that the plugin is capable of performing obfuscation in response to a request. Whether a combination of input parameters exists that results in obfuscation depends on the definition of the snapshot.

The hasRule method of IDataMaskerInspector indicates if at least one rule is selected in the snapshot for a given content type. Rules not associated with any content type may be interrogated using an empty string.

It is not possible to discern any information about the selected rules, such as their associated value types or the cues used to apply them in a buffer.

Implementations

The groundwork exists for two types of rules, serial and parallel. Serial rules, as the name implies, are evaluated sequentially. Parallel rules, on the other hand, are evaluated concurrently. Concurrent evaluation is expected to be more efficient than sequential, but no concurrent implementation is provided.

The included sequential implementation should be viewed as a starting point. Each rule defines a start token and an optional end token. For each occurrence of the start token in the blob, a corresponding search for an end token (newline if omitted), is performed. When both parts are found, the content between is obfuscated using the associated value type's default mask style.

Traversing a potentially large blob of text once per rule is inefficient. A concurrent implementation is in development to improve performance and capabilities. A domain originated using the original implementation and migrated to the new implementation illustrates one domain implemented by multiple plugins and, by extension, multiple profiles.

maskMarkupValue

bool maskMarkupValue(const char* element, const char* attribute, char* buffer, size_t offset, size_t length, IDataMaskingDependencyCallback& callback) const;

Overview

Where maskValue obfuscates individual values based on what the caller believes the value to be, and maskContent obfuscates any number of values it identifies based on cues found in surrounding text, maskMarkupValue obfuscastes individual values based on cues not provided to it.

Given a value and a relative location within a structure document, an implementation must decide whether obfuscation is required, may be required, or is not required. If required, it can obfuscate immediately. If not required, and can return immediately. If it might be required, the implementation must request the context it needs from the caller in order to make a final determination.

A request to mask the content of an element named Value might depend on the content of a sibling element named Name. If value of Value probably does not require obfuscation when the value of Name is city, but almost certainly does require obfuscation when the value of Name is password. When processing the request for Value, an implementation must ask for the value of a sibline named Name to decide whether or not to obfuscate the data.

There is no equivalent to the unconditional mode offered by maskValue. Markup value obfuscation presumes that the caller is traversing a structured document without awareness of which values require obfuscation. If the caller knows which values require obfuscation, it should use maskValue on those specific values.

Runtime Compatibility

    virtual bool getMarkupValueInfo(const char* element, const char* attribute, const char* buffer, size_t offset, size_t length, IDataMaskingDependencyCallback& callback, DataMaskingMarkupValueInfo& info) const = 0;

The canMaskMarkupValue method of IDataMasker can be used to establish whether a snapshot supports this operation. A result of true indicates that the plugin is capable of performing obfuscation in response to a request. Whether a combination of input parameters exists that results in obfuscation depends on the definition of the snapshot.

The getMarkupValueInfo method of IDataMaskerInspector determines if the value of an element or attribute requires obfuscation. If required, the info parameter will describe on completion how to perform the obfuscation using maskValue instead of maskMarkupValue. This is done to optimize performance by eliminating the need to reevaluate rules to re-identify a match.

Assme a value type representing passwords is defined. If given an element value containing a URL with an embedded password, obfuscation is required. But most likely not for the entire value. In addition to identifying the value type associated with value, the offset and length of the embedded substring requiring obfuscation are supplied; use of maskValue with these three values and no mask style will apply the default obfuscation only to the embedded password. Alternatively, if a mask style is supplied, use of maskValue with this style and the original value offset and length should obfuscate only the embedded password.

A callback interface is required for all calls to getMarkupValueInfo. The interface is only used when additional document context is required to make a determination about the requested value. In these cases, the checks are proximity-dependent and cannot be performed as part of load-time compatibility checks, when no document is available.

Implementations

TBD

Load-time Compatibility

bool checkCompatibility(const IPTree& requirements) const;

Overview

Use of runtime compatibility checks may be unavoidable in some circumstances, but reliance on this in every script is inefficient. It may also devalue trace output by omitting messaging required to debug an issue, a problem that might only be found when said messaging is needed.

The requirements parameter represents a standardized set of runtime compatibility checks to be applied. By default, each check must pass to establish compatibility. Explicitly optional checks may be included to detect conditions the caller is prepared to work around.

This can test which values will or won't be affected by maskValue, and which obfuscation styles are available. It can test which rule sets will impact maskContent. It specifically excludes maskMarkupValue checks that depend on the proximity of one value to another.

Returning to the earlier SSN example, a caller might require that an SSN value type will be obfuscated. It might also want to use a particilar mask style, but may be prepared for its absence. A caller may prefer to mask the first four digits but may accept masking the last five instead, or may omit certain trace messages.

Implementation

compatibility:
+  - context:
+      - domain: optional text
+        version: optional number
+        property:
+          - name: required text
+            value: required text
+    accepts:
+      - name: required text
+        presence: one of "r", "o", or "p"
+    uses:
+      - name: required text
+        presence: one of "r", "o", or "p"
+    operation:
+      - name: one of "maskValue", "maskContent", or "maskMarkupValue"
+        presence: one of "r", "o", or "p"
+    valueType
+      - name: required text
+        presence: one of "r", "o", or "p"
+        maskStyle:
+          - name: required text
+            presence: one of "r", "o", or "p"
+        Set:
+          - name: required text
+            presence: one of "r", "o", or "p"
+    rule:
+      - contentType: required text
+        presence: one of "r", "o", or "p"

The requirements parameter of checkCompatibility must be either a compatibility element or the parent of a collection of compatibility elements. Each instance may contain at most one context element, three operation elements (one per operation), and a variable number of accepts, uses, valueType, or rule elements.

The context element describes the target of an evaluation. It may include a domain identifier, or assume the default domain. It may include a version number, or assume the default snapshot of the domain. It may specify any number of custom context properties to select various profile elements in the snapshot.

The accepts and uses elements identify custom context property names either accepted or used within a snapshot. Acceptance indicates some part of a snapshot has declared an understanding of the named property. Usage indicates the some part of a snapshot will react to the named value being set. Usage implies acceptance.

To improve compatibility check capabilities, a snapshot may synthesize properties that, if set, will have no effect. Specifically, a caller may be more interested to know if a particular set name (for either value type or rule set membership) is used than to know that an unidentified set name is used.

The Operations element identifies which operations must be supported. Its purpose is to enforce availability of an operation when no more explicit requirements are given (for example, one may require maskContent without also requiring the presence of rules for any given content type). When more explicit requirements are given, the attributes of this element are redundant. If all attributes are redundant, the element may be omitted.

The valueType element describes all optional and mandatory requirements for using maskValue with a single value type name and optional mask style name. Set membership requirements can also be evaluated, which is most useful when compatibility/context/property/@name is "*".

The rule element describes all optional and mandatory requirements for using maskContent with a single content type value. Unlike evaluable value type set membership, rule set membership requirements cannot be evaluated.

In all elements described as accepting @presence, the acceptable values are

  • r: the check is required to pass
  • o: the check is optional
  • p: the check is prohibited from passing

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/system/masking/plugins/datamasker/readme.html b/system/masking/plugins/datamasker/readme.html new file mode 100644 index 00000000000..8642b5cbaea --- /dev/null +++ b/system/masking/plugins/datamasker/readme.html @@ -0,0 +1,47 @@ + + + + + + HPCC Platform + + + + + + + + + + + + + +
Skip to content

libdatamasker.so

This implements a plugin library producing instances of IDataMaskingProfileIterator to be used by an IDataMaskingEngine instance. The library is written in C++, and this section is organized according to the namespace and class names used.

namespace DataMasking

CContext

Standard implementation of IDataMaskingProfileContext and IDataMaskingProfileContextInspector tightly coupled to a specific version of a specific profile instance. It manages custom properties that are accepted by the associated profile, and rejects attempts to set those not accepted. The rejection is one way of letting the caller know that something it might deem essential is not handled by the profile.

Depends on CProfile, an abstract implementation of IDataMaskingProfile and IDataMaskingProfileInspector.

CMaskStyle

Implementation of IDataMaskingProfileMaskStyle providing customized masked output of values. Configuration options include:

  • @maximumVersion is an optional version number, needed only when the style does not apply to the maximum version of the value type in which it is defined.
  • @minimumVersion is an optional version number, needed only when the style does not apply to the minumum version of the value type in which it is defined.
  • @name is a required unique identifier for the style.
  • @overrideDefault is an optional Boolean flag controlling whether the style should replace the default style for the value type in which it is defined.
  • @pattern is an optional character sequence (one or more characters in length) that will be used to mask value content.

CPartialMaskStyle

Am extension of CMaskStyle, this is intended to partially mask values, such as account numbers or telephone numbers, without assuming knowledge of the values. Configuration is generally expressed as:

[ [ @action ] [ @location ] @count  [ @characters ] ]
+

Where:

  • @action what to do, either keep or mask (default); and
  • @location where to do it, either first or last (default); and
  • @count how many characters to examine, a positive integer value; and
  • @characters type of characters to be considered, either numbers, letters, alphanumeric, or all (default)

All four values may be omitted when only the inherited options are needed. If any of the four values are given, count is required and the other three are optional.

A value substring containing at most @count instances of the character class denoted by @characters is identified. Character classes are ASCII numeric characters (numbers), ASCII alphabetic characters (letters), ASCII alphabetic and numeric characters (alphanumeric), or any characters (all).

The value substring is at the start of the value when @location is first, and at the end of the value when @location is last.

The value substring is masked when @action is mask. All of the value with the exception of the substring is masked when @action is keep.

CRule

A base class for rule implementations, this defines standard rule properties without providing any content knowledge for use with maskContent.

Configuration includes:

  • @contentType is an optional, user-defined, label describing the content format to which the rule should be applied. maskContent requests that indicate a content type should apply all rules that match the type in addition to all rules that omit a type.
  • memberOf is an optional and repeating element identifying user-defined set labels. Only rules with membership in the requested set are considered; in the absence of an explictly requested set, a default set with an empty name is implied.
  • memberOf/@name is a required user-defined set label.

rule instances will frequently be specific to a content format. For example, a rule applied to XML markup value may include dependencies on XML syntax which are not applicable when masking JSON content. Rules specific to a markup format can be associated with that format using @contentType. Requests to mask content of a type will apply all rules without a @contentType value or with a matching value, and will exclude all rules with a non-matching value.

CSerialTokenRule

An extension of CRule, this identifies content substrings to be masked based on matching start and end tokens in the content buffer. For each occurrence of a configured start token that is balanced by a corresponding configured end token, the characters between the tokens are masked.

This class may be used with TSerialProfile.

Configuration includes:

  • @endToken is an optional character sequence expected to immediately follow a content substring to be masked. This might be an XML element end tag, or a terminating double quote for a JSON value. A newline, i.e., \n, is assumed if omitted or empty, for masking values such as HTTP headers.
  • @matchCase is an optional Boolean flag controlling whether token matches are case sensitive (true) or insensitivie (false). Matches are case insensitive by default. The implementation is based on ASCII data; case sensitive comparisons of similarly encoded non-ASCI text can work, but case insensitive comparisons are not supported.
  • @startToken is a required character sequence expected to precede a content substring to be masked.

No content type knowledge is implied by this class. An instance with @contentType of xml does not inherently know how to find values in XML markup. The defined tokens must include characters such as < and > to match an element name and quotes to match attribute values.

TPlugin

An implementation of IDataMaskingProfileIterator used for transforming a configuration property tree into a collection of profiles to be returned by a library entry point function. Created profiles are of the same class, identified by the template parameter profile_t.

Configuration includes:

  • profile An optional and repeating element defining a profile. The content of this element depends on profile_t.
  • If and only if profile is not included as a child of the configuration node, the node itself will be treated as a configuration for profile_t. The name of the configuration node is not required to be profile.

TProfile

A concrete extension of CProfile, this manages value types and rules, providing a default implementation of maskValue but leaving other operations to subclasses.

Template parameters are:

  • valuetype_t identifies the concrete implementation of IDataMaskingProfileValueType to be instantiated during configuration.
  • rule_t identifies the concrete implementation of rules to be managed. Creation is assumed to be the responsibility of the value type.
  • context_t identifies the concrete implementation of IDataMaskingProfileContext to be instantiated on demand.

Configuration includes:

  • @defaultVersion is an optional version number identifying the version to be used when version 0 is requested. Omission or 0 implies the maximum version (which is configured before this value).
  • @domain is the required default identifier used to select this profile.
  • legacyDomain is an optional and repeating element identifying alternate identifiers by which the profile may be selected. This enables domain identifier naming conventions to be changed without breaking pre-existing references.
  • legacyDomain/@id is a required identifier of this profile.
  • @maximumVersion is an optional version number identifying the highest version supported by the configuration. Omission or 0 implies a value matching the minimum version (whether implicitly or explicitly defined).
  • @minimumVersion is an optional version number identifying the lowest version supported by the configuration. Omission or 0 implies 1.
  • @name is an optional label for the instance. If given, the value is used in trace output.
  • property is an optional and repeating element describing a custom context property recognized by the profile. The profile can declare awareness of properties without explicit use of them.
  • property/@name is a required context property name.
  • property/@minimumVersion is an optional version number indicating the lowest profile version aware of the property. Omission or 0 implies the profile minimum.
  • property/@maximumVersion is an optional version number indicating the highest profile version aware of the property. Omisssion or 0 implies the profile maximum.
  • valueType is an optional and repeating element describing the value types defined by the profile. Element content depends on the value of valuetype_t.

For profiles supporting a single version, value type names must be unique. For profiles supporting multiple versions, value type names may be repeated but must be unique for each version. To illustrate, consider this snippet:

profile:
+  minimumVersion: 1
+  maximumVersion: 2
+  valueType:
+    - name: foo
+      maximumVersion: 1
+    - name: foo
+      minimumVersion: 2
+    - name: bar
+    - name: bar
+      minimumVersion: 2
+

In the example, foo and bar are each defined twice. The redefinition of foo is acceptable because each instance applies to a different version. The redefinition of bar is invalid because both instances claim to apply to version 2.

The value type name * is reserved by this class. A profile that includes a value type named * supports unconditional value masking. maskValue requests specifying unknown value type names can fall back to this special type and force masking for these values. Without this type, maskValue masks what it thinks should be masked; with this type, it trusts the caller to request masking for only those values known to require it. Value types included in unselected value type sets are known to the profile and, as such, can be used to prevent specific typed values from ever being masked.

The requirement of a type definition instead of a simpler flag is to enable the definition of mask styles. It has the side effect of enabling the definition of rules. In theory, an entire profile could be defined using a single value type. This may make sense in some cases, but not in all. For example, a partial mask style intended for use with U.S. Social Security numbers could be inappropriately applied to a password. Use care when configuring this type.

TSerialProfile

An extension of TProfile that adds support for maskContent. It is assumed by maskContent that each applicable rule must be applied serially, i.e., one after the other, using an bool applyRule(buffer, length, context) interface.

Template parameters are unchanged from TProfile.

Configuration options are unchanged from TProfile.

TValueType

An implementation of IDataMaskingProfileValueType that manages mask styles and creates rules for the profile.

Template parameters are:

  • maskstyle_t identifies the concrete implementation of IDataMaskingProfileMaskStyle to be instantiated during configuration.
  • rule_t identifies the concrete implementation of rules to be created during configuration.

Configuration includes:

  • @maximumVersion is an optional version number, needed only when the type does not apply to the maximum version of the profile in which it is defined.
  • @minimumVersion is an optional version number, needed only when the type does not apply to the minumum version of the profile in which it is defined.
  • maskStyle is an optional and repeating element describing mask styles defined by the type. Element content depends on the value of maskstyle_t.
  • memberOf is an optional and repeating element identifying user-defined set labels. All value types that are not explicitly assigned set membership are considered for all masking requests. Value types assigned set membership are considered only when one of their assigned sets is selected with the request context.
  • memberOf/@name is a required user-defined set label.
  • @name is a required unique identifier for the type.
  • rule is an optional and repeating element describing rules defined by the type. Element content depends on the value of rule_t.

For value types supporting a single version, mask style names must be unique. For types supporting multiple versions, style names may be repeated but must be unique for each version. To illustrate, consider this snippet:

valueType:
+  minimumVersion: 1
+  maximumVersion: 2
+  maskStyle:
+    - name: foo
+      maximumVersion: 1
+    - name: foo
+      minimumVersion: 2
+    - name: bar
+    - name: bar
+      minimumVersion: 2
+

In the example, foo and bar are each defined twice. The redefinition of foo is acceptable because each instance applies to a different version. The redefinition of bar is invalid because both instances claim to apply to version 2.

Entry Point Functions

This section describes the entry point functions exported by the shared library. The library must export one function, and may export multiple functions.

The description of each entry point will identify which of the previously described classes is used to represent the returned collection of profiles. If a templated class is identified, the template parameters are also listed. Refer to the class descriptions for additional information.

newPartialMaskSerialToken

Returns a (possibly empty) collection of profiles supporting maskValue and maskContent operations.

  • The profile collection is an instance of TPlugin.
  • Collection profiles are implemented using TProfile.
  • Profile value types are implemented using TValueType.
  • Value type mask styles are implemented using CPartialMaskStyle.
  • Value type and profile rules are implemented using CSerialTokenRule.
  • Profile contexts are implemented using CContext.

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/system/security/plugins/jwtSecurity/README.html b/system/security/plugins/jwtSecurity/README.html new file mode 100644 index 00000000000..928364d1bab --- /dev/null +++ b/system/security/plugins/jwtSecurity/README.html @@ -0,0 +1,31 @@ + + + + + + HPCC Platform + + + + + + + + + + + + + +
Skip to content

JWT Authorization Security Manager Plugin

The purpose of this plugin is to provide authentication and authorization capabilities for HPCC Systems users, with the credentials passed via valid JWT tokens.

The intention is to adhere as closely as possibly to the OpenID Connect (OIC) specification, which is a simple identity layer on top of the OAuth 2.0 protocol, while maintaining compatibility with the way HPCC Systems performs authentication and authorization today. More information about the OpenID Connect specification can be found at https://openid.net/specs/openid-connect-core-1_0.html.

One of the big advantages of OAuth 2.0 and OIC is that the service (in this case, HPCC Systems) never interacts with the user directly. Instead, authentication is performed by a trusted third party and the (successful) results are passed to the service in the form of a verifiable encoded token.

Unfortunately, HPCC Systems does not support the concept of third-party verification. It assumes that users -- really, any client application that operates as a user, including things like IDEs -- will submit username/password credentials for authentication. Until that is changed, HPCC Systems won't be able to fully adhere to the OIC specification.

We can, however, implement most of the specification. That is what this plugin does.

NOTE: This plugin is not available in a Windows build.

Code Documentation

Doxygen (https://www.doxygen.nl/index.html) can be used to create nice HTML documentation for the code. Call/caller graphs are also generated for functions if you have dot (https://www.graphviz.org/download/) installed and available on your path.

Assuming doxygen is on your path, you can build the documentation via:

cd system/security/plugins/jwtSecurity
+doxygen Doxyfile
+

The documentation can then be accessed via docs/html/index.html.

Theory of Operations

The plugin is called by the HPCC Systems esp process when a user needs to be authenticated. That call will contain the user's username and either a reference to a session token or a password. The session token is present only for already-authenticated users.

If the session token is not present, the plugin will call a JWT login service (also known as a JWT login endpoint) with the username and password, plus a nonce value for additional security.

That service authenticates the username/password credentials. If everything is good, the service constructs an OIC-compatible token that includes authorization information for that user and returns it to the plugin. The token is validated according to the OIC specification, including signature verification.

Note that token signature verification requires an additional piece of information. Tokens can be signed with a hash-based algorithm or with a public key-based algorithm (the actual algorithm used is determined by the JWT service). To verify either kind of algorithm, the plugin will need either the secret hash key or the public key that matches what the JWT service used. That key is read by the plugin from a file, and the file is determined by a configuration setting (see below). It is possible to change the contents of that file without restarting the esp process. Note, though, that the plugin may not notice that the file's contents have changed for several seconds (changes do not immediately take effect).

HPCC Systems uses a well-defined authorization scheme, originally designed around an LDAP implementation. That scheme is represented within the token as JWT claims. This plugin will unpack those claims and map to the authorization checks already in place within the HPCC Systems platform.

OIC includes the concept of refresh tokens. Refresh tokens enable a service to re-authorize an existing token without user intervention. Re-authorization typically happens due to a token expiring. Tokens should have a relatively short lifetime -- e.g. 15-30 minutes -- to promote good security and also give administrators the ability to modify a user's authorization while the user is logged in. This plugin fully supports refresh tokens by validating token lifetime at every authorization check and calling a JWT refresh service (also known as a JWT refresh endpoint) when needed. This largely follows the OIC specification.

Deviations From OIC Specification

  • Initial authentication: As stated above, the esp process will gather the username/password credentials instead of a third party, then send those credentials off to another service. In a true OIC configuration, the client process (the esp process) never sees user credentials and relies on an external service to gather them from the user.
  • The request made to the JWT login service is a POST HTTP or HTTPS call (depending on your configuration) containing four items in JSON format; example:
	{
+		"username": "my_username",
+		"password": "my_password",
+		"client_id": "https://myhpcccluster.com",
+		"nonce": "hf674DTRMd4Z1s"
+	}

Implications of Deviations

The most obvious outcome of this implementation is that a custom service/endpoint needs to be available. Or rather two services: One to handle the initial user login and one to handle token refreshes. Neither service precisely handles requests and replies in an OIC-compatible way, but the tokens themselves are OIC-compatible, which is good. That allows you to use third-party JWT libraries to construct and validate those tokens.

HPCC Systems Configuration Notes

Several items must be defined in the platform's configuration. Within configmgr, the jwtsecmgr Security Manager plugin must be added as a component and then modified according to your environment:

  • The URL or unique name of this HPCC Systems cluster, used as the client_id in token requests
  • Full URL to the JWT Login Endpoint (should be HTTPS, but not required)
  • Full URL to the JWT Refresh Endpoint (should be HTTPS, but not required)
  • Boolean indicating whether to accept self-signed certificates for those endpoints; defaults to false
  • Secrets vault key/name or subdirectory under /opt/HPCCSystems/secrets/esp in which the JWT key used for the chosen signature algorithm is stored; defaults to "jwt-security"
  • Default permission access level (either "Full" or "None"); defaults to "Full"
  • Default workunit scope access level (either "Full" or "None"); defaults to "Full"
  • Default file scope access level (either "Full" or "None"); defaults to "Full"

Only the first three items have no default values and must be supplied.

Once the jwtseccmgr component is added, you have to tell other parts of the system to use the plugin. For user authentication and permissions affecting features and workunit scopes, you need to add the plugin to the esp component. Instructions for doing so can be found in the HPCC Systems Administrator's Guide manual (though the manual uses the htpasswd plugin as an example, the process is the same).

If you intend to implement file scope permissions then you will also need provide Dali information about the JWT plugin. In configmgr, within the Dali Server component, select the LDAP tab. Change the authMethod entry to secmgrPlugin and enter "jwtsecmgr" as the authPluginType. Make sure checkScopeScans is set to true.

HPCC Systems Authorization and JWT Claims

This plugin supports all authorizations documented in the HPCC Systems® Administrator's Guide with the exception of "View Permissions". Loosely speaking, the permissions are divided into three groups: Feature, Workunit Scope, and File Scope.

Feature permissions are supported exactly as documented. A specific permission would exist as a JWT claim, by name, with the associated value being the name of the permission. For example, to grant read-only access to ECL Watch, use this claim:

	{ "SmcAccess": "Read" }

File and workunit scope permissions are handled the same way, but different from feature permissions. The claim is one of the Claim constants in the tables below, and the associated value is a matching pattern. A pattern can be simple string or it can use wildcards (specifically, Linux's file globbing wildcards). Wildcards are not typically needed.

Multiple patterns can be set for each claim.

Workunit Scope Permissions

MeaningClaimValue
User has view rights to workunit scopeAllowWorkunitScopeViewpattern
User has modify rights to workunit scopeAllowWorkunitScopeModifypattern
User has delete rights to workunit scopeAllowWorkunitScopeDeletepattern
User does not have view rights to workunit scopeDenyWorkunitScopeViewpattern
User does not have modify rights to workunit scopeDenyWorkunitScopeModifypattern
User does not have delete rights to workunit scopeDenyWorkunitScopeDeletepattern

File Scope Permissions

MeaningClaimValue
User has view rights to file scopeAllowFileScopeViewpattern
User has modify rights to file scopeAllowFileScopeModifypattern
User has delete rights to file scopeAllowFileScopeDeletepattern
User does not have view rights to file scopeDenyFileScopeViewpattern
User does not have modify rights to file scopeDenyFileScopeModifypattern
User does not have delete rights to file scopeDenyFileScopeDeletepattern

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/testing/regress/cleanupReadme.html b/testing/regress/cleanupReadme.html new file mode 100644 index 00000000000..f9df77ec0cd --- /dev/null +++ b/testing/regress/cleanupReadme.html @@ -0,0 +1,24 @@ + + + + + + Cleanup Parameter of Regression Suite run and query sub-command | HPCC Platform + + + + + + + + + + + + + +
Skip to content

Cleanup Parameter of Regression Suite run and query sub-command

The cleanup parameter has been introduced to allow the user to automatically delete the workunits created by executing the Regression Suite on their local system.

It is an optional argument of the run and query sub-command.

A custom logging system also creates log files for each execution of the run and query sub-command that contains information about the workunit deletion.

Command:

./ecl-test run --cleanup [mode]

./ecl-test query --cleanup [mode]

Modes allowed are ‘workunits’, ‘passed’. Default is ‘none’.

  • workunits - all passed and failed workunits are deleted.

  • passed - only the passed workunits of the queries executed are deleted.

  • none - no workunits created during the current run command are deleted.

Result:

The sample terminal output for hthor target:

./ecl-test query ECL_query action1.ecl action2.ecl action4.ecl action5.ecl -t hthor --cleanup workunits

[Action] Suite: hthor
[Action] Queries: 4
[Action] 1. Test: action1.ecl
[Pass] 1. Pass action1.ecl - W20240526-094322 (2 sec)
[Pass] 1. URL http://127.0.0.1:8010/?Widget=WUDetailsWidget&Wuid=W20240526-094322
[Action] 2. Test: action2.ecl
[Pass] 2. Pass action2.ecl - W20240526-094324 (2 sec)
[Pass] 2. URL http://127.0.0.1:8010/?Widget=WUDetailsWidget&Wuid=W20240526-094324
[Action] 3. Test: action4.ecl
[Pass] 3. Pass action4.ecl - W20240526-094325 (2 sec)
[Pass] 3. URL http://127.0.0.1:8010/?Widget=WUDetailsWidget&Wuid=W20240526-094325
[Action] 4. Test: action5.ecl
[Pass] 4. Pass action5.ecl - W20240526-094327 (2 sec)
[Pass] 4. URL http://127.0.0.1:8010/?Widget=WUDetailsWidget&Wuid=W20240526-094327
[Action]
-------------------------------------------------
Result:
Passing: 4
Failure: 0
-------------------------------------------------
Log: /root/HPCCSystems-regression/log/hthor.24-05-26-09-43-22.log
-------------------------------------------------
Elapsed time: 11 sec (00:00:11)
-------------------------------------------------

[Action] Automatic Cleanup Routine

[Pass] 1. Workunit Wuid=W20240526-094322 deleted successfully.
[Pass] 2. Workunit Wuid=W20240526-094324 deleted successfully.
[Failure] 3. Failed to delete Wuid=W20240526-094325. URL: http://127.0.0.1:8010/?Widget=WUDetailsWidget&Wuid=W20240526-094325
Failed cannot open workunit Wuid=W20240526-094325.. Response status code: 200
[Pass] 4. Workunit Wuid=W20240526-094327 deleted successfully.
Suite destructor.

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/testing/regress/ecl/analyzers/corporate/tmp/README.html b/testing/regress/ecl/analyzers/corporate/tmp/README.html new file mode 100644 index 00000000000..a6445fa79ad --- /dev/null +++ b/testing/regress/ecl/analyzers/corporate/tmp/README.html @@ -0,0 +1,24 @@ + + + + + + HPCC Platform + + + + + + + + + + + + + +
Skip to content

This directory is for the function kbdumptree. The engine should create this but sometimes permission problems arrise.

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/tools/esdlcmd/README.html b/tools/esdlcmd/README.html new file mode 100644 index 00000000000..5ef399ae737 --- /dev/null +++ b/tools/esdlcmd/README.html @@ -0,0 +1,330 @@ + + + + + + esdl Utility | HPCC Platform + + + + + + + + + + + + + +
Skip to content

esdl Utility

The esdl utility tool aids with creating and managing ESDL-based and Dynamic ESDL services on an HPCC cluster. It consists of several different commands.

To generate output from an ESDL definition:

xml               Generate XML from ESDL definition.
+ecl               Generate ECL from ESDL definition.
+xsd               Generate XSD from ESDL definition.
+wsdl              Generate WSDL from ESDL definition.
+java              Generate Java code from ESDL definition.
+cpp               Generate C++ code from ESDL definition.
+monitor           Generate ECL code for result monitoring / differencing
+monitor-template  Generate a template for use with 'monitor' command
+

To manage ESDL and DESDL services:

publish               Publish ESDL Definition for ESP use.
+list-definitions      List all ESDL definitions.
+get-definition        Get ESDL definition.
+delete                Delete ESDL Definition.
+bind-service          Configure ESDL based service on target ESP (with existing ESP Binding).
+list-bindings         List all ESDL bindings.
+unbind-service        Remove ESDL based service binding on target ESP.
+bind-method           Configure method associated with existing ESDL binding.
+unbind-method         Remove method associated with existing ESDL binding.
+get-binding           Get ESDL binding.
+manifest              Build a service binding or bundle from a manifest file.
+bind-log-transform    Configure log transform associated with existing ESDL binding.
+unbind-log-transform  Remove log transform associated with existing ESDL binding.
+

The sections below cover the commands in more detail.

manifest

The manifest command creates an XML configuration file for an ESDL ESP from an input XML manifest file. The type of configuration output depends on the manifest file input and on command-line options.

Manifest File

A manifest file is an XML-formatted template combining elements in and outside of the manifest's urn:hpcc:esdl:manifest namespace. Recognized elements of this namespace control the tool while all other markup is copied to the output. The goal of using a manifest file with the tool is to make configuring and deploying services easier:

  1. The manifest file format abstracts some of the complexity of the actual configuration.
  2. By allowing you to include external files like ESDL Scripts and XSLTs into the ouput, you can store and maintain them separately in your repo.

The result of running the manifest tool on a manifest file is an XML artifact suitable for use with the ESDL ESP. Supported output includes:

  • binding: The output is an ESDL binding that may be published to dali.
  • bundle: The output is an ESDL bundle file that may be used to launch an ESP in application mode.

Example

A simplified example showing the format of a bundle manifest file:

xml
<em:Manifest xmlns:em="urn:hpcc:esdl:manifest">
+    <em:ServiceBinding esdlservice="WsFoobar" id="WsFoobar_desdl_binding" auth_feature="DEFERRED">
+        <Methods>
+            <em:Scripts>
+                <em:Include file="WsFoobar-request-prep.xml"/>
+                <em:Include file="WsFoobar-logging-prep.xml"/>
+            </em:Scripts>
+            <Method name="FoobarSearch" url="127.0.0.1:8888">
+                <em:Scripts>
+                    <em:Include file="FoobarSearch-scripts.xml"/>
+                </em:Scripts>
+            </Method>
+        </Methods>
+        <LoggingManager>
+            <LogAgent transformSource="local" name="main-logging">
+                <LogDataXPath>
+                    <LogInfo name="PreparedData" xsl="log-prep">
+                </LogDataXPath>
+                <XSL>
+                    <em:Transform name="log-prep">
+                        <em:Include file="log-prep.xslt">
+                    </em:Transform>
+                </XSL>
+            </LogAgent>
+        </LoggingManager>
+    </em:ServiceBinding>
+    <em:EsdlDefinition>
+        <em:Include file="WsFoobar.ecm"/>
+    </em:EsdlDefinition>
+</em:Manifest>

The tool is permissive and flexible, copying through most markup to the output. Recognized elements in the manifest namespace may be treated differently. They are only required in order to take advantage of the automated processing and simplified format of the manifest file. This example highlights the recommended usage of manifest elements to use the tool's capabilities. Although you could replace some of the elements below with verbatim bundle or binding output elements we won't cover that usage here.

  • <em:Manifest> is the required root element. By default the tool outputs a bundle, though you may explicitly override that on the command line or by providing an @outputType='binding' attribute.
  • <em:ServiceBinding> is valid for both bundle and binding output. It is necessary to enable recognition of <em:Scripts>, and <em:Transform> elements.
  • <em:EsdlDefinition> is relevant only for bundle output. It is necessary to enable element order preservation and recognition of <em:Include> as a descendant element.
  • <em:Include> causes external file contents to be inserted in place of the element. The processing of included files is context dependent; the parent of the <em:Include> element dictates how the file is handled. File inclusion facilitates code reuse in a configuration as code development environment.
  • <em:Scripts> and <em:Transform> trigger preservation of element order for all descendent elements and enable <em:Include> recognition.

XML element ordering is significant to proper ESDL script, XSLT, and ESXDL content processing. The platform's IPropertyTree implementation, used when loading an artifact, does not preserve order. Output configuration files must embed order-sensitive content as text as opposed to XML markup. The tool allows configuration authors to create and maintain files as XML markup, which is easy to read. It then automates the conversion of the XML markup into the embedded text required by an ESP.

Syntax

These elements may create artifact content, change the tool's behavior, or both. When used as intended, none of these elements will appear in the generated output:

Manifest

Required root element of all manifest files that create output:

  • bundle output is created by default. It can be made explicit by setting either/or:
    • command line option --output-type to bundle
    • manifest property @outputType to bundle.
  • binding output is created when either/or:
    • command line option --output-type is binding
    • manifest property @outputType is binding.
AttributeRequired?ValueDescription
@outputTypeNstringA hint informing the tool which type of output to generate. The command line option --output-type may supersede this value to produce a different output.
A bundle manifest is a superset of a binding manifest and may logically be used to create either output type. A binding manifest, as a subset of a bundle, cannot be used to create a valid bundle.
@xmlns[:prefix]YstringThe manifest namespace urn:hpcc:esdl:manifest must be declared. The default namespace prefix should not be used unless all other markup is fully qualified.

ServiceBinding

Recommended child of Manifest that creates ESDL binding content and applies ESDL binding-specific logic to descendent content:

  • A Binding output element is always created containing attributes of Manifest as required. See the sections below for details of the attributes referenced by the tool and how they're output.
  • A child of Binding named Definition is created with attributes @id and @esdlservice.
  • Recognition of <em:Scripts> elements is enabled.
  • Recognition of <em:Transform> elements is enabled.

While possible to omit the <em:ServiceBinding> element and instead embed a complete Binding tree in the manifest, it is discouraged because you lose the benefit of the processing described above.

There are three categories of attributes that can be defined on the <em:ServiceBinding> element:

  1. Standard attributes needed for setup
  2. Service-specific or binding-specific attributes
  3. Auxillary attributes recommended for read-only reference
Standard Attributes

First are the standard attributes used to setup and define the binding:

AttributeRequired?ValueUsage
@esdlserviceYesstring- Name of the ESDL service to which the binding is bound. Output on the Binding/Definition element.
- Also used to generate a value for Definition/@id for bundle type output.

Note that Definition/@id is not output by the tool for binding output as it would need to match the ESDLDefinitionId passed on command line esdl bind-service call, which may differ between environments.

Service-Specific Attributes

Second are binding-specific or service-specific attributes. This is an open-ended category where most future attributes will belong. Any attribute not in the other two categories is included here and output on the Binding/Definition element:

AttributeRequired?ValueUsage
@auth_featureNostringUsed declare authorization settings if they aren't present in the ESDL Definition, or override them if they are. Additional documentation on this attribute is forthcoming.
@returnSchemaLocationOnOKNoBooleanWhen true, a successful SOAP response (non SOAP-Fault) will include the schema location property. False by default.
@namespaceNostringString specifying the namespace for all methods in the binding. May contain variables that are replaced with their values by the ESP during runtime:
- ${service} : lowercase service name
- ${esdl-service} : service name, possibly mixed case
- ${method} : lowercase method name
- ${esdl-method} : method name, possibly mixed case
- ${optionals} : comma-delimited list of all optional URL parameters included in the method request, enclosed in parentheses
- ${version} : client version number
Auxillary Attributes

Finally are auxillary attributes. These should be thought of as read-only or for reference, and it is not recommended that you set these in the manifest. They are set by the system when publishing to dali using esdl bind-service, and they are present on binding configurations retrieved from the dali. If you set these attributes in the manifest, the values will be overwritten when running esdl bind-service. Alternately, if you run as an esdl application, these values aren't set by default and don't have a material effect on the binding, but they may appear in the trace log.

AttributeRequired?ValueUsage
@createdNostringTimestamp of binding creation
@espbindingNostringSet to match the id. Otherwise the value is unset and unused.
@espprocessNostringName of the ESP process this binding is running on
@idNostringRuntime name of the binding. When publishing to dali the value is [ESP Process].[port].[ESDL Service]. When not present in the manifest a default value is generated of the form [@esdlservice]_desdl_binding
@portNostringPort on the ESP Process listening for connections to the binding.
@publishedByNostringUserid of the person publishing the binding

EsdlDefinition

Recommended child of <em:Manifest> that enables ESDL definition-specific logic in the tool:

  • A Definitions element is output. All content is enclosed in a CDATA section.
  • The manifest <em:Include> element is recognized to import ESDL definitions:
    • An included .ecm file is transformed into XML.
    • An included .xml file is imported as-is.
AttributeRequired?ValueUsage
N/A

The element is not required since it is possible to embed a complete Definitions element hierarchy in the manifest.

Include

Optional element that imports the contents of another file into the output in place of itself. The outcome of the import depends on the context in which this element is used. See EsdlDefinition, Scripts, and Transform for more information.

AttributeRequired?ValueUsage
@fileYesfile pathFull or partial path to an external file to be imported. If a partial path is outside of the tool's working directory, the tool's command line must specify the appropriate root directory using either -I or --include-path.

Any XSLTs or ESDL Scripts written inline in a manifest file will have XML escaping applied where required to generate valid XML. If an XSLT contains any text content or markup that needs to be preserved as-is (no XML escaping applied) then be sure to use an <em:Include> operation. Included files are inserted into the output as-is, with the exception of encoding nested CDATA markup. If the included file will be inside a CDATA section on output, then any CDATA end markup in the file will be encoded as ]]]]><![CDATA[> to prevent nested CDATA sections or a prematurely ending a CDATA section.

For details on using XSLT to generate unescaped output, see this section of the specification: https://www.w3.org/TR/1999/REC-xslt-19991116#disable-output-escaping

Scripts

Optional repeatable element appearing within an <em:ServiceBinding> element that processes child elements and creates output expected for an ESDL binding:

  • The <em:Scripts> element is replaced on output with <Scripts>. Then all content is enclosed in a CDATA section after wrapping it with a new Scripts element. That new <Scripts> element contains namespaces declared by the input <em:Scripts> element. The input <em:Scripts foo="..." xmlns:bar="..."><!-- content --></em:Scripts> becomes <Scripts><![CDATA[<Scripts xmlns:bar="..."><!-- content --></Scripts>]]></Scripts>.
  • The manifest <em:Include> element is recognized to import scripts from external files. The entire file, minus leading and trailing whitespace, is imported. Refrain from including files that contain an XML declaration.

Transform

Optional repeatable element appearing within an <em:ServiceBinding> element that processes child elements and creates output expected for an ESDL binding:

  • All content is enclosed in a CDATA section. The input <em:Transform> <!-- content --> </em:Transform> becomes <Transform><![CDATA[<!-- content -->]]></Transform>.
  • The manifest <em:Include> element is recognized to import transforms from external files. The entire file, minus leading and trailing whitespace, is imported. Refrain from including files that contain an XML declaration.

Usage

Usage:
+
+esdl manifest <manifest-file> [options]
+
+Options:
+    -I | --include-path <path>
+                        Search path for external files included in the manifest.
+                        Use once for each path.
+    --outfile <filename>
+                        Path and name of the output file
+    --output-type <type>
+                        When specified this option overrides the value supplied
+                        in the manifest attribute Manifest/@outputType.
+                        Allowed values are 'binding' or 'bundle'.
+                        When not specified in either location the default is
+                        'bundle'
+    --help              Display usage information for the given command
+    -v,--verbose        Output additional tracing information
+    -tcat,--trace-category <flags>
+                        Control which debug messages are output; a case-insensitive
+                        comma-delimited combination of:
+                            dev: all output for the developer audience
+                            admin: all output for the operator audience
+                            user: all output for the user audience
+                            err: all error output
+                            warn: all warning output
+                            prog: all progress output
+                            info: all info output
+                        Errors and warnings are enabled by default if not verbose,
+                        and all are enabled when verbose. Use an empty <flags> value
+                        to disable all.
+

Output

The esdl manifest command reads the manifest, processes statements in the urn:hpcc:esdl:manifest namespace and generates an output XML file formatted to the requirements of the ESDL ESP. This includes wrapping included content in CDATA sections to ensure element order is maintained and replacing urn:hpcc:esdl:manifest elements as required.

An example output of each type -bundle and binding- is shown below. The examples use the sample manifest above as input plus these included files:

WsFoobar-request-prep.xml

xml
<es:BackendRequest name="request-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+    <es:set-value target="RequestValue" value="'foobar'"/>
+</es:BackendRequest>

WsFoobar-logging-prep.xml

xml
<es:PreLogging name="logging-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+    <es:set-value target="LogValue" value="23"/>
+</es:PreLogging>

FoobarSearch-scripts.xml

xml
<Scripts>
+    <es:BackendRequest name="search-request-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+        <es:if test="RequestOption>1">
+            <es:set-value target="HiddenOption" value="true()"/>
+        </es:if>
+    </es:BackendRequest>
+
+    <es:PreLogging name="search-logging-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+        <es:if test="RequestOption=1">
+            <es:set-value target="ProductPrice" value="10"/>
+        </es:if>
+    </es:PreLogging>
+</Scripts>

log-prep.xslt

xml
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+    <xsl:output method="xml" omit-xml-declaration="yes"/>
+    <xsl:variable name="logContent" select="/UpdateLogRequest/LogContent"/>
+    <xsl:variable name="transactionId" select="$logContent/UserContext/Context/Row/Common/TransactionId"/>
+    <xsl:template match="/">
+        <Result>
+        <Dataset name='special-data'>
+            <Row>
+            <Records>
+                <Rec>
+                <transaction_id><xsl:value-of select="$transactionId"/></transaction_id>
+                <request_data>
+                    <xsl:text disable-output-escaping="yes">&amp;lt;![CDATA[COMPRESS('</xsl:text>
+                    <xsl:copy-of select="$logContent/UserContent/Context"/>
+                    <xsl:text disable-output-escaping="yes">')]]&amp;gt;</xsl:text>
+                </request_data>
+                <request_format>SPECIAL</request_format>
+                <type>23</type>
+                </Rec>
+            </Records>
+            </Row>
+        </Dataset>
+        </Result>
+    </xsl:template>
+</xsl:stylesheet>

WsFoobar.ecm

ESPrequest FoobarSearchRequest
+{
+    int RequestOption;
+    string RequestName;
+    [optional("hidden")] bool HiddenOption;
+};
+
+ESPresponse FoobarSearchResponse
+{
+    int FoundCount;
+    string FoundAddress;
+};
+
+ESPservice [
+    auth_feature("DEFERRED"),
+    version("1"),
+    default_client_version("1"),
+] WsFoobar
+{
+    ESPmethod FoobarSearch(FoobarSearchRequest, FoobarSearchResponse);
+};
+

Bundle

The bundle is suitable to configure a service on an ESP launched in esdl application mode.

xml
  <EsdlBundle>
+    <Binding id="WsFoobar_desdl_binding">
+      <Definition esdlservice="WsFoobar" id="WsFoobar.1">
+        <Methods>
+          <Scripts>
+            <![CDATA[
+              <Scripts>
+                <es:BackendRequest name="request-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+                    <es:set-value target="RequestValue" value="'foobar'"/>
+                </es:BackendRequest>
+                <es:PreLogging name="logging-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+                    <es:set-value target="LogValue" value="23"/>
+                </es:PreLogging>
+              </Scripts>
+            ]]>
+          </Scripts>
+          <Method name="FoobarSearch" url="127.0.0.1:8888">
+            <Scripts>
+              <![CDATA[
+                <Scripts>
+                  <Scripts>
+                      <es:BackendRequest name="search-request-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+                          <es:if test="RequestOption>1">
+                              <es:set-value target="HiddenOption" value="true()"/>
+                          </es:if>
+                      </es:BackendRequest>
+
+                      <es:PreLogging name="search-logging-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+                          <es:if test="RequestOption=1">
+                              <es:set-value target="ProductPrice" value="10"/>
+                          </es:if>
+                      </es:PreLogging>
+                  </Scripts>
+                </Scripts>
+              ]]>
+            </Scripts>
+          </Method>
+        </Methods>
+        <LoggingManager>
+          <LogAgent transformSource="local" name="main-logging">
+            <LogDataXPath>
+              <LogInfo name="PreparedData" xsl="log-prep"/>
+            </LogDataXPath>
+            <XSL>
+              <Transform name="log-prep">
+                <![CDATA[
+                  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+                      <xsl:output method="xml" omit-xml-declaration="yes"/>
+                      <xsl:variable name="logContent" select="/UpdateLogRequest/LogContent"/>
+                      <xsl:variable name="transactionId" select="$logContent/UserContext/Context/Row/Common/TransactionId"/>
+                      <xsl:template match="/">
+                          <Result>
+                          <Dataset name='special-data'>
+                              <Row>
+                              <Records>
+                                  <Rec>
+                                  <transaction_id><xsl:value-of select="$transactionId"/></transaction_id>
+                                  <request_data>
+                                      <xsl:text disable-output-escaping="yes">&amp;lt;![CDATA[COMPRESS('</xsl:text>
+                                      <xsl:copy-of select="$logContent/UserContent/Context"/>
+                                      <xsl:text disable-output-escaping="yes">')]]&amp;gt;</xsl:text>
+                                  </request_data>
+                                  <request_format>SPECIAL</request_format>
+                                  <type>23</type>
+                                  </Rec>
+                              </Records>
+                              </Row>
+                          </Dataset>
+                          </Result>
+                      </xsl:template>
+                  </xsl:stylesheet>
+                ]]>
+              </Transform>
+            </XSL>
+          </LogAgent>
+        </LoggingManager>
+      </Definition>
+    </Binding>
+    <Definitions>
+      <![CDATA[
+        <esxdl name="WsFoobar"><EsdlRequest name="FoobarSearchRequest"><EsdlElement  type="int" name="RequestOption"/><EsdlElement  type="string" name="RequestName"/><EsdlElement  optional="hidden" type="bool" name="HiddenOption"/></EsdlRequest>
+        <EsdlResponse name="FoobarSearchResponse"><EsdlElement  type="int" name="FoundCount"/><EsdlElement  type="string" name="FoundAddress"/></EsdlResponse>
+        <EsdlRequest name="WsFoobarPingRequest"></EsdlRequest>
+        <EsdlResponse name="WsFoobarPingResponse"></EsdlResponse>
+        <EsdlService version="1" auth_feature="DEFERRED" name="WsFoobar" default_client_version="1"><EsdlMethod response_type="FoobarSearchResponse" request_type="FoobarSearchRequest" name="FoobarSearch"/><EsdlMethod response_type="WsFoobarPingResponse" auth_feature="none" request_type="WsFoobarPingRequest" name="Ping"/></EsdlService>
+        </esxdl>
+      ]]>
+    </Definitions>
+  </EsdlBundle>

Binding

The binding can be used to configure a service for an ESP using a dali.

xml
<Binding id="WsFoobar_desdl_binding">
+  <Definition esdlservice="WsFoobar" id="WsFoobar.1">
+    <Methods>
+      <Scripts>
+        <![CDATA[
+          <Scripts>
+            <es:BackendRequest name="request-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+                <es:set-value target="RequestValue" value="'foobar'"/>
+            </es:BackendRequest>
+            <es:PreLogging name="logging-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+                <es:set-value target="LogValue" value="23"/>
+            </es:PreLogging>
+          </Scripts>
+        ]]>
+      </Scripts>
+      <Method name="FoobarSearch" url="127.0.0.1:8888">
+        <Scripts>
+          <![CDATA[
+            <Scripts>
+              <Scripts>
+                  <es:BackendRequest name="search-request-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+                      <es:if test="RequestOption>1">
+                          <es:set-value target="HiddenOption" value="true()"/>
+                      </es:if>
+                  </es:BackendRequest>
+
+                  <es:PreLogging name="search-logging-prep" target="soap:Body/{$query}" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:es="urn:hpcc:esdl:script">
+                      <es:if test="RequestOption=1">
+                          <es:set-value target="ProductPrice" value="10"/>
+                      </es:if>
+                  </es:PreLogging>
+              </Scripts>
+            </Scripts>
+          ]]>
+        </Scripts>
+      </Method>
+    </Methods>
+    <LoggingManager>
+      <LogAgent transformSource="local" name="main-logging">
+        <LogDataXPath>
+          <LogInfo name="PreparedData" xsl="log-prep"/>
+        </LogDataXPath>
+        <XSL>
+          <Transform name="log-prep">
+            <![CDATA[
+              <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+                  <xsl:output method="xml" omit-xml-declaration="yes"/>
+                  <xsl:variable name="logContent" select="/UpdateLogRequest/LogContent"/>
+                  <xsl:variable name="transactionId" select="$logContent/UserContext/Context/Row/Common/TransactionId"/>
+                  <xsl:template match="/">
+                      <Result>
+                      <Dataset name='special-data'>
+                          <Row>
+                          <Records>
+                              <Rec>
+                              <transaction_id><xsl:value-of select="$transactionId"/></transaction_id>
+                              <request_data>
+                                  <xsl:text disable-output-escaping="yes">&amp;lt;![CDATA[COMPRESS('</xsl:text>
+                                  <xsl:copy-of select="$logContent/UserContent/Context"/>
+                                  <xsl:text disable-output-escaping="yes">')]]&amp;gt;</xsl:text>
+                              </request_data>
+                              <request_format>SPECIAL</request_format>
+                              <type>23</type>
+                              </Rec>
+                          </Records>
+                          </Row>
+                      </Dataset>
+                      </Result>
+                  </xsl:template>
+              </xsl:stylesheet>
+            ]]>
+          </Transform>
+        </XSL>
+      </LogAgent>
+    </LoggingManager>
+  </Definition>
+</Binding>

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file diff --git a/tools/tagging/README.html b/tools/tagging/README.html new file mode 100644 index 00000000000..322df42f6e3 --- /dev/null +++ b/tools/tagging/README.html @@ -0,0 +1,36 @@ + + + + + + Tagging new versions | HPCC Platform + + + + + + + + + + + + + +
Skip to content

Tagging new versions

General

The file tools/git/aliases.sh contains various git aliases which are useful when using git, and may be used by the merge scripts.

The file env.sh.example contains some example environment variable settings. Copy that locally to env.sh and modify it to match your local setup.

Before running any of the other scripts, process the contents of that file as a source file

. env.sh

to initialize the common environment variables.

Pre-requisites

The following tools are required:

  • git
  • helm

The following repositories should be checked out in a directory reserved for merging and tagging (default for scripts is ~/git):

git clone git@github.com:hpcc-systems/eclide.git
+git clone git@github.com:hpcc-systems/hpcc4j.git HPCC-JAPIs
+git clone git@github.com:hpcc-systems/Spark-HPCC.git
+git clone git@github.com:hpcc-systems/LN.git ln
+git clone git@github.com:hpcc-systems/HPCC-Platform.git hpcc
+git clone git@github.com:hpcc-systems/helm-chart.git

The following are required for builds prior to 8.12.x

git clone git@github.com:hpcc-systems/nagios-monitoring.git
+git clone git@github.com:hpcc-systems/ganglia-monitoring.git

The files git-fixversion and git-unupmerge can copied so they are on your default path, and then they will be available as git commands.

Tagging new versions

The following process should be followed when tagging a new set of versions.

  1. Upmerge all changes between candidate branches for the different versions

You can set the all environment variable to a subset of the projects (e.g. export all=hpcc) if there are no changes in the other repositories. The only effect for projects that are upmerged with no changes will be that they gain an empty merge transaction. If multiple people are merging PRs to different repositories it may be safer to upmerge all projects.

For example:

./upmerge A.a.x candidate-A.b.x
+./upmerge A.b.x candidate-A.c.x
+./upmerge A.b.x candidate-B.0.x
+./upmerge B.0.x master
  1. Create new point-release candidate branches:
./gorc.sh A.a.x
+./gorc.sh A.b.x
+./gorc.sh A.c.x

Taking a build gold:

Go gold with each of the explicit versions

./gogold.sh 7.8.76
+./gogold.sh 7.10.50

If you have merged changes onto a point-release branch you would normally create a new rc before going gold. If the change was trivial (e.g. removing an unwanted file) then you can use the --ignore option to skip that step.

Creating a new rc for an existing point release:

This normally happens after cherry-picking a late fix for a particular version, which has already been merged into the .x candidate branch.

./gorc.sh A.a.<n>

Create a new minor/major branch

A new minor branch is created from the current master...

./gominor.sh

Released under the Apache-2.0 License.

+ + + + \ No newline at end of file

ZW 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�zBE;A6`RoYnYVbGchb9$^iIU2 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 literal 0 HcmV?d00001 diff --git a/assets/inter-roman-vietnamese.paY3CzEB.woff2 b/assets/inter-roman-vietnamese.paY3CzEB.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..5a9f9cb9ca0cd78b6ea2f3e5c9d2838dc8895598 GIT binary patch literal 8492 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 a!Vc~9U7SGB8$q3Ii#9CnAx#qS0RRAsKuhfa literal 0 HcmV?d00001 diff --git a/assets/repository-tag-tab.0n7rea9Z.png b/assets/repository-tag-tab.0n7rea9Z.png new file mode 100644 index 0000000000000000000000000000000000000000..16bdb37acbc939a22f3df1881fd09636b05726a0 GIT binary patch literal 72083 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 ~SsXN57R?zB1 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 literal 0 HcmV?d00001 diff --git a/assets/style.BC59MYRm.css b/assets/style.BC59MYRm.css new file mode 100644 index 00000000000..722ff731749 --- /dev/null +++ b/assets/style.BC59MYRm.css @@ -0,0 +1 @@ +@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/HPCC-Platform/assets/inter-roman-cyrillic.jIZ9REo5.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/HPCC-Platform/assets/inter-roman-cyrillic-ext.8T9wMG5w.woff2) format("woff2");unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/HPCC-Platform/assets/inter-roman-greek.Cb5wWeGA.woff2) format("woff2");unicode-range:U+0370-03FF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/HPCC-Platform/assets/inter-roman-greek-ext.9JiNzaSO.woff2) format("woff2");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/HPCC-Platform/assets/inter-roman-latin.bvIUbFQP.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/HPCC-Platform/assets/inter-roman-latin-ext.GZWE-KO4.woff2) format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/HPCC-Platform/assets/inter-roman-vietnamese.paY3CzEB.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/HPCC-Platform/assets/inter-italic-cyrillic.-nLMcIwj.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/HPCC-Platform/assets/inter-italic-cyrillic-ext.OVycGSDq.woff2) format("woff2");unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/HPCC-Platform/assets/inter-italic-greek.PSfer2Kc.woff2) format("woff2");unicode-range:U+0370-03FF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/HPCC-Platform/assets/inter-italic-greek-ext.hznxWNZO.woff2) format("woff2");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/HPCC-Platform/assets/inter-italic-latin.27E69YJn.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/HPCC-Platform/assets/inter-italic-latin-ext.RnFly65-.woff2) format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/HPCC-Platform/assets/inter-italic-vietnamese.xzQHe1q1.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:Chinese Quotes;src:local("PingFang SC Regular"),local("PingFang SC"),local("SimHei"),local("Source Han Sans SC");unicode-range:U+2018,U+2019,U+201C,U+201D}:root{--vp-c-white: #ffffff;--vp-c-black: #000000;--vp-c-neutral: var(--vp-c-black);--vp-c-neutral-inverse: var(--vp-c-white)}.dark{--vp-c-neutral: var(--vp-c-white);--vp-c-neutral-inverse: var(--vp-c-black)}:root{--vp-c-gray-1: #dddde3;--vp-c-gray-2: #e4e4e9;--vp-c-gray-3: #ebebef;--vp-c-gray-soft: rgba(142, 150, 170, .14);--vp-c-indigo-1: #3451b2;--vp-c-indigo-2: #3a5ccc;--vp-c-indigo-3: #5672cd;--vp-c-indigo-soft: rgba(100, 108, 255, .14);--vp-c-purple-1: #6f42c1;--vp-c-purple-2: #7e4cc9;--vp-c-purple-3: #8e5cd9;--vp-c-purple-soft: rgba(159, 122, 234, .14);--vp-c-green-1: #18794e;--vp-c-green-2: #299764;--vp-c-green-3: #30a46c;--vp-c-green-soft: rgba(16, 185, 129, .14);--vp-c-yellow-1: #915930;--vp-c-yellow-2: #946300;--vp-c-yellow-3: #9f6a00;--vp-c-yellow-soft: rgba(234, 179, 8, .14);--vp-c-red-1: #b8272c;--vp-c-red-2: #d5393e;--vp-c-red-3: #e0575b;--vp-c-red-soft: rgba(244, 63, 94, .14);--vp-c-sponsor: #db2777}.dark{--vp-c-gray-1: #515c67;--vp-c-gray-2: #414853;--vp-c-gray-3: #32363f;--vp-c-gray-soft: rgba(101, 117, 133, .16);--vp-c-indigo-1: #a8b1ff;--vp-c-indigo-2: #5c73e7;--vp-c-indigo-3: #3e63dd;--vp-c-indigo-soft: rgba(100, 108, 255, .16);--vp-c-purple-1: #c8abfa;--vp-c-purple-2: #a879e6;--vp-c-purple-3: #8e5cd9;--vp-c-purple-soft: rgba(159, 122, 234, .16);--vp-c-green-1: #3dd68c;--vp-c-green-2: #30a46c;--vp-c-green-3: #298459;--vp-c-green-soft: rgba(16, 185, 129, .16);--vp-c-yellow-1: #f9b44e;--vp-c-yellow-2: #da8b17;--vp-c-yellow-3: #a46a0a;--vp-c-yellow-soft: rgba(234, 179, 8, .16);--vp-c-red-1: #f66f81;--vp-c-red-2: #f14158;--vp-c-red-3: #b62a3c;--vp-c-red-soft: rgba(244, 63, 94, .16)}:root{--vp-c-bg: #ffffff;--vp-c-bg-alt: #f6f6f7;--vp-c-bg-elv: #ffffff;--vp-c-bg-soft: #f6f6f7}.dark{--vp-c-bg: #1b1b1f;--vp-c-bg-alt: #161618;--vp-c-bg-elv: #202127;--vp-c-bg-soft: #202127}:root{--vp-c-border: #c2c2c4;--vp-c-divider: #e2e2e3;--vp-c-gutter: #e2e2e3}.dark{--vp-c-border: #3c3f44;--vp-c-divider: #2e2e32;--vp-c-gutter: #000000}:root{--vp-c-text-1: rgba(60, 60, 67);--vp-c-text-2: rgba(60, 60, 67, .78);--vp-c-text-3: rgba(60, 60, 67, .56)}.dark{--vp-c-text-1: rgba(255, 255, 245, .86);--vp-c-text-2: rgba(235, 235, 245, .6);--vp-c-text-3: rgba(235, 235, 245, .38)}:root{--vp-c-default-1: var(--vp-c-gray-1);--vp-c-default-2: var(--vp-c-gray-2);--vp-c-default-3: var(--vp-c-gray-3);--vp-c-default-soft: var(--vp-c-gray-soft);--vp-c-brand-1: var(--vp-c-indigo-1);--vp-c-brand-2: var(--vp-c-indigo-2);--vp-c-brand-3: var(--vp-c-indigo-3);--vp-c-brand-soft: var(--vp-c-indigo-soft);--vp-c-brand: var(--vp-c-brand-1);--vp-c-tip-1: var(--vp-c-brand-1);--vp-c-tip-2: var(--vp-c-brand-2);--vp-c-tip-3: var(--vp-c-brand-3);--vp-c-tip-soft: var(--vp-c-brand-soft);--vp-c-note-1: var(--vp-c-brand-1);--vp-c-note-2: var(--vp-c-brand-2);--vp-c-note-3: var(--vp-c-brand-3);--vp-c-note-soft: var(--vp-c-brand-soft);--vp-c-success-1: var(--vp-c-green-1);--vp-c-success-2: var(--vp-c-green-2);--vp-c-success-3: var(--vp-c-green-3);--vp-c-success-soft: var(--vp-c-green-soft);--vp-c-important-1: var(--vp-c-purple-1);--vp-c-important-2: var(--vp-c-purple-2);--vp-c-important-3: var(--vp-c-purple-3);--vp-c-important-soft: var(--vp-c-purple-soft);--vp-c-warning-1: var(--vp-c-yellow-1);--vp-c-warning-2: var(--vp-c-yellow-2);--vp-c-warning-3: var(--vp-c-yellow-3);--vp-c-warning-soft: var(--vp-c-yellow-soft);--vp-c-danger-1: var(--vp-c-red-1);--vp-c-danger-2: var(--vp-c-red-2);--vp-c-danger-3: var(--vp-c-red-3);--vp-c-danger-soft: var(--vp-c-red-soft);--vp-c-caution-1: var(--vp-c-red-1);--vp-c-caution-2: var(--vp-c-red-2);--vp-c-caution-3: var(--vp-c-red-3);--vp-c-caution-soft: var(--vp-c-red-soft)}:root{--vp-font-family-base: "Chinese Quotes", "Inter var", "Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--vp-font-family-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace}:root{--vp-shadow-1: 0 1px 2px rgba(0, 0, 0, .04), 0 1px 2px rgba(0, 0, 0, .06);--vp-shadow-2: 0 3px 12px rgba(0, 0, 0, .07), 0 1px 4px rgba(0, 0, 0, .07);--vp-shadow-3: 0 12px 32px rgba(0, 0, 0, .1), 0 2px 6px rgba(0, 0, 0, .08);--vp-shadow-4: 0 14px 44px rgba(0, 0, 0, .12), 0 3px 9px rgba(0, 0, 0, .12);--vp-shadow-5: 0 18px 56px rgba(0, 0, 0, .16), 0 4px 12px rgba(0, 0, 0, .16)}:root{--vp-z-index-footer: 10;--vp-z-index-local-nav: 20;--vp-z-index-nav: 30;--vp-z-index-layout-top: 40;--vp-z-index-backdrop: 50;--vp-z-index-sidebar: 60}@media (min-width: 960px){:root{--vp-z-index-sidebar: 25}}:root{--vp-icon-copy: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2'/%3E%3C/svg%3E");--vp-icon-copied: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2m-6 9 2 2 4-4'/%3E%3C/svg%3E")}:root{--vp-layout-max-width: 1440px}:root{--vp-header-anchor-symbol: "#"}:root{--vp-code-line-height: 1.7;--vp-code-font-size: .875em;--vp-code-color: var(--vp-c-brand-1);--vp-code-link-color: var(--vp-c-brand-1);--vp-code-link-hover-color: var(--vp-c-brand-2);--vp-code-bg: var(--vp-c-default-soft);--vp-code-block-color: var(--vp-c-text-2);--vp-code-block-bg: var(--vp-c-bg-alt);--vp-code-block-divider-color: var(--vp-c-gutter);--vp-code-lang-color: var(--vp-c-text-3);--vp-code-line-highlight-color: var(--vp-c-default-soft);--vp-code-line-number-color: var(--vp-c-text-3);--vp-code-line-diff-add-color: var(--vp-c-success-soft);--vp-code-line-diff-add-symbol-color: var(--vp-c-success-1);--vp-code-line-diff-remove-color: var(--vp-c-danger-soft);--vp-code-line-diff-remove-symbol-color: var(--vp-c-danger-1);--vp-code-line-warning-color: var(--vp-c-warning-soft);--vp-code-line-error-color: var(--vp-c-danger-soft);--vp-code-copy-code-border-color: var(--vp-c-divider);--vp-code-copy-code-bg: var(--vp-c-bg-soft);--vp-code-copy-code-hover-border-color: var(--vp-c-divider);--vp-code-copy-code-hover-bg: var(--vp-c-bg);--vp-code-copy-code-active-text: var(--vp-c-text-2);--vp-code-copy-copied-text-content: "Copied";--vp-code-tab-divider: var(--vp-code-block-divider-color);--vp-code-tab-text-color: var(--vp-c-text-2);--vp-code-tab-bg: var(--vp-code-block-bg);--vp-code-tab-hover-text-color: var(--vp-c-text-1);--vp-code-tab-active-text-color: var(--vp-c-text-1);--vp-code-tab-active-bar-color: var(--vp-c-brand-1)}:root{--vp-button-brand-border: transparent;--vp-button-brand-text: var(--vp-c-white);--vp-button-brand-bg: var(--vp-c-brand-3);--vp-button-brand-hover-border: transparent;--vp-button-brand-hover-text: var(--vp-c-white);--vp-button-brand-hover-bg: var(--vp-c-brand-2);--vp-button-brand-active-border: transparent;--vp-button-brand-active-text: var(--vp-c-white);--vp-button-brand-active-bg: var(--vp-c-brand-1);--vp-button-alt-border: transparent;--vp-button-alt-text: var(--vp-c-text-1);--vp-button-alt-bg: var(--vp-c-default-3);--vp-button-alt-hover-border: transparent;--vp-button-alt-hover-text: var(--vp-c-text-1);--vp-button-alt-hover-bg: var(--vp-c-default-2);--vp-button-alt-active-border: transparent;--vp-button-alt-active-text: var(--vp-c-text-1);--vp-button-alt-active-bg: var(--vp-c-default-1);--vp-button-sponsor-border: var(--vp-c-text-2);--vp-button-sponsor-text: var(--vp-c-text-2);--vp-button-sponsor-bg: transparent;--vp-button-sponsor-hover-border: var(--vp-c-sponsor);--vp-button-sponsor-hover-text: var(--vp-c-sponsor);--vp-button-sponsor-hover-bg: transparent;--vp-button-sponsor-active-border: var(--vp-c-sponsor);--vp-button-sponsor-active-text: var(--vp-c-sponsor);--vp-button-sponsor-active-bg: transparent}:root{--vp-custom-block-font-size: 14px;--vp-custom-block-code-font-size: 13px;--vp-custom-block-info-border: transparent;--vp-custom-block-info-text: var(--vp-c-text-1);--vp-custom-block-info-bg: var(--vp-c-default-soft);--vp-custom-block-info-code-bg: var(--vp-c-default-soft);--vp-custom-block-note-border: transparent;--vp-custom-block-note-text: var(--vp-c-text-1);--vp-custom-block-note-bg: var(--vp-c-default-soft);--vp-custom-block-note-code-bg: var(--vp-c-default-soft);--vp-custom-block-tip-border: transparent;--vp-custom-block-tip-text: var(--vp-c-text-1);--vp-custom-block-tip-bg: var(--vp-c-tip-soft);--vp-custom-block-tip-code-bg: var(--vp-c-tip-soft);--vp-custom-block-important-border: transparent;--vp-custom-block-important-text: var(--vp-c-text-1);--vp-custom-block-important-bg: var(--vp-c-important-soft);--vp-custom-block-important-code-bg: var(--vp-c-important-soft);--vp-custom-block-warning-border: transparent;--vp-custom-block-warning-text: var(--vp-c-text-1);--vp-custom-block-warning-bg: var(--vp-c-warning-soft);--vp-custom-block-warning-code-bg: var(--vp-c-warning-soft);--vp-custom-block-danger-border: transparent;--vp-custom-block-danger-text: var(--vp-c-text-1);--vp-custom-block-danger-bg: var(--vp-c-danger-soft);--vp-custom-block-danger-code-bg: var(--vp-c-danger-soft);--vp-custom-block-caution-border: transparent;--vp-custom-block-caution-text: var(--vp-c-text-1);--vp-custom-block-caution-bg: var(--vp-c-caution-soft);--vp-custom-block-caution-code-bg: var(--vp-c-caution-soft);--vp-custom-block-details-border: var(--vp-custom-block-info-border);--vp-custom-block-details-text: var(--vp-custom-block-info-text);--vp-custom-block-details-bg: var(--vp-custom-block-info-bg);--vp-custom-block-details-code-bg: var(--vp-custom-block-info-code-bg)}:root{--vp-input-border-color: var(--vp-c-border);--vp-input-bg-color: var(--vp-c-bg-alt);--vp-input-switch-bg-color: var(--vp-c-default-soft)}:root{--vp-nav-height: 64px;--vp-nav-bg-color: var(--vp-c-bg);--vp-nav-screen-bg-color: var(--vp-c-bg);--vp-nav-logo-height: 24px}.hide-nav{--vp-nav-height: 0px}.hide-nav .VPSidebar{--vp-nav-height: 22px}:root{--vp-local-nav-bg-color: var(--vp-c-bg)}:root{--vp-sidebar-width: 272px;--vp-sidebar-bg-color: var(--vp-c-bg-alt)}:root{--vp-backdrop-bg-color: rgba(0, 0, 0, .6)}:root{--vp-home-hero-name-color: var(--vp-c-brand-1);--vp-home-hero-name-background: transparent;--vp-home-hero-image-background-image: none;--vp-home-hero-image-filter: none}:root{--vp-badge-info-border: transparent;--vp-badge-info-text: var(--vp-c-text-2);--vp-badge-info-bg: var(--vp-c-default-soft);--vp-badge-tip-border: transparent;--vp-badge-tip-text: var(--vp-c-tip-1);--vp-badge-tip-bg: var(--vp-c-tip-soft);--vp-badge-warning-border: transparent;--vp-badge-warning-text: var(--vp-c-warning-1);--vp-badge-warning-bg: var(--vp-c-warning-soft);--vp-badge-danger-border: transparent;--vp-badge-danger-text: var(--vp-c-danger-1);--vp-badge-danger-bg: var(--vp-c-danger-soft)}:root{--vp-carbon-ads-text-color: var(--vp-c-text-1);--vp-carbon-ads-poweredby-color: var(--vp-c-text-2);--vp-carbon-ads-bg-color: var(--vp-c-bg-soft);--vp-carbon-ads-hover-text-color: var(--vp-c-brand-1);--vp-carbon-ads-hover-poweredby-color: var(--vp-c-text-1)}:root{--vp-local-search-bg: var(--vp-c-bg);--vp-local-search-result-bg: var(--vp-c-bg);--vp-local-search-result-border: var(--vp-c-divider);--vp-local-search-result-selected-bg: var(--vp-c-bg);--vp-local-search-result-selected-border: var(--vp-c-brand-1);--vp-local-search-highlight-bg: var(--vp-c-brand-1);--vp-local-search-highlight-text: var(--vp-c-neutral-inverse)}@media (prefers-reduced-motion: reduce){*,:before,:after{animation-delay:-1ms!important;animation-duration:1ms!important;animation-iteration-count:1!important;background-attachment:initial!important;scroll-behavior:auto!important;transition-duration:0s!important;transition-delay:0s!important}}*,:before,:after{box-sizing:border-box}html{line-height:1.4;font-size:16px;-webkit-text-size-adjust:100%}html.dark{color-scheme:dark}body{margin:0;width:100%;min-width:320px;min-height:100vh;line-height:24px;font-family:var(--vp-font-family-base);font-size:16px;font-weight:400;color:var(--vp-c-text-1);background-color:var(--vp-c-bg);font-synthesis:style;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}main{display:block}h1,h2,h3,h4,h5,h6{margin:0;line-height:24px;font-size:16px;font-weight:400}p{margin:0}strong,b{font-weight:600}a,area,button,[role=button],input,label,select,summary,textarea{touch-action:manipulation}a{color:inherit;text-decoration:inherit}ol,ul{list-style:none;margin:0;padding:0}blockquote{margin:0}pre,code,kbd,samp{font-family:var(--vp-font-family-mono)}img,svg,video,canvas,audio,iframe,embed,object{display:block}figure{margin:0}img,video{max-width:100%;height:auto}button,input,optgroup,select,textarea{border:0;padding:0;line-height:inherit;color:inherit}button{padding:0;font-family:inherit;background-color:transparent;background-image:none}button:enabled,[role=button]:enabled{cursor:pointer}button:focus,button:focus-visible{outline:1px dotted;outline:4px auto -webkit-focus-ring-color}button:focus:not(:focus-visible){outline:none!important}input:focus,textarea:focus,select:focus{outline:none}table{border-collapse:collapse}input{background-color:transparent}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:var(--vp-c-text-3)}input::-ms-input-placeholder,textarea::-ms-input-placeholder{color:var(--vp-c-text-3)}input::placeholder,textarea::placeholder{color:var(--vp-c-text-3)}input::-webkit-outer-spin-button,input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}input[type=number]{-moz-appearance:textfield}textarea{resize:vertical}select{-webkit-appearance:none}fieldset{margin:0;padding:0}h1,h2,h3,h4,h5,h6,li,p{overflow-wrap:break-word}vite-error-overlay{z-index:9999}mjx-container{display:inline-block;margin:auto 2px -2px}mjx-container>svg{margin:auto}.visually-hidden{position:absolute;width:1px;height:1px;white-space:nowrap;clip:rect(0 0 0 0);clip-path:inset(50%);overflow:hidden}.custom-block{border:1px solid transparent;border-radius:8px;padding:16px 16px 8px;line-height:24px;font-size:var(--vp-custom-block-font-size);color:var(--vp-c-text-2)}.custom-block.info{border-color:var(--vp-custom-block-info-border);color:var(--vp-custom-block-info-text);background-color:var(--vp-custom-block-info-bg)}.custom-block.info a,.custom-block.info code{color:var(--vp-c-brand-1)}.custom-block.info a:hover,.custom-block.info a:hover>code{color:var(--vp-c-brand-2)}.custom-block.info code{background-color:var(--vp-custom-block-info-code-bg)}.custom-block.note{border-color:var(--vp-custom-block-note-border);color:var(--vp-custom-block-note-text);background-color:var(--vp-custom-block-note-bg)}.custom-block.note a,.custom-block.note code{color:var(--vp-c-brand-1)}.custom-block.note a:hover,.custom-block.note a:hover>code{color:var(--vp-c-brand-2)}.custom-block.note code{background-color:var(--vp-custom-block-note-code-bg)}.custom-block.tip{border-color:var(--vp-custom-block-tip-border);color:var(--vp-custom-block-tip-text);background-color:var(--vp-custom-block-tip-bg)}.custom-block.tip a,.custom-block.tip code{color:var(--vp-c-tip-1)}.custom-block.tip a:hover,.custom-block.tip a:hover>code{color:var(--vp-c-tip-2)}.custom-block.tip code{background-color:var(--vp-custom-block-tip-code-bg)}.custom-block.important{border-color:var(--vp-custom-block-important-border);color:var(--vp-custom-block-important-text);background-color:var(--vp-custom-block-important-bg)}.custom-block.important a,.custom-block.important code{color:var(--vp-c-important-1)}.custom-block.important a:hover,.custom-block.important a:hover>code{color:var(--vp-c-important-2)}.custom-block.important code{background-color:var(--vp-custom-block-important-code-bg)}.custom-block.warning{border-color:var(--vp-custom-block-warning-border);color:var(--vp-custom-block-warning-text);background-color:var(--vp-custom-block-warning-bg)}.custom-block.warning a,.custom-block.warning code{color:var(--vp-c-warning-1)}.custom-block.warning a:hover,.custom-block.warning a:hover>code{color:var(--vp-c-warning-2)}.custom-block.warning code{background-color:var(--vp-custom-block-warning-code-bg)}.custom-block.danger{border-color:var(--vp-custom-block-danger-border);color:var(--vp-custom-block-danger-text);background-color:var(--vp-custom-block-danger-bg)}.custom-block.danger a,.custom-block.danger code{color:var(--vp-c-danger-1)}.custom-block.danger a:hover,.custom-block.danger a:hover>code{color:var(--vp-c-danger-2)}.custom-block.danger code{background-color:var(--vp-custom-block-danger-code-bg)}.custom-block.caution{border-color:var(--vp-custom-block-caution-border);color:var(--vp-custom-block-caution-text);background-color:var(--vp-custom-block-caution-bg)}.custom-block.caution a,.custom-block.caution code{color:var(--vp-c-caution-1)}.custom-block.caution a:hover,.custom-block.caution a:hover>code{color:var(--vp-c-caution-2)}.custom-block.caution code{background-color:var(--vp-custom-block-caution-code-bg)}.custom-block.details{border-color:var(--vp-custom-block-details-border);color:var(--vp-custom-block-details-text);background-color:var(--vp-custom-block-details-bg)}.custom-block.details a{color:var(--vp-c-brand-1)}.custom-block.details a:hover,.custom-block.details a:hover>code{color:var(--vp-c-brand-2)}.custom-block.details code{background-color:var(--vp-custom-block-details-code-bg)}.custom-block-title{font-weight:600}.custom-block p+p{margin:8px 0}.custom-block.details summary{margin:0 0 8px;font-weight:700;cursor:pointer;-webkit-user-select:none;user-select:none}.custom-block.details summary+p{margin:8px 0}.custom-block a{color:inherit;font-weight:600;text-decoration:underline;text-underline-offset:2px;transition:opacity .25s}.custom-block a:hover{opacity:.75}.custom-block code{font-size:var(--vp-custom-block-code-font-size)}.custom-block.custom-block th,.custom-block.custom-block blockquote>p{font-size:var(--vp-custom-block-font-size);color:inherit}.dark .vp-code span{color:var(--shiki-dark, inherit)}html:not(.dark) .vp-code span{color:var(--shiki-light, inherit)}.vp-code-group{margin-top:16px}.vp-code-group .tabs{position:relative;display:flex;margin-right:-24px;margin-left:-24px;padding:0 12px;background-color:var(--vp-code-tab-bg);overflow-x:auto;overflow-y:hidden;box-shadow:inset 0 -1px var(--vp-code-tab-divider)}@media (min-width: 640px){.vp-code-group .tabs{margin-right:0;margin-left:0;border-radius:8px 8px 0 0}}.vp-code-group .tabs input{position:fixed;opacity:0;pointer-events:none}.vp-code-group .tabs label{position:relative;display:inline-block;border-bottom:1px solid transparent;padding:0 12px;line-height:48px;font-size:14px;font-weight:500;color:var(--vp-code-tab-text-color);white-space:nowrap;cursor:pointer;transition:color .25s}.vp-code-group .tabs label:after{position:absolute;right:8px;bottom:-1px;left:8px;z-index:1;height:2px;border-radius:2px;content:"";background-color:transparent;transition:background-color .25s}.vp-code-group label:hover{color:var(--vp-code-tab-hover-text-color)}.vp-code-group input:checked+label{color:var(--vp-code-tab-active-text-color)}.vp-code-group input:checked+label:after{background-color:var(--vp-code-tab-active-bar-color)}.vp-code-group div[class*=language-],.vp-block{display:none;margin-top:0!important;border-top-left-radius:0!important;border-top-right-radius:0!important}.vp-code-group div[class*=language-].active,.vp-block.active{display:block}.vp-block{padding:20px 24px}.vp-doc h1,.vp-doc h2,.vp-doc h3,.vp-doc h4,.vp-doc h5,.vp-doc h6{position:relative;font-weight:600;outline:none}.vp-doc h1{letter-spacing:-.02em;line-height:40px;font-size:28px}.vp-doc h2{margin:48px 0 16px;border-top:1px solid var(--vp-c-divider);padding-top:24px;letter-spacing:-.02em;line-height:32px;font-size:24px}.vp-doc h3{margin:32px 0 0;letter-spacing:-.01em;line-height:28px;font-size:20px}.vp-doc .header-anchor{position:absolute;top:0;left:0;margin-left:-.87em;font-weight:500;-webkit-user-select:none;user-select:none;opacity:0;text-decoration:none;transition:color .25s,opacity .25s}.vp-doc .header-anchor:before{content:var(--vp-header-anchor-symbol)}.vp-doc h1:hover .header-anchor,.vp-doc h1 .header-anchor:focus,.vp-doc h2:hover .header-anchor,.vp-doc h2 .header-anchor:focus,.vp-doc h3:hover .header-anchor,.vp-doc h3 .header-anchor:focus,.vp-doc h4:hover .header-anchor,.vp-doc h4 .header-anchor:focus,.vp-doc h5:hover .header-anchor,.vp-doc h5 .header-anchor:focus,.vp-doc h6:hover .header-anchor,.vp-doc h6 .header-anchor:focus{opacity:1}@media (min-width: 768px){.vp-doc h1{letter-spacing:-.02em;line-height:40px;font-size:32px}}.vp-doc h2 .header-anchor{top:24px}.vp-doc p,.vp-doc summary{margin:16px 0}.vp-doc p{line-height:28px}.vp-doc blockquote{margin:16px 0;border-left:2px solid var(--vp-c-divider);padding-left:16px;transition:border-color .5s}.vp-doc blockquote>p{margin:0;font-size:16px;color:var(--vp-c-text-2);transition:color .5s}.vp-doc a{font-weight:500;color:var(--vp-c-brand-1);text-decoration:underline;text-underline-offset:2px;transition:color .25s,opacity .25s}.vp-doc a:hover{color:var(--vp-c-brand-2)}.vp-doc strong{font-weight:600}.vp-doc ul,.vp-doc ol{padding-left:1.25rem;margin:16px 0}.vp-doc ul{list-style:disc}.vp-doc ol{list-style:decimal}.vp-doc li+li{margin-top:8px}.vp-doc li>ol,.vp-doc li>ul{margin:8px 0 0}.vp-doc table{display:block;border-collapse:collapse;margin:20px 0;overflow-x:auto}.vp-doc tr{background-color:var(--vp-c-bg);border-top:1px solid var(--vp-c-divider);transition:background-color .5s}.vp-doc tr:nth-child(2n){background-color:var(--vp-c-bg-soft)}.vp-doc th,.vp-doc td{border:1px solid var(--vp-c-divider);padding:8px 16px}.vp-doc th{text-align:left;font-size:14px;font-weight:600;color:var(--vp-c-text-2);background-color:var(--vp-c-bg-soft)}.vp-doc td{font-size:14px}.vp-doc hr{margin:16px 0;border:none;border-top:1px solid var(--vp-c-divider)}.vp-doc .custom-block{margin:16px 0}.vp-doc .custom-block p{margin:8px 0;line-height:24px}.vp-doc .custom-block p:first-child{margin:0}.vp-doc .custom-block div[class*=language-]{margin:8px 0;border-radius:8px}.vp-doc .custom-block div[class*=language-] code{font-weight:400;background-color:transparent}.vp-doc .custom-block .vp-code-group .tabs{margin:0;border-radius:8px 8px 0 0}.vp-doc :not(pre,h1,h2,h3,h4,h5,h6)>code{font-size:var(--vp-code-font-size);color:var(--vp-code-color)}.vp-doc :not(pre)>code{border-radius:4px;padding:3px 6px;background-color:var(--vp-code-bg);transition:color .25s,background-color .5s}.vp-doc a>code{color:var(--vp-code-link-color)}.vp-doc a:hover>code{color:var(--vp-code-link-hover-color)}.vp-doc h1>code,.vp-doc h2>code,.vp-doc h3>code{font-size:.9em}.vp-doc div[class*=language-],.vp-block{position:relative;margin:16px -24px;background-color:var(--vp-code-block-bg);overflow-x:auto;transition:background-color .5s}@media (min-width: 640px){.vp-doc div[class*=language-],.vp-block{border-radius:8px;margin:16px 0}}@media (max-width: 639px){.vp-doc li div[class*=language-]{border-radius:8px 0 0 8px}}.vp-doc div[class*=language-]+div[class*=language-],.vp-doc div[class$=-api]+div[class*=language-],.vp-doc div[class*=language-]+div[class$=-api]>div[class*=language-]{margin-top:-8px}.vp-doc [class*=language-] pre,.vp-doc [class*=language-] code{direction:ltr;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}.vp-doc [class*=language-] pre{position:relative;z-index:1;margin:0;padding:20px 0;background:transparent;overflow-x:auto}.vp-doc [class*=language-] code{display:block;padding:0 24px;width:fit-content;min-width:100%;line-height:var(--vp-code-line-height);font-size:var(--vp-code-font-size);color:var(--vp-code-block-color);transition:color .5s}.vp-doc [class*=language-] code .highlighted{background-color:var(--vp-code-line-highlight-color);transition:background-color .5s;margin:0 -24px;padding:0 24px;width:calc(100% + 48px);display:inline-block}.vp-doc [class*=language-] code .highlighted.error{background-color:var(--vp-code-line-error-color)}.vp-doc [class*=language-] code .highlighted.warning{background-color:var(--vp-code-line-warning-color)}.vp-doc [class*=language-] code .diff{transition:background-color .5s;margin:0 -24px;padding:0 24px;width:calc(100% + 48px);display:inline-block}.vp-doc [class*=language-] code .diff:before{position:absolute;left:10px}.vp-doc [class*=language-] .has-focused-lines .line:not(.has-focus){filter:blur(.095rem);opacity:.4;transition:filter .35s,opacity .35s}.vp-doc [class*=language-] .has-focused-lines .line:not(.has-focus){opacity:.7;transition:filter .35s,opacity .35s}.vp-doc [class*=language-]:hover .has-focused-lines .line:not(.has-focus){filter:blur(0);opacity:1}.vp-doc [class*=language-] code .diff.remove{background-color:var(--vp-code-line-diff-remove-color);opacity:.7}.vp-doc [class*=language-] code .diff.remove:before{content:"-";color:var(--vp-code-line-diff-remove-symbol-color)}.vp-doc [class*=language-] code .diff.add{background-color:var(--vp-code-line-diff-add-color)}.vp-doc [class*=language-] code .diff.add:before{content:"+";color:var(--vp-code-line-diff-add-symbol-color)}.vp-doc div[class*=language-].line-numbers-mode{padding-left:32px}.vp-doc .line-numbers-wrapper{position:absolute;top:0;bottom:0;left:0;z-index:3;border-right:1px solid var(--vp-code-block-divider-color);padding-top:20px;width:32px;text-align:center;font-family:var(--vp-font-family-mono);line-height:var(--vp-code-line-height);font-size:var(--vp-code-font-size);color:var(--vp-code-line-number-color);transition:border-color .5s,color .5s}.vp-doc [class*=language-]>button.copy{direction:ltr;position:absolute;top:12px;right:12px;z-index:3;border:1px solid var(--vp-code-copy-code-border-color);border-radius:4px;width:40px;height:40px;background-color:var(--vp-code-copy-code-bg);opacity:0;cursor:pointer;background-image:var(--vp-icon-copy);background-position:50%;background-size:20px;background-repeat:no-repeat;transition:border-color .25s,background-color .25s,opacity .25s}.vp-doc [class*=language-]:hover>button.copy,.vp-doc [class*=language-]>button.copy:focus{opacity:1}.vp-doc [class*=language-]>button.copy:hover,.vp-doc [class*=language-]>button.copy.copied{border-color:var(--vp-code-copy-code-hover-border-color);background-color:var(--vp-code-copy-code-hover-bg)}.vp-doc [class*=language-]>button.copy.copied,.vp-doc [class*=language-]>button.copy:hover.copied{border-radius:0 4px 4px 0;background-color:var(--vp-code-copy-code-hover-bg);background-image:var(--vp-icon-copied)}.vp-doc [class*=language-]>button.copy.copied:before,.vp-doc [class*=language-]>button.copy:hover.copied:before{position:relative;top:-1px;transform:translate(calc(-100% - 1px));display:flex;justify-content:center;align-items:center;border:1px solid var(--vp-code-copy-code-hover-border-color);border-right:0;border-radius:4px 0 0 4px;padding:0 10px;width:fit-content;height:40px;text-align:center;font-size:12px;font-weight:500;color:var(--vp-code-copy-code-active-text);background-color:var(--vp-code-copy-code-hover-bg);white-space:nowrap;content:var(--vp-code-copy-copied-text-content)}.vp-doc [class*=language-]>span.lang{position:absolute;top:2px;right:8px;z-index:2;font-size:12px;font-weight:500;color:var(--vp-code-lang-color);transition:color .4s,opacity .4s}.vp-doc [class*=language-]:hover>button.copy+span.lang,.vp-doc [class*=language-]>button.copy:focus+span.lang{opacity:0}.vp-doc .VPTeamMembers{margin-top:24px}.vp-doc .VPTeamMembers.small.count-1 .container{margin:0!important;max-width:calc((100% - 24px)/2)!important}.vp-doc .VPTeamMembers.small.count-2 .container,.vp-doc .VPTeamMembers.small.count-3 .container{max-width:100%!important}.vp-doc .VPTeamMembers.medium.count-1 .container{margin:0!important;max-width:calc((100% - 24px)/2)!important}:is(.vp-external-link-icon,.vp-doc a[href*="://"],.vp-doc a[target=_blank]):not(.no-icon):after{display:inline-block;margin-top:-1px;margin-left:4px;width:11px;height:11px;background:currentColor;color:var(--vp-c-text-3);flex-shrink:0;--icon: url("data:image/svg+xml, %3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' %3E%3Cpath d='M0 0h24v24H0V0z' fill='none' /%3E%3Cpath d='M9 5v2h6.59L4 18.59 5.41 20 17 8.41V15h2V5H9z' /%3E%3C/svg%3E");-webkit-mask-image:var(--icon);mask-image:var(--icon)}.vp-external-link-icon:after{content:""}.external-link-icon-enabled :is(.vp-doc a[href*="://"],.vp-doc a[target=_blank]):after{content:"";color:currentColor}.vp-sponsor{border-radius:16px;overflow:hidden}.vp-sponsor.aside{border-radius:12px}.vp-sponsor-section+.vp-sponsor-section{margin-top:4px}.vp-sponsor-tier{margin-bottom:4px;text-align:center;letter-spacing:1px;line-height:24px;width:100%;font-weight:600;color:var(--vp-c-text-2);background-color:var(--vp-c-bg-soft)}.vp-sponsor.normal .vp-sponsor-tier{padding:13px 0 11px;font-size:14px}.vp-sponsor.aside .vp-sponsor-tier{padding:9px 0 7px;font-size:12px}.vp-sponsor-grid+.vp-sponsor-tier{margin-top:4px}.vp-sponsor-grid{display:flex;flex-wrap:wrap;gap:4px}.vp-sponsor-grid.xmini .vp-sponsor-grid-link{height:64px}.vp-sponsor-grid.xmini .vp-sponsor-grid-image{max-width:64px;max-height:22px}.vp-sponsor-grid.mini .vp-sponsor-grid-link{height:72px}.vp-sponsor-grid.mini .vp-sponsor-grid-image{max-width:96px;max-height:24px}.vp-sponsor-grid.small .vp-sponsor-grid-link{height:96px}.vp-sponsor-grid.small .vp-sponsor-grid-image{max-width:96px;max-height:24px}.vp-sponsor-grid.medium .vp-sponsor-grid-link{height:112px}.vp-sponsor-grid.medium .vp-sponsor-grid-image{max-width:120px;max-height:36px}.vp-sponsor-grid.big .vp-sponsor-grid-link{height:184px}.vp-sponsor-grid.big .vp-sponsor-grid-image{max-width:192px;max-height:56px}.vp-sponsor-grid[data-vp-grid="2"] .vp-sponsor-grid-item{width:calc((100% - 4px)/2)}.vp-sponsor-grid[data-vp-grid="3"] .vp-sponsor-grid-item{width:calc((100% - 4px * 2) / 3)}.vp-sponsor-grid[data-vp-grid="4"] .vp-sponsor-grid-item{width:calc((100% - 12px)/4)}.vp-sponsor-grid[data-vp-grid="5"] .vp-sponsor-grid-item{width:calc((100% - 16px)/5)}.vp-sponsor-grid[data-vp-grid="6"] .vp-sponsor-grid-item{width:calc((100% - 4px * 5) / 6)}.vp-sponsor-grid-item{flex-shrink:0;width:100%;background-color:var(--vp-c-bg-soft);transition:background-color .25s}.vp-sponsor-grid-item:hover{background-color:var(--vp-c-default-soft)}.vp-sponsor-grid-item:hover .vp-sponsor-grid-image{filter:grayscale(0) invert(0)}.vp-sponsor-grid-item.empty:hover{background-color:var(--vp-c-bg-soft)}.dark .vp-sponsor-grid-item:hover{background-color:var(--vp-c-white)}.dark .vp-sponsor-grid-item.empty:hover{background-color:var(--vp-c-bg-soft)}.vp-sponsor-grid-link{display:flex}.vp-sponsor-grid-box{display:flex;justify-content:center;align-items:center;width:100%}.vp-sponsor-grid-image{max-width:100%;filter:grayscale(1);transition:filter .25s}.dark .vp-sponsor-grid-image{filter:grayscale(1) invert(1)}.VPBadge{display:inline-block;margin-left:2px;border:1px solid transparent;border-radius:12px;padding:0 10px;line-height:22px;font-size:12px;font-weight:500;transform:translateY(-2px)}.VPBadge.small{padding:0 6px;line-height:18px;font-size:10px;transform:translateY(-8px)}.VPDocFooter .VPBadge{display:none}.vp-doc h1>.VPBadge{margin-top:4px;vertical-align:top}.vp-doc h2>.VPBadge{margin-top:3px;padding:0 8px;vertical-align:top}.vp-doc h3>.VPBadge{vertical-align:middle}.vp-doc h4>.VPBadge,.vp-doc h5>.VPBadge,.vp-doc h6>.VPBadge{vertical-align:middle;line-height:18px}.VPBadge.info{border-color:var(--vp-badge-info-border);color:var(--vp-badge-info-text);background-color:var(--vp-badge-info-bg)}.VPBadge.tip{border-color:var(--vp-badge-tip-border);color:var(--vp-badge-tip-text);background-color:var(--vp-badge-tip-bg)}.VPBadge.warning{border-color:var(--vp-badge-warning-border);color:var(--vp-badge-warning-text);background-color:var(--vp-badge-warning-bg)}.VPBadge.danger{border-color:var(--vp-badge-danger-border);color:var(--vp-badge-danger-text);background-color:var(--vp-badge-danger-bg)}.VPBackdrop[data-v-54a304ca]{position:fixed;top:0;right:0;bottom:0;left:0;z-index:var(--vp-z-index-backdrop);background:var(--vp-backdrop-bg-color);transition:opacity .5s}.VPBackdrop.fade-enter-from[data-v-54a304ca],.VPBackdrop.fade-leave-to[data-v-54a304ca]{opacity:0}.VPBackdrop.fade-leave-active[data-v-54a304ca]{transition-duration:.25s}@media (min-width: 1280px){.VPBackdrop[data-v-54a304ca]{display:none}}.NotFound[data-v-b9c0c15a]{padding:64px 24px 96px;text-align:center}@media (min-width: 768px){.NotFound[data-v-b9c0c15a]{padding:96px 32px 168px}}.code[data-v-b9c0c15a]{line-height:64px;font-size:64px;font-weight:600}.title[data-v-b9c0c15a]{padding-top:12px;letter-spacing:2px;line-height:20px;font-size:20px;font-weight:700}.divider[data-v-b9c0c15a]{margin:24px auto 18px;width:64px;height:1px;background-color:var(--vp-c-divider)}.quote[data-v-b9c0c15a]{margin:0 auto;max-width:256px;font-size:14px;font-weight:500;color:var(--vp-c-text-2)}.action[data-v-b9c0c15a]{padding-top:20px}.link[data-v-b9c0c15a]{display:inline-block;border:1px solid var(--vp-c-brand-1);border-radius:16px;padding:3px 16px;font-size:14px;font-weight:500;color:var(--vp-c-brand-1);transition:border-color .25s,color .25s}.link[data-v-b9c0c15a]:hover{border-color:var(--vp-c-brand-2);color:var(--vp-c-brand-2)}.root[data-v-53c99d69]{position:relative;z-index:1}.nested[data-v-53c99d69]{padding-right:16px;padding-left:16px}.outline-link[data-v-53c99d69]{display:block;line-height:32px;font-size:14px;font-weight:400;color:var(--vp-c-text-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:color .5s}.outline-link[data-v-53c99d69]:hover,.outline-link.active[data-v-53c99d69]{color:var(--vp-c-text-1);transition:color .25s}.outline-link.nested[data-v-53c99d69]{padding-left:13px}.VPDocAsideOutline[data-v-6b52fe58]{display:none}.VPDocAsideOutline.has-outline[data-v-6b52fe58]{display:block}.content[data-v-6b52fe58]{position:relative;border-left:1px solid var(--vp-c-divider);padding-left:16px;font-size:13px;font-weight:500}.outline-marker[data-v-6b52fe58]{position:absolute;top:32px;left:-1px;z-index:0;opacity:0;width:2px;border-radius:2px;height:18px;background-color:var(--vp-c-brand-1);transition:top .25s cubic-bezier(0,1,.5,1),background-color .5s,opacity .25s}.outline-title[data-v-6b52fe58]{line-height:32px;font-size:14px;font-weight:600}.VPDocAside[data-v-cb998dce]{display:flex;flex-direction:column;flex-grow:1}.spacer[data-v-cb998dce]{flex-grow:1}.VPDocAside[data-v-cb998dce] .spacer+.VPDocAsideSponsors,.VPDocAside[data-v-cb998dce] .spacer+.VPDocAsideCarbonAds{margin-top:24px}.VPDocAside[data-v-cb998dce] .VPDocAsideSponsors+.VPDocAsideCarbonAds{margin-top:16px}.VPLastUpdated[data-v-19a7ae4e]{line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-2)}@media (min-width: 640px){.VPLastUpdated[data-v-19a7ae4e]{line-height:32px;font-size:14px;font-weight:500}}.VPDocFooter[data-v-b4b63abf]{margin-top:64px}.edit-info[data-v-b4b63abf]{padding-bottom:18px}@media (min-width: 640px){.edit-info[data-v-b4b63abf]{display:flex;justify-content:space-between;align-items:center;padding-bottom:14px}}.edit-link-button[data-v-b4b63abf]{display:flex;align-items:center;border:0;line-height:32px;font-size:14px;font-weight:500;color:var(--vp-c-brand-1);transition:color .25s}.edit-link-button[data-v-b4b63abf]:hover{color:var(--vp-c-brand-2)}.edit-link-icon[data-v-b4b63abf]{margin-right:8px;width:14px;height:14px;fill:currentColor}.prev-next[data-v-b4b63abf]{border-top:1px solid var(--vp-c-divider);padding-top:24px;display:grid;grid-row-gap:8px}@media (min-width: 640px){.prev-next[data-v-b4b63abf]{grid-template-columns:repeat(2,1fr);grid-column-gap:16px}}.pager-link[data-v-b4b63abf]{display:block;border:1px solid var(--vp-c-divider);border-radius:8px;padding:11px 16px 13px;width:100%;height:100%;transition:border-color .25s}.pager-link[data-v-b4b63abf]:hover{border-color:var(--vp-c-brand-1)}.pager-link.next[data-v-b4b63abf]{margin-left:auto;text-align:right}.desc[data-v-b4b63abf]{display:block;line-height:20px;font-size:12px;font-weight:500;color:var(--vp-c-text-2)}.title[data-v-b4b63abf]{display:block;line-height:20px;font-size:14px;font-weight:500;color:var(--vp-c-brand-1);transition:color .25s}.VPDoc[data-v-e6f2a212]{padding:32px 24px 96px;width:100%}@media (min-width: 768px){.VPDoc[data-v-e6f2a212]{padding:48px 32px 128px}}@media (min-width: 960px){.VPDoc[data-v-e6f2a212]{padding:48px 32px 0}.VPDoc:not(.has-sidebar) .container[data-v-e6f2a212]{display:flex;justify-content:center;max-width:992px}.VPDoc:not(.has-sidebar) .content[data-v-e6f2a212]{max-width:752px}}@media (min-width: 1280px){.VPDoc .container[data-v-e6f2a212]{display:flex;justify-content:center}.VPDoc .aside[data-v-e6f2a212]{display:block}}@media (min-width: 1440px){.VPDoc:not(.has-sidebar) .content[data-v-e6f2a212]{max-width:784px}.VPDoc:not(.has-sidebar) .container[data-v-e6f2a212]{max-width:1104px}}.container[data-v-e6f2a212]{margin:0 auto;width:100%}.aside[data-v-e6f2a212]{position:relative;display:none;order:2;flex-grow:1;padding-left:32px;width:100%;max-width:256px}.left-aside[data-v-e6f2a212]{order:1;padding-left:unset;padding-right:32px}.aside-container[data-v-e6f2a212]{position:fixed;top:0;padding-top:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + var(--vp-doc-top-height, 0px) + 48px);width:224px;height:100vh;overflow-x:hidden;overflow-y:auto;scrollbar-width:none}.aside-container[data-v-e6f2a212]::-webkit-scrollbar{display:none}.aside-curtain[data-v-e6f2a212]{position:fixed;bottom:0;z-index:10;width:224px;height:32px;background:linear-gradient(transparent,var(--vp-c-bg) 70%)}.aside-content[data-v-e6f2a212]{display:flex;flex-direction:column;min-height:calc(100vh - (var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 48px));padding-bottom:32px}.content[data-v-e6f2a212]{position:relative;margin:0 auto;width:100%}@media (min-width: 960px){.content[data-v-e6f2a212]{padding:0 32px 128px}}@media (min-width: 1280px){.content[data-v-e6f2a212]{order:1;margin:0;min-width:640px}}.content-container[data-v-e6f2a212]{margin:0 auto}.VPDoc.has-aside .content-container[data-v-e6f2a212]{max-width:688px}.VPButton[data-v-1e76fe75]{display:inline-block;border:1px solid transparent;text-align:center;font-weight:600;white-space:nowrap;transition:color .25s,border-color .25s,background-color .25s}.VPButton[data-v-1e76fe75]:active{transition:color .1s,border-color .1s,background-color .1s}.VPButton.medium[data-v-1e76fe75]{border-radius:20px;padding:0 20px;line-height:38px;font-size:14px}.VPButton.big[data-v-1e76fe75]{border-radius:24px;padding:0 24px;line-height:46px;font-size:16px}.VPButton.brand[data-v-1e76fe75]{border-color:var(--vp-button-brand-border);color:var(--vp-button-brand-text);background-color:var(--vp-button-brand-bg)}.VPButton.brand[data-v-1e76fe75]:hover{border-color:var(--vp-button-brand-hover-border);color:var(--vp-button-brand-hover-text);background-color:var(--vp-button-brand-hover-bg)}.VPButton.brand[data-v-1e76fe75]:active{border-color:var(--vp-button-brand-active-border);color:var(--vp-button-brand-active-text);background-color:var(--vp-button-brand-active-bg)}.VPButton.alt[data-v-1e76fe75]{border-color:var(--vp-button-alt-border);color:var(--vp-button-alt-text);background-color:var(--vp-button-alt-bg)}.VPButton.alt[data-v-1e76fe75]:hover{border-color:var(--vp-button-alt-hover-border);color:var(--vp-button-alt-hover-text);background-color:var(--vp-button-alt-hover-bg)}.VPButton.alt[data-v-1e76fe75]:active{border-color:var(--vp-button-alt-active-border);color:var(--vp-button-alt-active-text);background-color:var(--vp-button-alt-active-bg)}.VPButton.sponsor[data-v-1e76fe75]{border-color:var(--vp-button-sponsor-border);color:var(--vp-button-sponsor-text);background-color:var(--vp-button-sponsor-bg)}.VPButton.sponsor[data-v-1e76fe75]:hover{border-color:var(--vp-button-sponsor-hover-border);color:var(--vp-button-sponsor-hover-text);background-color:var(--vp-button-sponsor-hover-bg)}.VPButton.sponsor[data-v-1e76fe75]:active{border-color:var(--vp-button-sponsor-active-border);color:var(--vp-button-sponsor-active-text);background-color:var(--vp-button-sponsor-active-bg)}html:not(.dark) .VPImage.dark[data-v-ab19afbb]{display:none}.dark .VPImage.light[data-v-ab19afbb]{display:none}.VPHero[data-v-5a3e9999]{margin-top:calc((var(--vp-nav-height) + var(--vp-layout-top-height, 0px)) * -1);padding:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 48px) 24px 48px}@media (min-width: 640px){.VPHero[data-v-5a3e9999]{padding:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 80px) 48px 64px}}@media (min-width: 960px){.VPHero[data-v-5a3e9999]{padding:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 80px) 64px 64px}}.container[data-v-5a3e9999]{display:flex;flex-direction:column;margin:0 auto;max-width:1152px}@media (min-width: 960px){.container[data-v-5a3e9999]{flex-direction:row}}.main[data-v-5a3e9999]{position:relative;z-index:10;order:2;flex-grow:1;flex-shrink:0}.VPHero.has-image .container[data-v-5a3e9999]{text-align:center}@media (min-width: 960px){.VPHero.has-image .container[data-v-5a3e9999]{text-align:left}}@media (min-width: 960px){.main[data-v-5a3e9999]{order:1;width:calc((100% / 3) * 2)}.VPHero.has-image .main[data-v-5a3e9999]{max-width:592px}}.name[data-v-5a3e9999],.text[data-v-5a3e9999]{max-width:392px;letter-spacing:-.4px;line-height:40px;font-size:32px;font-weight:700;white-space:pre-wrap}.VPHero.has-image .name[data-v-5a3e9999],.VPHero.has-image .text[data-v-5a3e9999]{margin:0 auto}.name[data-v-5a3e9999]{color:var(--vp-home-hero-name-color)}.clip[data-v-5a3e9999]{background:var(--vp-home-hero-name-background);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:var(--vp-home-hero-name-color)}@media (min-width: 640px){.name[data-v-5a3e9999],.text[data-v-5a3e9999]{max-width:576px;line-height:56px;font-size:48px}}@media (min-width: 960px){.name[data-v-5a3e9999],.text[data-v-5a3e9999]{line-height:64px;font-size:56px}.VPHero.has-image .name[data-v-5a3e9999],.VPHero.has-image .text[data-v-5a3e9999]{margin:0}}.tagline[data-v-5a3e9999]{padding-top:8px;max-width:392px;line-height:28px;font-size:18px;font-weight:500;white-space:pre-wrap;color:var(--vp-c-text-2)}.VPHero.has-image .tagline[data-v-5a3e9999]{margin:0 auto}@media (min-width: 640px){.tagline[data-v-5a3e9999]{padding-top:12px;max-width:576px;line-height:32px;font-size:20px}}@media (min-width: 960px){.tagline[data-v-5a3e9999]{line-height:36px;font-size:24px}.VPHero.has-image .tagline[data-v-5a3e9999]{margin:0}}.actions[data-v-5a3e9999]{display:flex;flex-wrap:wrap;margin:-6px;padding-top:24px}.VPHero.has-image .actions[data-v-5a3e9999]{justify-content:center}@media (min-width: 640px){.actions[data-v-5a3e9999]{padding-top:32px}}@media (min-width: 960px){.VPHero.has-image .actions[data-v-5a3e9999]{justify-content:flex-start}}.action[data-v-5a3e9999]{flex-shrink:0;padding:6px}.image[data-v-5a3e9999]{order:1;margin:-76px -24px -48px}@media (min-width: 640px){.image[data-v-5a3e9999]{margin:-108px -24px -48px}}@media (min-width: 960px){.image[data-v-5a3e9999]{flex-grow:1;order:2;margin:0;min-height:100%}}.image-container[data-v-5a3e9999]{position:relative;margin:0 auto;width:320px;height:320px}@media (min-width: 640px){.image-container[data-v-5a3e9999]{width:392px;height:392px}}@media (min-width: 960px){.image-container[data-v-5a3e9999]{display:flex;justify-content:center;align-items:center;width:100%;height:100%;transform:translate(-32px,-32px)}}.image-bg[data-v-5a3e9999]{position:absolute;top:50%;left:50%;border-radius:50%;width:192px;height:192px;background-image:var(--vp-home-hero-image-background-image);filter:var(--vp-home-hero-image-filter);transform:translate(-50%,-50%)}@media (min-width: 640px){.image-bg[data-v-5a3e9999]{width:256px;height:256px}}@media (min-width: 960px){.image-bg[data-v-5a3e9999]{width:320px;height:320px}}[data-v-5a3e9999] .image-src{position:absolute;top:50%;left:50%;max-width:192px;max-height:192px;transform:translate(-50%,-50%)}@media (min-width: 640px){[data-v-5a3e9999] .image-src{max-width:256px;max-height:256px}}@media (min-width: 960px){[data-v-5a3e9999] .image-src{max-width:320px;max-height:320px}}.VPFeature[data-v-ee984185]{display:block;border:1px solid var(--vp-c-bg-soft);border-radius:12px;height:100%;background-color:var(--vp-c-bg-soft);transition:border-color .25s,background-color .25s}.VPFeature.link[data-v-ee984185]:hover{border-color:var(--vp-c-brand-1)}.box[data-v-ee984185]{display:flex;flex-direction:column;padding:24px;height:100%}.box[data-v-ee984185]>.VPImage{margin-bottom:20px}.icon[data-v-ee984185]{display:flex;justify-content:center;align-items:center;margin-bottom:20px;border-radius:6px;background-color:var(--vp-c-default-soft);width:48px;height:48px;font-size:24px;transition:background-color .25s}.title[data-v-ee984185]{line-height:24px;font-size:16px;font-weight:600}.details[data-v-ee984185]{flex-grow:1;padding-top:8px;line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-2)}.link-text[data-v-ee984185]{padding-top:8px}.link-text-value[data-v-ee984185]{display:flex;align-items:center;font-size:14px;font-weight:500;color:var(--vp-c-brand-1)}.link-text-icon[data-v-ee984185]{display:inline-block;margin-left:6px;width:14px;height:14px;fill:currentColor}.VPFeatures[data-v-b1eea84a]{position:relative;padding:0 24px}@media (min-width: 640px){.VPFeatures[data-v-b1eea84a]{padding:0 48px}}@media (min-width: 960px){.VPFeatures[data-v-b1eea84a]{padding:0 64px}}.container[data-v-b1eea84a]{margin:0 auto;max-width:1152px}.items[data-v-b1eea84a]{display:flex;flex-wrap:wrap;margin:-8px}.item[data-v-b1eea84a]{padding:8px;width:100%}@media (min-width: 640px){.item.grid-2[data-v-b1eea84a],.item.grid-4[data-v-b1eea84a],.item.grid-6[data-v-b1eea84a]{width:50%}}@media (min-width: 768px){.item.grid-2[data-v-b1eea84a],.item.grid-4[data-v-b1eea84a]{width:50%}.item.grid-3[data-v-b1eea84a],.item.grid-6[data-v-b1eea84a]{width:calc(100% / 3)}}@media (min-width: 960px){.item.grid-4[data-v-b1eea84a]{width:25%}}.VPHome[data-v-20eabd3a]{padding-bottom:96px}.VPHome[data-v-20eabd3a] .VPHomeSponsors{margin-top:112px;margin-bottom:-128px}@media (min-width: 768px){.VPHome[data-v-20eabd3a]{padding-bottom:128px}}.VPContent[data-v-3cf691b6]{flex-grow:1;flex-shrink:0;margin:var(--vp-layout-top-height, 0px) auto 0;width:100%}.VPContent.is-home[data-v-3cf691b6]{width:100%;max-width:100%}.VPContent.has-sidebar[data-v-3cf691b6]{margin:0}@media (min-width: 960px){.VPContent[data-v-3cf691b6]{padding-top:var(--vp-nav-height)}.VPContent.has-sidebar[data-v-3cf691b6]{margin:var(--vp-layout-top-height, 0px) 0 0;padding-left:var(--vp-sidebar-width)}}@media (min-width: 1440px){.VPContent.has-sidebar[data-v-3cf691b6]{padding-right:calc((100vw - var(--vp-layout-max-width)) / 2);padding-left:calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width))}}.VPFooter[data-v-566314d4]{position:relative;z-index:var(--vp-z-index-footer);border-top:1px solid var(--vp-c-gutter);padding:32px 24px;background-color:var(--vp-c-bg)}.VPFooter.has-sidebar[data-v-566314d4]{display:none}.VPFooter[data-v-566314d4] a{text-decoration-line:underline;text-underline-offset:2px;transition:color .25s}.VPFooter[data-v-566314d4] a:hover{color:var(--vp-c-text-1)}@media (min-width: 768px){.VPFooter[data-v-566314d4]{padding:32px}}.container[data-v-566314d4]{margin:0 auto;max-width:var(--vp-layout-max-width);text-align:center}.message[data-v-566314d4],.copyright[data-v-566314d4]{line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-2)}.VPLocalNavOutlineDropdown[data-v-2744f6e0]{padding:12px 20px 11px}@media (min-width: 960px){.VPLocalNavOutlineDropdown[data-v-2744f6e0]{padding:12px 36px 11px}}.VPLocalNavOutlineDropdown button[data-v-2744f6e0]{display:block;font-size:12px;font-weight:500;line-height:24px;color:var(--vp-c-text-2);transition:color .5s;position:relative}.VPLocalNavOutlineDropdown button[data-v-2744f6e0]:hover{color:var(--vp-c-text-1);transition:color .25s}.VPLocalNavOutlineDropdown button.open[data-v-2744f6e0]{color:var(--vp-c-text-1)}@media (min-width: 960px){.VPLocalNavOutlineDropdown button[data-v-2744f6e0]{font-size:14px}}.icon[data-v-2744f6e0]{display:inline-block;vertical-align:middle;margin-left:2px;width:14px;height:14px;fill:currentColor}.open>.icon[data-v-2744f6e0]{transform:rotate(90deg)}.items[data-v-2744f6e0]{position:absolute;top:40px;right:16px;left:16px;display:grid;gap:1px;border:1px solid var(--vp-c-border);border-radius:8px;background-color:var(--vp-c-gutter);max-height:calc(var(--vp-vh, 100vh) - 86px);overflow:hidden auto;box-shadow:var(--vp-shadow-3)}@media (min-width: 960px){.items[data-v-2744f6e0]{right:auto;left:calc(var(--vp-sidebar-width) + 32px);width:320px}}.header[data-v-2744f6e0]{background-color:var(--vp-c-bg-soft)}.top-link[data-v-2744f6e0]{display:block;padding:0 16px;line-height:48px;font-size:14px;font-weight:500;color:var(--vp-c-brand-1)}.outline[data-v-2744f6e0]{padding:8px 0;background-color:var(--vp-c-bg-soft)}.flyout-enter-active[data-v-2744f6e0]{transition:all .2s ease-out}.flyout-leave-active[data-v-2744f6e0]{transition:all .15s ease-in}.flyout-enter-from[data-v-2744f6e0],.flyout-leave-to[data-v-2744f6e0]{opacity:0;transform:translateY(-16px)}.VPLocalNav[data-v-b979e4d9]{position:sticky;top:0;left:0;z-index:var(--vp-z-index-local-nav);border-bottom:1px solid var(--vp-c-gutter);padding-top:var(--vp-layout-top-height, 0px);width:100%;background-color:var(--vp-local-nav-bg-color)}.VPLocalNav.fixed[data-v-b979e4d9]{position:fixed}@media (min-width: 960px){.VPLocalNav[data-v-b979e4d9]{top:var(--vp-nav-height)}.VPLocalNav.has-sidebar[data-v-b979e4d9]{padding-left:var(--vp-sidebar-width)}.VPLocalNav.empty[data-v-b979e4d9]{display:none}}@media (min-width: 1280px){.VPLocalNav[data-v-b979e4d9]{display:none}}@media (min-width: 1440px){.VPLocalNav.has-sidebar[data-v-b979e4d9]{padding-left:calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width))}}.container[data-v-b979e4d9]{display:flex;justify-content:space-between;align-items:center}.menu[data-v-b979e4d9]{display:flex;align-items:center;padding:12px 24px 11px;line-height:24px;font-size:12px;font-weight:500;color:var(--vp-c-text-2);transition:color .5s}.menu[data-v-b979e4d9]:hover{color:var(--vp-c-text-1);transition:color .25s}@media (min-width: 768px){.menu[data-v-b979e4d9]{padding:0 32px}}@media (min-width: 960px){.menu[data-v-b979e4d9]{display:none}}.menu-icon[data-v-b979e4d9]{margin-right:8px;width:16px;height:16px;fill:currentColor}.VPOutlineDropdown[data-v-b979e4d9]{padding:12px 24px 11px}@media (min-width: 768px){.VPOutlineDropdown[data-v-b979e4d9]{padding:12px 32px 11px}}.VPSwitch[data-v-1c29e291]{position:relative;border-radius:11px;display:block;width:40px;height:22px;flex-shrink:0;border:1px solid var(--vp-input-border-color);background-color:var(--vp-input-switch-bg-color);transition:border-color .25s!important}.VPSwitch[data-v-1c29e291]:hover{border-color:var(--vp-c-brand-1)}.check[data-v-1c29e291]{position:absolute;top:1px;left:1px;width:18px;height:18px;border-radius:50%;background-color:var(--vp-c-neutral-inverse);box-shadow:var(--vp-shadow-1);transition:transform .25s!important}.icon[data-v-1c29e291]{position:relative;display:block;width:18px;height:18px;border-radius:50%;overflow:hidden}.icon[data-v-1c29e291] svg{position:absolute;top:3px;left:3px;width:12px;height:12px;fill:var(--vp-c-text-2)}.dark .icon[data-v-1c29e291] svg{fill:var(--vp-c-text-1);transition:opacity .25s!important}.sun[data-v-d80abb8e]{opacity:1}.moon[data-v-d80abb8e],.dark .sun[data-v-d80abb8e]{opacity:0}.dark .moon[data-v-d80abb8e]{opacity:1}.dark .VPSwitchAppearance[data-v-d80abb8e] .check{transform:translate(18px)}.VPNavBarAppearance[data-v-283b26e9]{display:none}@media (min-width: 1280px){.VPNavBarAppearance[data-v-283b26e9]{display:flex;align-items:center}}.VPMenuGroup+.VPMenuLink[data-v-f51f088d]{margin:12px -12px 0;border-top:1px solid var(--vp-c-divider);padding:12px 12px 0}.link[data-v-f51f088d]{display:block;border-radius:6px;padding:0 12px;line-height:32px;font-size:14px;font-weight:500;color:var(--vp-c-text-1);white-space:nowrap;transition:background-color .25s,color .25s}.link[data-v-f51f088d]:hover{color:var(--vp-c-brand-1);background-color:var(--vp-c-default-soft)}.link.active[data-v-f51f088d]{color:var(--vp-c-brand-1)}.VPMenuGroup[data-v-a6b0397c]{margin:12px -12px 0;border-top:1px solid var(--vp-c-divider);padding:12px 12px 0}.VPMenuGroup[data-v-a6b0397c]:first-child{margin-top:0;border-top:0;padding-top:0}.VPMenuGroup+.VPMenuGroup[data-v-a6b0397c]{margin-top:12px;border-top:1px solid var(--vp-c-divider)}.title[data-v-a6b0397c]{padding:0 12px;line-height:32px;font-size:14px;font-weight:600;color:var(--vp-c-text-2);white-space:nowrap;transition:color .25s}.VPMenu[data-v-e42ed9b3]{border-radius:12px;padding:12px;min-width:128px;border:1px solid var(--vp-c-divider);background-color:var(--vp-c-bg-elv);box-shadow:var(--vp-shadow-3);transition:background-color .5s;max-height:calc(100vh - var(--vp-nav-height));overflow-y:auto}.VPMenu[data-v-e42ed9b3] .group{margin:0 -12px;padding:0 12px 12px}.VPMenu[data-v-e42ed9b3] .group+.group{border-top:1px solid var(--vp-c-divider);padding:11px 12px 12px}.VPMenu[data-v-e42ed9b3] .group:last-child{padding-bottom:0}.VPMenu[data-v-e42ed9b3] .group+.item{border-top:1px solid var(--vp-c-divider);padding:11px 16px 0}.VPMenu[data-v-e42ed9b3] .item{padding:0 16px;white-space:nowrap}.VPMenu[data-v-e42ed9b3] .label{flex-grow:1;line-height:28px;font-size:12px;font-weight:500;color:var(--vp-c-text-2);transition:color .5s}.VPMenu[data-v-e42ed9b3] .action{padding-left:24px}.VPFlyout[data-v-aa8de344]{position:relative}.VPFlyout[data-v-aa8de344]:hover{color:var(--vp-c-brand-1);transition:color .25s}.VPFlyout:hover .text[data-v-aa8de344]{color:var(--vp-c-text-2)}.VPFlyout:hover .icon[data-v-aa8de344]{fill:var(--vp-c-text-2)}.VPFlyout.active .text[data-v-aa8de344]{color:var(--vp-c-brand-1)}.VPFlyout.active:hover .text[data-v-aa8de344]{color:var(--vp-c-brand-2)}.VPFlyout:hover .menu[data-v-aa8de344],.button[aria-expanded=true]+.menu[data-v-aa8de344]{opacity:1;visibility:visible;transform:translateY(0)}.button[aria-expanded=false]+.menu[data-v-aa8de344]{opacity:0;visibility:hidden;transform:translateY(0)}.button[data-v-aa8de344]{display:flex;align-items:center;padding:0 12px;height:var(--vp-nav-height);color:var(--vp-c-text-1);transition:color .5s}.text[data-v-aa8de344]{display:flex;align-items:center;line-height:var(--vp-nav-height);font-size:14px;font-weight:500;color:var(--vp-c-text-1);transition:color .25s}.option-icon[data-v-aa8de344]{margin-right:0;width:16px;height:16px;fill:currentColor}.text-icon[data-v-aa8de344]{margin-left:4px;width:14px;height:14px;fill:currentColor}.icon[data-v-aa8de344]{width:20px;height:20px;fill:currentColor;transition:fill .25s}.menu[data-v-aa8de344]{position:absolute;top:calc(var(--vp-nav-height) / 2 + 20px);right:0;opacity:0;visibility:hidden;transition:opacity .25s,visibility .25s,transform .25s}.VPSocialLink[data-v-16cf740a]{display:flex;justify-content:center;align-items:center;width:36px;height:36px;color:var(--vp-c-text-2);transition:color .5s}.VPSocialLink[data-v-16cf740a]:hover{color:var(--vp-c-text-1);transition:color .25s}.VPSocialLink[data-v-16cf740a]>svg{width:20px;height:20px;fill:currentColor}.VPSocialLinks[data-v-e71e869c]{display:flex;justify-content:center}.VPNavBarExtra[data-v-8e87c032]{display:none;margin-right:-12px}@media (min-width: 768px){.VPNavBarExtra[data-v-8e87c032]{display:block}}@media (min-width: 1280px){.VPNavBarExtra[data-v-8e87c032]{display:none}}.trans-title[data-v-8e87c032]{padding:0 24px 0 12px;line-height:32px;font-size:14px;font-weight:700;color:var(--vp-c-text-1)}.item.appearance[data-v-8e87c032],.item.social-links[data-v-8e87c032]{display:flex;align-items:center;padding:0 12px}.item.appearance[data-v-8e87c032]{min-width:176px}.appearance-action[data-v-8e87c032]{margin-right:-2px}.social-links-list[data-v-8e87c032]{margin:-4px -8px}.VPNavBarHamburger[data-v-6bee1efd]{display:flex;justify-content:center;align-items:center;width:48px;height:var(--vp-nav-height)}@media (min-width: 768px){.VPNavBarHamburger[data-v-6bee1efd]{display:none}}.container[data-v-6bee1efd]{position:relative;width:16px;height:14px;overflow:hidden}.VPNavBarHamburger:hover .top[data-v-6bee1efd]{top:0;left:0;transform:translate(4px)}.VPNavBarHamburger:hover .middle[data-v-6bee1efd]{top:6px;left:0;transform:translate(0)}.VPNavBarHamburger:hover .bottom[data-v-6bee1efd]{top:12px;left:0;transform:translate(8px)}.VPNavBarHamburger.active .top[data-v-6bee1efd]{top:6px;transform:translate(0) rotate(225deg)}.VPNavBarHamburger.active .middle[data-v-6bee1efd]{top:6px;transform:translate(16px)}.VPNavBarHamburger.active .bottom[data-v-6bee1efd]{top:6px;transform:translate(0) rotate(135deg)}.VPNavBarHamburger.active:hover .top[data-v-6bee1efd],.VPNavBarHamburger.active:hover .middle[data-v-6bee1efd],.VPNavBarHamburger.active:hover .bottom[data-v-6bee1efd]{background-color:var(--vp-c-text-2);transition:top .25s,background-color .25s,transform .25s}.top[data-v-6bee1efd],.middle[data-v-6bee1efd],.bottom[data-v-6bee1efd]{position:absolute;width:16px;height:2px;background-color:var(--vp-c-text-1);transition:top .25s,background-color .5s,transform .25s}.top[data-v-6bee1efd]{top:0;left:0;transform:translate(0)}.middle[data-v-6bee1efd]{top:6px;left:0;transform:translate(8px)}.bottom[data-v-6bee1efd]{top:12px;left:0;transform:translate(4px)}.VPNavBarMenuLink[data-v-cb318fec]{display:flex;align-items:center;padding:0 12px;line-height:var(--vp-nav-height);font-size:14px;font-weight:500;color:var(--vp-c-text-1);transition:color .25s}.VPNavBarMenuLink.active[data-v-cb318fec],.VPNavBarMenuLink[data-v-cb318fec]:hover{color:var(--vp-c-brand-1)}.VPNavBarMenu[data-v-f732b5d0]{display:none}@media (min-width: 768px){.VPNavBarMenu[data-v-f732b5d0]{display:flex}}/*! @docsearch/css 3.5.2 | MIT License | © Algolia, Inc. and contributors | https://docsearch.algolia.com */:root{--docsearch-primary-color:#5468ff;--docsearch-text-color:#1c1e21;--docsearch-spacing:12px;--docsearch-icon-stroke-width:1.4;--docsearch-highlight-color:var(--docsearch-primary-color);--docsearch-muted-color:#969faf;--docsearch-container-background:rgba(101,108,133,.8);--docsearch-logo-color:#5468ff;--docsearch-modal-width:560px;--docsearch-modal-height:600px;--docsearch-modal-background:#f5f6f7;--docsearch-modal-shadow:inset 1px 1px 0 0 hsla(0,0%,100%,.5),0 3px 8px 0 #555a64;--docsearch-searchbox-height:56px;--docsearch-searchbox-background:#ebedf0;--docsearch-searchbox-focus-background:#fff;--docsearch-searchbox-shadow:inset 0 0 0 2px var(--docsearch-primary-color);--docsearch-hit-height:56px;--docsearch-hit-color:#444950;--docsearch-hit-active-color:#fff;--docsearch-hit-background:#fff;--docsearch-hit-shadow:0 1px 3px 0 #d4d9e1;--docsearch-key-gradient:linear-gradient(-225deg,#d5dbe4,#f8f8f8);--docsearch-key-shadow:inset 0 -2px 0 0 #cdcde6,inset 0 0 1px 1px #fff,0 1px 2px 1px rgba(30,35,90,.4);--docsearch-footer-height:44px;--docsearch-footer-background:#fff;--docsearch-footer-shadow:0 -1px 0 0 #e0e3e8,0 -3px 6px 0 rgba(69,98,155,.12)}html[data-theme=dark]{--docsearch-text-color:#f5f6f7;--docsearch-container-background:rgba(9,10,17,.8);--docsearch-modal-background:#15172a;--docsearch-modal-shadow:inset 1px 1px 0 0 #2c2e40,0 3px 8px 0 #000309;--docsearch-searchbox-background:#090a11;--docsearch-searchbox-focus-background:#000;--docsearch-hit-color:#bec3c9;--docsearch-hit-shadow:none;--docsearch-hit-background:#090a11;--docsearch-key-gradient:linear-gradient(-26.5deg,#565872,#31355b);--docsearch-key-shadow:inset 0 -2px 0 0 #282d55,inset 0 0 1px 1px #51577d,0 2px 2px 0 rgba(3,4,9,.3);--docsearch-footer-background:#1e2136;--docsearch-footer-shadow:inset 0 1px 0 0 rgba(73,76,106,.5),0 -4px 8px 0 rgba(0,0,0,.2);--docsearch-logo-color:#fff;--docsearch-muted-color:#7f8497}.DocSearch-Button{align-items:center;background:var(--docsearch-searchbox-background);border:0;border-radius:40px;color:var(--docsearch-muted-color);cursor:pointer;display:flex;font-weight:500;height:36px;justify-content:space-between;margin:0 0 0 16px;padding:0 8px;-webkit-user-select:none;user-select:none}.DocSearch-Button:active,.DocSearch-Button:focus,.DocSearch-Button:hover{background:var(--docsearch-searchbox-focus-background);box-shadow:var(--docsearch-searchbox-shadow);color:var(--docsearch-text-color);outline:none}.DocSearch-Button-Container{align-items:center;display:flex}.DocSearch-Search-Icon{stroke-width:1.6}.DocSearch-Button .DocSearch-Search-Icon{color:var(--docsearch-text-color)}.DocSearch-Button-Placeholder{font-size:1rem;padding:0 12px 0 6px}.DocSearch-Button-Keys{display:flex;min-width:calc(40px + .8em)}.DocSearch-Button-Key{align-items:center;background:var(--docsearch-key-gradient);border-radius:3px;box-shadow:var(--docsearch-key-shadow);color:var(--docsearch-muted-color);display:flex;height:18px;justify-content:center;margin-right:.4em;position:relative;padding:0 0 2px;border:0;top:-1px;width:20px}@media (max-width:768px){.DocSearch-Button-Keys,.DocSearch-Button-Placeholder{display:none}}.DocSearch--active{overflow:hidden!important}.DocSearch-Container,.DocSearch-Container *{box-sizing:border-box}.DocSearch-Container{background-color:var(--docsearch-container-background);height:100vh;left:0;position:fixed;top:0;width:100vw;z-index:200}.DocSearch-Container a{text-decoration:none}.DocSearch-Link{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;font:inherit;margin:0;padding:0}.DocSearch-Modal{background:var(--docsearch-modal-background);border-radius:6px;box-shadow:var(--docsearch-modal-shadow);flex-direction:column;margin:60px auto auto;max-width:var(--docsearch-modal-width);position:relative}.DocSearch-SearchBar{display:flex;padding:var(--docsearch-spacing) var(--docsearch-spacing) 0}.DocSearch-Form{align-items:center;background:var(--docsearch-searchbox-focus-background);border-radius:4px;box-shadow:var(--docsearch-searchbox-shadow);display:flex;height:var(--docsearch-searchbox-height);margin:0;padding:0 var(--docsearch-spacing);position:relative;width:100%}.DocSearch-Input{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;color:var(--docsearch-text-color);flex:1;font:inherit;font-size:1.2em;height:100%;outline:none;padding:0 0 0 8px;width:80%}.DocSearch-Input::placeholder{color:var(--docsearch-muted-color);opacity:1}.DocSearch-Input::-webkit-search-cancel-button,.DocSearch-Input::-webkit-search-decoration,.DocSearch-Input::-webkit-search-results-button,.DocSearch-Input::-webkit-search-results-decoration{display:none}.DocSearch-LoadingIndicator,.DocSearch-MagnifierLabel,.DocSearch-Reset{margin:0;padding:0}.DocSearch-MagnifierLabel,.DocSearch-Reset{align-items:center;color:var(--docsearch-highlight-color);display:flex;justify-content:center}.DocSearch-Container--Stalled .DocSearch-MagnifierLabel,.DocSearch-LoadingIndicator{display:none}.DocSearch-Container--Stalled .DocSearch-LoadingIndicator{align-items:center;color:var(--docsearch-highlight-color);display:flex;justify-content:center}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Reset{animation:none;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;right:0;stroke-width:var(--docsearch-icon-stroke-width)}}.DocSearch-Reset{animation:fade-in .1s ease-in forwards;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;padding:2px;right:0;stroke-width:var(--docsearch-icon-stroke-width)}.DocSearch-Reset[hidden]{display:none}.DocSearch-Reset:hover{color:var(--docsearch-highlight-color)}.DocSearch-LoadingIndicator svg,.DocSearch-MagnifierLabel svg{height:24px;width:24px}.DocSearch-Cancel{display:none}.DocSearch-Dropdown{max-height:calc(var(--docsearch-modal-height) - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height));min-height:var(--docsearch-spacing);overflow-y:auto;overflow-y:overlay;padding:0 var(--docsearch-spacing);scrollbar-color:var(--docsearch-muted-color) var(--docsearch-modal-background);scrollbar-width:thin}.DocSearch-Dropdown::-webkit-scrollbar{width:12px}.DocSearch-Dropdown::-webkit-scrollbar-track{background:transparent}.DocSearch-Dropdown::-webkit-scrollbar-thumb{background-color:var(--docsearch-muted-color);border:3px solid var(--docsearch-modal-background);border-radius:20px}.DocSearch-Dropdown ul{list-style:none;margin:0;padding:0}.DocSearch-Label{font-size:.75em;line-height:1.6em}.DocSearch-Help,.DocSearch-Label{color:var(--docsearch-muted-color)}.DocSearch-Help{font-size:.9em;margin:0;-webkit-user-select:none;user-select:none}.DocSearch-Title{font-size:1.2em}.DocSearch-Logo a{display:flex}.DocSearch-Logo svg{color:var(--docsearch-logo-color);margin-left:8px}.DocSearch-Hits:last-of-type{margin-bottom:24px}.DocSearch-Hits mark{background:none;color:var(--docsearch-highlight-color)}.DocSearch-HitsFooter{color:var(--docsearch-muted-color);display:flex;font-size:.85em;justify-content:center;margin-bottom:var(--docsearch-spacing);padding:var(--docsearch-spacing)}.DocSearch-HitsFooter a{border-bottom:1px solid;color:inherit}.DocSearch-Hit{border-radius:4px;display:flex;padding-bottom:4px;position:relative}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit--deleting{transition:none}}.DocSearch-Hit--deleting{opacity:0;transition:all .25s linear}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit--favoriting{transition:none}}.DocSearch-Hit--favoriting{transform:scale(0);transform-origin:top center;transition:all .25s linear;transition-delay:.25s}.DocSearch-Hit a{background:var(--docsearch-hit-background);border-radius:4px;box-shadow:var(--docsearch-hit-shadow);display:block;padding-left:var(--docsearch-spacing);width:100%}.DocSearch-Hit-source{background:var(--docsearch-modal-background);color:var(--docsearch-highlight-color);font-size:.85em;font-weight:600;line-height:32px;margin:0 -4px;padding:8px 4px 0;position:sticky;top:0;z-index:10}.DocSearch-Hit-Tree{color:var(--docsearch-muted-color);height:var(--docsearch-hit-height);opacity:.5;stroke-width:var(--docsearch-icon-stroke-width);width:24px}.DocSearch-Hit[aria-selected=true] a{background-color:var(--docsearch-highlight-color)}.DocSearch-Hit[aria-selected=true] mark{text-decoration:underline}.DocSearch-Hit-Container{align-items:center;color:var(--docsearch-hit-color);display:flex;flex-direction:row;height:var(--docsearch-hit-height);padding:0 var(--docsearch-spacing) 0 0}.DocSearch-Hit-icon{height:20px;width:20px}.DocSearch-Hit-action,.DocSearch-Hit-icon{color:var(--docsearch-muted-color);stroke-width:var(--docsearch-icon-stroke-width)}.DocSearch-Hit-action{align-items:center;display:flex;height:22px;width:22px}.DocSearch-Hit-action svg{display:block;height:18px;width:18px}.DocSearch-Hit-action+.DocSearch-Hit-action{margin-left:6px}.DocSearch-Hit-action-button{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;border-radius:50%;color:inherit;cursor:pointer;padding:2px}svg.DocSearch-Hit-Select-Icon{display:none}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Select-Icon{display:block}.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:#0003;transition:background-color .1s ease-in}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{transition:none}}.DocSearch-Hit-action-button:focus path,.DocSearch-Hit-action-button:hover path{fill:#fff}.DocSearch-Hit-content-wrapper{display:flex;flex:1 1 auto;flex-direction:column;font-weight:500;justify-content:center;line-height:1.2em;margin:0 8px;overflow-x:hidden;position:relative;text-overflow:ellipsis;white-space:nowrap;width:80%}.DocSearch-Hit-title{font-size:.9em}.DocSearch-Hit-path{color:var(--docsearch-muted-color);font-size:.75em}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-action,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-icon,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-path,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-text,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-title,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Tree,.DocSearch-Hit[aria-selected=true] mark{color:var(--docsearch-hit-active-color)!important}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:#0003;transition:none}}.DocSearch-ErrorScreen,.DocSearch-NoResults,.DocSearch-StartScreen{font-size:.9em;margin:0 auto;padding:36px 0;text-align:center;width:80%}.DocSearch-Screen-Icon{color:var(--docsearch-muted-color);padding-bottom:12px}.DocSearch-NoResults-Prefill-List{display:inline-block;padding-bottom:24px;text-align:left}.DocSearch-NoResults-Prefill-List ul{display:inline-block;padding:8px 0 0}.DocSearch-NoResults-Prefill-List li{list-style-position:inside;list-style-type:"» "}.DocSearch-Prefill{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;border-radius:1em;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;font-size:1em;font-weight:700;padding:0}.DocSearch-Prefill:focus,.DocSearch-Prefill:hover{outline:none;text-decoration:underline}.DocSearch-Footer{align-items:center;background:var(--docsearch-footer-background);border-radius:0 0 8px 8px;box-shadow:var(--docsearch-footer-shadow);display:flex;flex-direction:row-reverse;flex-shrink:0;height:var(--docsearch-footer-height);justify-content:space-between;padding:0 var(--docsearch-spacing);position:relative;-webkit-user-select:none;user-select:none;width:100%;z-index:300}.DocSearch-Commands{color:var(--docsearch-muted-color);display:flex;list-style:none;margin:0;padding:0}.DocSearch-Commands li{align-items:center;display:flex}.DocSearch-Commands li:not(:last-of-type){margin-right:.8em}.DocSearch-Commands-Key{align-items:center;background:var(--docsearch-key-gradient);border-radius:2px;box-shadow:var(--docsearch-key-shadow);display:flex;height:18px;justify-content:center;margin-right:.4em;padding:0 0 1px;color:var(--docsearch-muted-color);border:0;width:20px}@media (max-width:768px){:root{--docsearch-spacing:10px;--docsearch-footer-height:40px}.DocSearch-Dropdown{height:100%}.DocSearch-Container{height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh, 1vh)*100);position:absolute}.DocSearch-Footer{border-radius:0;bottom:0;position:absolute}.DocSearch-Hit-content-wrapper{display:flex;position:relative;width:80%}.DocSearch-Modal{border-radius:0;box-shadow:none;height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh, 1vh)*100);margin:0;max-width:100%;width:100%}.DocSearch-Dropdown{max-height:calc(var(--docsearch-vh, 1vh)*100 - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height))}.DocSearch-Cancel{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;flex:none;font:inherit;font-size:1em;font-weight:500;margin-left:var(--docsearch-spacing);outline:none;overflow:hidden;padding:0;-webkit-user-select:none;user-select:none;white-space:nowrap}.DocSearch-Commands,.DocSearch-Hit-Tree{display:none}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}[class*=DocSearch]{--docsearch-primary-color: var(--vp-c-brand-1);--docsearch-highlight-color: var(--docsearch-primary-color);--docsearch-text-color: var(--vp-c-text-1);--docsearch-muted-color: var(--vp-c-text-2);--docsearch-searchbox-shadow: none;--docsearch-searchbox-background: transparent;--docsearch-searchbox-focus-background: transparent;--docsearch-key-gradient: transparent;--docsearch-key-shadow: none;--docsearch-modal-background: var(--vp-c-bg-soft);--docsearch-footer-background: var(--vp-c-bg)}.dark [class*=DocSearch]{--docsearch-modal-shadow: none;--docsearch-footer-shadow: none;--docsearch-logo-color: var(--vp-c-text-2);--docsearch-hit-background: var(--vp-c-default-soft);--docsearch-hit-color: var(--vp-c-text-2);--docsearch-hit-shadow: none}.DocSearch-Button{display:flex;justify-content:center;align-items:center;margin:0;padding:0;width:48px;height:55px;background:transparent;transition:border-color .25s}.DocSearch-Button:hover{background:transparent}.DocSearch-Button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}.DocSearch-Button:focus:not(:focus-visible){outline:none!important}@media (min-width: 768px){.DocSearch-Button{justify-content:flex-start;border:1px solid transparent;border-radius:8px;padding:0 10px 0 12px;width:100%;height:40px;background-color:var(--vp-c-bg-alt)}.DocSearch-Button:hover{border-color:var(--vp-c-brand-1);background:var(--vp-c-bg-alt)}}.DocSearch-Button .DocSearch-Button-Container{display:flex;align-items:center}.DocSearch-Button .DocSearch-Search-Icon{position:relative;width:16px;height:16px;color:var(--vp-c-text-1);fill:currentColor;transition:color .5s}.DocSearch-Button:hover .DocSearch-Search-Icon{color:var(--vp-c-text-1)}@media (min-width: 768px){.DocSearch-Button .DocSearch-Search-Icon{top:1px;margin-right:8px;width:14px;height:14px;color:var(--vp-c-text-2)}}.DocSearch-Button .DocSearch-Button-Placeholder{display:none;margin-top:2px;padding:0 16px 0 0;font-size:13px;font-weight:500;color:var(--vp-c-text-2);transition:color .5s}.DocSearch-Button:hover .DocSearch-Button-Placeholder{color:var(--vp-c-text-1)}@media (min-width: 768px){.DocSearch-Button .DocSearch-Button-Placeholder{display:inline-block}}.DocSearch-Button .DocSearch-Button-Keys{direction:ltr;display:none;min-width:auto}@media (min-width: 768px){.DocSearch-Button .DocSearch-Button-Keys{display:flex;align-items:center}}.DocSearch-Button .DocSearch-Button-Key{display:block;margin:2px 0 0;border:1px solid var(--vp-c-divider);border-right:none;border-radius:4px 0 0 4px;padding-left:6px;min-width:0;width:auto;height:22px;line-height:22px;font-family:var(--vp-font-family-base);font-size:12px;font-weight:500;transition:color .5s,border-color .5s}.DocSearch-Button .DocSearch-Button-Key+.DocSearch-Button-Key{border-right:1px solid var(--vp-c-divider);border-left:none;border-radius:0 4px 4px 0;padding-left:2px;padding-right:6px}.DocSearch-Button .DocSearch-Button-Key:first-child{font-size:0!important}.DocSearch-Button .DocSearch-Button-Key:first-child:after{content:"Ctrl";font-size:12px;letter-spacing:normal;color:var(--docsearch-muted-color)}.mac .DocSearch-Button .DocSearch-Button-Key:first-child:after{content:"⌘"}.DocSearch-Button .DocSearch-Button-Key:first-child>*{display:none}.VPNavBarSearch{display:flex;align-items:center}@media (min-width: 768px){.VPNavBarSearch{flex-grow:1;padding-left:24px}}@media (min-width: 960px){.VPNavBarSearch{padding-left:32px}}.dark .DocSearch-Footer{border-top:1px solid var(--vp-c-divider)}.DocSearch-Form{border:1px solid var(--vp-c-brand-1);background-color:var(--vp-c-white)}.dark .DocSearch-Form{background-color:var(--vp-c-default-soft)}.DocSearch-Screen-Icon>svg{margin:auto}.VPNavBarSocialLinks[data-v-ef6192dc]{display:none}@media (min-width: 1280px){.VPNavBarSocialLinks[data-v-ef6192dc]{display:flex;align-items:center}}.title[data-v-e4cade88]{display:flex;align-items:center;border-bottom:1px solid transparent;width:100%;height:var(--vp-nav-height);font-size:16px;font-weight:600;color:var(--vp-c-text-1);transition:opacity .25s}@media (min-width: 960px){.title[data-v-e4cade88]{flex-shrink:0}.VPNavBarTitle.has-sidebar .title[data-v-e4cade88]{border-bottom-color:var(--vp-c-divider)}}[data-v-e4cade88] .logo{margin-right:8px;height:var(--vp-nav-logo-height)}.VPNavBarTranslations[data-v-ff4524ae]{display:none}@media (min-width: 1280px){.VPNavBarTranslations[data-v-ff4524ae]{display:flex;align-items:center}}.title[data-v-ff4524ae]{padding:0 24px 0 12px;line-height:32px;font-size:14px;font-weight:700;color:var(--vp-c-text-1)}.VPNavBar[data-v-3efcd581]{position:relative;height:var(--vp-nav-height);pointer-events:none;white-space:nowrap;transition:background-color .5s}.VPNavBar.has-local-nav[data-v-3efcd581]{background-color:var(--vp-nav-bg-color)}@media (min-width: 960px){.VPNavBar.has-local-nav[data-v-3efcd581]{background-color:transparent}.VPNavBar[data-v-3efcd581]:not(.has-sidebar):not(.top){background-color:var(--vp-nav-bg-color)}}.wrapper[data-v-3efcd581]{padding:0 8px 0 24px}@media (min-width: 768px){.wrapper[data-v-3efcd581]{padding:0 32px}}@media (min-width: 960px){.VPNavBar.has-sidebar .wrapper[data-v-3efcd581]{padding:0}}.container[data-v-3efcd581]{display:flex;justify-content:space-between;margin:0 auto;max-width:calc(var(--vp-layout-max-width) - 64px);height:var(--vp-nav-height);pointer-events:none}.container>.title[data-v-3efcd581],.container>.content[data-v-3efcd581]{pointer-events:none}.container[data-v-3efcd581] *{pointer-events:auto}@media (min-width: 960px){.VPNavBar.has-sidebar .container[data-v-3efcd581]{max-width:100%}}.title[data-v-3efcd581]{flex-shrink:0;height:calc(var(--vp-nav-height) - 1px);transition:background-color .5s}@media (min-width: 960px){.VPNavBar.has-sidebar .title[data-v-3efcd581]{position:absolute;top:0;left:0;z-index:2;padding:0 32px;width:var(--vp-sidebar-width);height:var(--vp-nav-height);background-color:transparent}}@media (min-width: 1440px){.VPNavBar.has-sidebar .title[data-v-3efcd581]{padding-left:max(32px,calc((100% - (var(--vp-layout-max-width) - 64px)) / 2));width:calc((100% - (var(--vp-layout-max-width) - 64px)) / 2 + var(--vp-sidebar-width) - 32px)}}.content[data-v-3efcd581]{flex-grow:1}@media (min-width: 960px){.VPNavBar.has-sidebar .content[data-v-3efcd581]{position:relative;z-index:1;padding-right:32px;padding-left:var(--vp-sidebar-width)}}@media (min-width: 1440px){.VPNavBar.has-sidebar .content[data-v-3efcd581]{padding-right:calc((100vw - var(--vp-layout-max-width)) / 2 + 32px);padding-left:calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width))}}.content-body[data-v-3efcd581]{display:flex;justify-content:flex-end;align-items:center;height:var(--vp-nav-height);transition:background-color .5s}@media (min-width: 960px){.VPNavBar:not(.top) .content-body[data-v-3efcd581]{position:relative;background-color:var(--vp-nav-bg-color)}.VPNavBar:not(.has-sidebar):not(.top) .content-body[data-v-3efcd581]{background-color:transparent}}@media (max-width: 767px){.content-body[data-v-3efcd581]{column-gap:.5rem}}.menu+.translations[data-v-3efcd581]:before,.menu+.appearance[data-v-3efcd581]:before,.menu+.social-links[data-v-3efcd581]:before,.translations+.appearance[data-v-3efcd581]:before,.appearance+.social-links[data-v-3efcd581]:before{margin-right:8px;margin-left:8px;width:1px;height:24px;background-color:var(--vp-c-divider);content:""}.menu+.appearance[data-v-3efcd581]:before,.translations+.appearance[data-v-3efcd581]:before{margin-right:16px}.appearance+.social-links[data-v-3efcd581]:before{margin-left:16px}.social-links[data-v-3efcd581]{margin-right:-8px}.divider[data-v-3efcd581]{width:100%;height:1px}@media (min-width: 960px){.VPNavBar.has-sidebar .divider[data-v-3efcd581]{padding-left:var(--vp-sidebar-width)}}@media (min-width: 1440px){.VPNavBar.has-sidebar .divider[data-v-3efcd581]{padding-left:calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width))}}.divider-line[data-v-3efcd581]{width:100%;height:1px;transition:background-color .5s}.VPNavBar.has-local-nav .divider-line[data-v-3efcd581]{background-color:var(--vp-c-gutter)}@media (min-width: 960px){.VPNavBar:not(.top) .divider-line[data-v-3efcd581]{background-color:var(--vp-c-gutter)}.VPNavBar:not(.has-sidebar):not(.top) .divider[data-v-3efcd581]{background-color:var(--vp-c-gutter)}}.VPNavScreenAppearance[data-v-338d9b48]{display:flex;justify-content:space-between;align-items:center;border-radius:8px;padding:12px 14px 12px 16px;background-color:var(--vp-c-bg-soft)}.text[data-v-338d9b48]{line-height:24px;font-size:12px;font-weight:500;color:var(--vp-c-text-2)}.VPNavScreenMenuLink[data-v-fe523e3d]{display:block;border-bottom:1px solid var(--vp-c-divider);padding:12px 0 11px;line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-1);transition:border-color .25s,color .25s}.VPNavScreenMenuLink[data-v-fe523e3d]:hover{color:var(--vp-c-brand-1)}.VPNavScreenMenuGroupLink[data-v-aea78dd1]{display:block;margin-left:12px;line-height:32px;font-size:14px;font-weight:400;color:var(--vp-c-text-1);transition:color .25s}.VPNavScreenMenuGroupLink[data-v-aea78dd1]:hover{color:var(--vp-c-brand-1)}.VPNavScreenMenuGroupSection[data-v-f60dbfa7]{display:block}.title[data-v-f60dbfa7]{line-height:32px;font-size:13px;font-weight:700;color:var(--vp-c-text-2);transition:color .25s}.VPNavScreenMenuGroup[data-v-32e4a89c]{border-bottom:1px solid var(--vp-c-divider);height:48px;overflow:hidden;transition:border-color .5s}.VPNavScreenMenuGroup .items[data-v-32e4a89c]{visibility:hidden}.VPNavScreenMenuGroup.open .items[data-v-32e4a89c]{visibility:visible}.VPNavScreenMenuGroup.open[data-v-32e4a89c]{padding-bottom:10px;height:auto}.VPNavScreenMenuGroup.open .button[data-v-32e4a89c]{padding-bottom:6px;color:var(--vp-c-brand-1)}.VPNavScreenMenuGroup.open .button-icon[data-v-32e4a89c]{transform:rotate(45deg)}.button[data-v-32e4a89c]{display:flex;justify-content:space-between;align-items:center;padding:12px 4px 11px 0;width:100%;line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-1);transition:color .25s}.button[data-v-32e4a89c]:hover{color:var(--vp-c-brand-1)}.button-icon[data-v-32e4a89c]{width:14px;height:14px;fill:var(--vp-c-text-2);transition:fill .5s,transform .25s}.group[data-v-32e4a89c]:first-child{padding-top:0}.group+.group[data-v-32e4a89c],.group+.item[data-v-32e4a89c]{padding-top:4px}.VPNavScreenTranslations[data-v-41505286]{height:24px;overflow:hidden}.VPNavScreenTranslations.open[data-v-41505286]{height:auto}.title[data-v-41505286]{display:flex;align-items:center;font-size:14px;font-weight:500;color:var(--vp-c-text-1)}.icon[data-v-41505286]{width:16px;height:16px;fill:currentColor}.icon.lang[data-v-41505286]{margin-right:8px}.icon.chevron[data-v-41505286]{margin-left:4px}.list[data-v-41505286]{padding:4px 0 0 24px}.link[data-v-41505286]{line-height:32px;font-size:13px;color:var(--vp-c-text-1)}.VPNavScreen[data-v-57cce842]{position:fixed;top:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 1px);right:0;bottom:0;left:0;padding:0 32px;width:100%;background-color:var(--vp-nav-screen-bg-color);overflow-y:auto;transition:background-color .5s;pointer-events:auto}.VPNavScreen.fade-enter-active[data-v-57cce842],.VPNavScreen.fade-leave-active[data-v-57cce842]{transition:opacity .25s}.VPNavScreen.fade-enter-active .container[data-v-57cce842],.VPNavScreen.fade-leave-active .container[data-v-57cce842]{transition:transform .25s ease}.VPNavScreen.fade-enter-from[data-v-57cce842],.VPNavScreen.fade-leave-to[data-v-57cce842]{opacity:0}.VPNavScreen.fade-enter-from .container[data-v-57cce842],.VPNavScreen.fade-leave-to .container[data-v-57cce842]{transform:translateY(-8px)}@media (min-width: 768px){.VPNavScreen[data-v-57cce842]{display:none}}.container[data-v-57cce842]{margin:0 auto;padding:24px 0 96px;max-width:288px}.menu+.translations[data-v-57cce842],.menu+.appearance[data-v-57cce842],.translations+.appearance[data-v-57cce842]{margin-top:24px}.menu+.social-links[data-v-57cce842]{margin-top:16px}.appearance+.social-links[data-v-57cce842]{margin-top:16px}.VPNav[data-v-7ad780c2]{position:relative;top:var(--vp-layout-top-height, 0px);left:0;z-index:var(--vp-z-index-nav);width:100%;pointer-events:none;transition:background-color .5s}@media (min-width: 960px){.VPNav[data-v-7ad780c2]{position:fixed}}.VPSidebarItem.level-0[data-v-bd01e0d5]{padding-bottom:24px}.VPSidebarItem.collapsed.level-0[data-v-bd01e0d5]{padding-bottom:10px}.item[data-v-bd01e0d5]{position:relative;display:flex;width:100%}.VPSidebarItem.collapsible>.item[data-v-bd01e0d5]{cursor:pointer}.indicator[data-v-bd01e0d5]{position:absolute;top:6px;bottom:6px;left:-17px;width:2px;border-radius:2px;transition:background-color .25s}.VPSidebarItem.level-2.is-active>.item>.indicator[data-v-bd01e0d5],.VPSidebarItem.level-3.is-active>.item>.indicator[data-v-bd01e0d5],.VPSidebarItem.level-4.is-active>.item>.indicator[data-v-bd01e0d5],.VPSidebarItem.level-5.is-active>.item>.indicator[data-v-bd01e0d5]{background-color:var(--vp-c-brand-1)}.link[data-v-bd01e0d5]{display:flex;align-items:center;flex-grow:1}.text[data-v-bd01e0d5]{flex-grow:1;padding:4px 0;line-height:24px;font-size:14px;transition:color .25s}.VPSidebarItem.level-0 .text[data-v-bd01e0d5]{font-weight:700;color:var(--vp-c-text-1)}.VPSidebarItem.level-1 .text[data-v-bd01e0d5],.VPSidebarItem.level-2 .text[data-v-bd01e0d5],.VPSidebarItem.level-3 .text[data-v-bd01e0d5],.VPSidebarItem.level-4 .text[data-v-bd01e0d5],.VPSidebarItem.level-5 .text[data-v-bd01e0d5]{font-weight:500;color:var(--vp-c-text-2)}.VPSidebarItem.level-0.is-link>.item>.link:hover .text[data-v-bd01e0d5],.VPSidebarItem.level-1.is-link>.item>.link:hover .text[data-v-bd01e0d5],.VPSidebarItem.level-2.is-link>.item>.link:hover .text[data-v-bd01e0d5],.VPSidebarItem.level-3.is-link>.item>.link:hover .text[data-v-bd01e0d5],.VPSidebarItem.level-4.is-link>.item>.link:hover .text[data-v-bd01e0d5],.VPSidebarItem.level-5.is-link>.item>.link:hover .text[data-v-bd01e0d5]{color:var(--vp-c-brand-1)}.VPSidebarItem.level-0.has-active>.item>.text[data-v-bd01e0d5],.VPSidebarItem.level-1.has-active>.item>.text[data-v-bd01e0d5],.VPSidebarItem.level-2.has-active>.item>.text[data-v-bd01e0d5],.VPSidebarItem.level-3.has-active>.item>.text[data-v-bd01e0d5],.VPSidebarItem.level-4.has-active>.item>.text[data-v-bd01e0d5],.VPSidebarItem.level-5.has-active>.item>.text[data-v-bd01e0d5],.VPSidebarItem.level-0.has-active>.item>.link>.text[data-v-bd01e0d5],.VPSidebarItem.level-1.has-active>.item>.link>.text[data-v-bd01e0d5],.VPSidebarItem.level-2.has-active>.item>.link>.text[data-v-bd01e0d5],.VPSidebarItem.level-3.has-active>.item>.link>.text[data-v-bd01e0d5],.VPSidebarItem.level-4.has-active>.item>.link>.text[data-v-bd01e0d5],.VPSidebarItem.level-5.has-active>.item>.link>.text[data-v-bd01e0d5]{color:var(--vp-c-text-1)}.VPSidebarItem.level-0.is-active>.item .link>.text[data-v-bd01e0d5],.VPSidebarItem.level-1.is-active>.item .link>.text[data-v-bd01e0d5],.VPSidebarItem.level-2.is-active>.item .link>.text[data-v-bd01e0d5],.VPSidebarItem.level-3.is-active>.item .link>.text[data-v-bd01e0d5],.VPSidebarItem.level-4.is-active>.item .link>.text[data-v-bd01e0d5],.VPSidebarItem.level-5.is-active>.item .link>.text[data-v-bd01e0d5]{color:var(--vp-c-brand-1)}.caret[data-v-bd01e0d5]{display:flex;justify-content:center;align-items:center;margin-right:-7px;width:32px;height:32px;color:var(--vp-c-text-3);cursor:pointer;transition:color .25s;flex-shrink:0}.item:hover .caret[data-v-bd01e0d5]{color:var(--vp-c-text-2)}.item:hover .caret[data-v-bd01e0d5]:hover{color:var(--vp-c-text-1)}.caret-icon[data-v-bd01e0d5]{width:18px;height:18px;fill:currentColor;transform:rotate(90deg);transition:transform .25s}.VPSidebarItem.collapsed .caret-icon[data-v-bd01e0d5]{transform:rotate(0)}.VPSidebarItem.level-1 .items[data-v-bd01e0d5],.VPSidebarItem.level-2 .items[data-v-bd01e0d5],.VPSidebarItem.level-3 .items[data-v-bd01e0d5],.VPSidebarItem.level-4 .items[data-v-bd01e0d5],.VPSidebarItem.level-5 .items[data-v-bd01e0d5]{border-left:1px solid var(--vp-c-divider);padding-left:16px}.VPSidebarItem.collapsed .items[data-v-bd01e0d5]{display:none}.VPSidebar[data-v-4871f9f5]{position:fixed;top:var(--vp-layout-top-height, 0px);bottom:0;left:0;z-index:var(--vp-z-index-sidebar);padding:32px 32px 96px;width:calc(100vw - 64px);max-width:320px;background-color:var(--vp-sidebar-bg-color);opacity:0;box-shadow:var(--vp-c-shadow-3);overflow-x:hidden;overflow-y:auto;transform:translate(-100%);transition:opacity .5s,transform .25s ease;overscroll-behavior:contain}.VPSidebar.open[data-v-4871f9f5]{opacity:1;visibility:visible;transform:translate(0);transition:opacity .25s,transform .5s cubic-bezier(.19,1,.22,1)}.dark .VPSidebar[data-v-4871f9f5]{box-shadow:var(--vp-shadow-1)}@media (min-width: 960px){.VPSidebar[data-v-4871f9f5]{padding-top:var(--vp-nav-height);width:var(--vp-sidebar-width);max-width:100%;background-color:var(--vp-sidebar-bg-color);opacity:1;visibility:visible;box-shadow:none;transform:translate(0)}}@media (min-width: 1440px){.VPSidebar[data-v-4871f9f5]{padding-left:max(32px,calc((100% - (var(--vp-layout-max-width) - 64px)) / 2));width:calc((100% - (var(--vp-layout-max-width) - 64px)) / 2 + var(--vp-sidebar-width) - 32px)}}@media (min-width: 960px){.curtain[data-v-4871f9f5]{position:sticky;top:-64px;left:0;z-index:1;margin-top:calc(var(--vp-nav-height) * -1);margin-right:-32px;margin-left:-32px;height:var(--vp-nav-height);background-color:var(--vp-sidebar-bg-color)}}.nav[data-v-4871f9f5]{outline:0}.group+.group[data-v-4871f9f5]{border-top:1px solid var(--vp-c-divider);padding-top:10px}@media (min-width: 960px){.group[data-v-4871f9f5]{padding-top:10px;width:calc(var(--vp-sidebar-width) - 64px)}}.VPSkipLink[data-v-c8291ffa]{top:8px;left:8px;padding:8px 16px;z-index:999;border-radius:8px;font-size:12px;font-weight:700;text-decoration:none;color:var(--vp-c-brand-1);box-shadow:var(--vp-shadow-3);background-color:var(--vp-c-bg)}.VPSkipLink[data-v-c8291ffa]:focus{height:auto;width:auto;clip:auto;clip-path:none}@media (min-width: 1280px){.VPSkipLink[data-v-c8291ffa]{top:14px;left:16px}}.Layout[data-v-9d8abc1e]{display:flex;flex-direction:column;min-height:100vh}.VPHomeSponsors[data-v-843cc1b2]{border-top:1px solid var(--vp-c-gutter);padding:88px 24px 96px;background-color:var(--vp-c-bg)}.container[data-v-843cc1b2]{margin:0 auto;max-width:1152px}.love[data-v-843cc1b2]{margin:0 auto;width:28px;height:28px;color:var(--vp-c-text-3)}.icon[data-v-843cc1b2]{width:28px;height:28px;fill:currentColor}.message[data-v-843cc1b2]{margin:0 auto;padding-top:10px;max-width:320px;text-align:center;line-height:24px;font-size:16px;font-weight:500;color:var(--vp-c-text-2)}.sponsors[data-v-843cc1b2]{padding-top:32px}.action[data-v-843cc1b2]{padding-top:40px;text-align:center}.VPTeamPage[data-v-b1cfd8dc]{padding-bottom:96px}@media (min-width: 768px){.VPTeamPage[data-v-b1cfd8dc]{padding-bottom:128px}}.VPTeamPageSection+.VPTeamPageSection[data-v-b1cfd8dc-s],.VPTeamMembers+.VPTeamPageSection[data-v-b1cfd8dc-s]{margin-top:64px}.VPTeamMembers+.VPTeamMembers[data-v-b1cfd8dc-s]{margin-top:24px}@media (min-width: 768px){.VPTeamPageTitle+.VPTeamPageSection[data-v-b1cfd8dc-s]{margin-top:16px}.VPTeamPageSection+.VPTeamPageSection[data-v-b1cfd8dc-s],.VPTeamMembers+.VPTeamPageSection[data-v-b1cfd8dc-s]{margin-top:96px}}.VPTeamMembers[data-v-b1cfd8dc-s]{padding:0 24px}@media (min-width: 768px){.VPTeamMembers[data-v-b1cfd8dc-s]{padding:0 48px}}@media (min-width: 960px){.VPTeamMembers[data-v-b1cfd8dc-s]{padding:0 64px}}.VPTeamPageTitle[data-v-46c5e327]{padding:48px 32px;text-align:center}@media (min-width: 768px){.VPTeamPageTitle[data-v-46c5e327]{padding:64px 48px 48px}}@media (min-width: 960px){.VPTeamPageTitle[data-v-46c5e327]{padding:80px 64px 48px}}.title[data-v-46c5e327]{letter-spacing:0;line-height:44px;font-size:36px;font-weight:500}@media (min-width: 768px){.title[data-v-46c5e327]{letter-spacing:-.5px;line-height:56px;font-size:48px}}.lead[data-v-46c5e327]{margin:0 auto;max-width:512px;padding-top:12px;line-height:24px;font-size:16px;font-weight:500;color:var(--vp-c-text-2)}@media (min-width: 768px){.lead[data-v-46c5e327]{max-width:592px;letter-spacing:.15px;line-height:28px;font-size:20px}}.VPTeamPageSection[data-v-3bf2e850]{padding:0 32px}@media (min-width: 768px){.VPTeamPageSection[data-v-3bf2e850]{padding:0 48px}}@media (min-width: 960px){.VPTeamPageSection[data-v-3bf2e850]{padding:0 64px}}.title[data-v-3bf2e850]{position:relative;margin:0 auto;max-width:1152px;text-align:center;color:var(--vp-c-text-2)}.title-line[data-v-3bf2e850]{position:absolute;top:16px;left:0;width:100%;height:1px;background-color:var(--vp-c-divider)}.title-text[data-v-3bf2e850]{position:relative;display:inline-block;padding:0 24px;letter-spacing:0;line-height:32px;font-size:20px;font-weight:500;background-color:var(--vp-c-bg)}.lead[data-v-3bf2e850]{margin:0 auto;max-width:480px;padding-top:12px;text-align:center;line-height:24px;font-size:16px;font-weight:500;color:var(--vp-c-text-2)}.members[data-v-3bf2e850]{padding-top:40px}.VPTeamMembersItem[data-v-9d746f28]{display:flex;flex-direction:column;gap:2px;border-radius:12px;width:100%;height:100%;overflow:hidden}.VPTeamMembersItem.small .profile[data-v-9d746f28]{padding:32px}.VPTeamMembersItem.small .data[data-v-9d746f28]{padding-top:20px}.VPTeamMembersItem.small .avatar[data-v-9d746f28]{width:64px;height:64px}.VPTeamMembersItem.small .name[data-v-9d746f28]{line-height:24px;font-size:16px}.VPTeamMembersItem.small .affiliation[data-v-9d746f28]{padding-top:4px;line-height:20px;font-size:14px}.VPTeamMembersItem.small .desc[data-v-9d746f28]{padding-top:12px;line-height:20px;font-size:14px}.VPTeamMembersItem.small .links[data-v-9d746f28]{margin:0 -16px -20px;padding:10px 0 0}.VPTeamMembersItem.medium .profile[data-v-9d746f28]{padding:48px 32px}.VPTeamMembersItem.medium .data[data-v-9d746f28]{padding-top:24px;text-align:center}.VPTeamMembersItem.medium .avatar[data-v-9d746f28]{width:96px;height:96px}.VPTeamMembersItem.medium .name[data-v-9d746f28]{letter-spacing:.15px;line-height:28px;font-size:20px}.VPTeamMembersItem.medium .affiliation[data-v-9d746f28]{padding-top:4px;font-size:16px}.VPTeamMembersItem.medium .desc[data-v-9d746f28]{padding-top:16px;max-width:288px;font-size:16px}.VPTeamMembersItem.medium .links[data-v-9d746f28]{margin:0 -16px -12px;padding:16px 12px 0}.profile[data-v-9d746f28]{flex-grow:1;background-color:var(--vp-c-bg-soft)}.data[data-v-9d746f28]{text-align:center}.avatar[data-v-9d746f28]{position:relative;flex-shrink:0;margin:0 auto;border-radius:50%;box-shadow:var(--vp-shadow-3)}.avatar-img[data-v-9d746f28]{position:absolute;top:0;right:0;bottom:0;left:0;border-radius:50%;object-fit:cover}.name[data-v-9d746f28]{margin:0;font-weight:600}.affiliation[data-v-9d746f28]{margin:0;font-weight:500;color:var(--vp-c-text-2)}.org.link[data-v-9d746f28]{color:var(--vp-c-text-2);transition:color .25s}.org.link[data-v-9d746f28]:hover{color:var(--vp-c-brand-1)}.desc[data-v-9d746f28]{margin:0 auto}.desc[data-v-9d746f28] a{font-weight:500;color:var(--vp-c-brand-1);text-decoration-style:dotted;transition:color .25s}.links[data-v-9d746f28]{display:flex;justify-content:center;height:56px}.sp-link[data-v-9d746f28]{display:flex;justify-content:center;align-items:center;text-align:center;padding:16px;font-size:14px;font-weight:500;color:var(--vp-c-sponsor);background-color:var(--vp-c-bg-soft);transition:color .25s,background-color .25s}.sp .sp-link.link[data-v-9d746f28]:hover,.sp .sp-link.link[data-v-9d746f28]:focus{outline:none;color:var(--vp-c-white);background-color:var(--vp-c-sponsor)}.sp-icon[data-v-9d746f28]{margin-right:8px;width:16px;height:16px;fill:currentColor}.VPTeamMembers.small .container[data-v-bf782009]{grid-template-columns:repeat(auto-fit,minmax(224px,1fr))}.VPTeamMembers.small.count-1 .container[data-v-bf782009]{max-width:276px}.VPTeamMembers.small.count-2 .container[data-v-bf782009]{max-width:576px}.VPTeamMembers.small.count-3 .container[data-v-bf782009]{max-width:876px}.VPTeamMembers.medium .container[data-v-bf782009]{grid-template-columns:repeat(auto-fit,minmax(256px,1fr))}@media (min-width: 375px){.VPTeamMembers.medium .container[data-v-bf782009]{grid-template-columns:repeat(auto-fit,minmax(288px,1fr))}}.VPTeamMembers.medium.count-1 .container[data-v-bf782009]{max-width:368px}.VPTeamMembers.medium.count-2 .container[data-v-bf782009]{max-width:760px}.container[data-v-bf782009]{display:grid;gap:24px;margin:0 auto;max-width:1152px}:root{--vp-c-brand: #13537f;--vp-c-brand-light: #34739c;--vp-c-brand-lighter: #4599cf;--vp-c-brand-dark: #0f4971;--vp-c-brand-darker: #051825}.dark{--vp-c-brand: #397daa;--vp-c-brand-light: #4599d0;--vp-c-brand-lighter: #52b5f6;--vp-c-brand-dark: #275775;--vp-c-brand-darker: #1f445c} diff --git a/assets/system_httplib_README.md.ayRNZub-.js b/assets/system_httplib_README.md.ayRNZub-.js new file mode 100644 index 00000000000..884d037f277 --- /dev/null +++ b/assets/system_httplib_README.md.ayRNZub-.js @@ -0,0 +1,287 @@ +import{_ as s,c as i,o as a,V as t}from"./chunks/framework.gBlNPWt_.js";const o=JSON.parse('{"title":"cpp-httplib","description":"","frontmatter":{},"headers":[],"relativePath":"system/httplib/README.md","filePath":"system/httplib/README.md","lastUpdated":1721825996000}'),n={name:"system/httplib/README.md"},h=t(`

CMake files structure and usage