Salvage blue-wt-o11y-readers worktree

This commit is contained in:
hanzo-dev
2026-09-06 13:21:06 -07:00
parent c6950210e4
commit 83ecbec03c
63 changed files with 1632 additions and 603 deletions
+182 -182
View File
@@ -36,128 +36,128 @@
"author": "",
"license": "ISC",
"dependencies": {
"@ant-design/colors": "6.0.0",
"@ant-design/v5-patch-for-react-19": "1.0.3",
"@codemirror/autocomplete": "6.18.6",
"@codemirror/lang-javascript": "6.2.3",
"@codemirror/state": "6.5.2",
"@codemirror/view": "6.36.6",
"@dnd-kit/core": "6.1.0",
"@dnd-kit/modifiers": "7.0.0",
"@dnd-kit/sortable": "8.0.0",
"@dnd-kit/utilities": "3.2.2",
"@grafana/data": "^11.6.14",
"@hanzo/insights": "^1.357.1",
"@hanzo/ui": "^5.3.41",
"@monaco-editor/react": "^4.7.0",
"@radix-ui/react-avatar": "1.1.11",
"@radix-ui/react-checkbox": "1.3.3",
"@radix-ui/react-dialog": "1.1.15",
"@radix-ui/react-dropdown-menu": "2.1.16",
"@radix-ui/react-popover": "1.1.15",
"@radix-ui/react-progress": "1.1.8",
"@radix-ui/react-radio-group": "1.3.8",
"@radix-ui/react-select": "2.2.6",
"@radix-ui/react-slider": "1.3.6",
"@radix-ui/react-slot": "1.2.3",
"@radix-ui/react-switch": "1.2.6",
"@radix-ui/react-tabs": "1.1.13",
"@radix-ui/react-toggle-group": "1.1.11",
"@radix-ui/react-tooltip": "1.2.8",
"@sentry/react": "8.41.0",
"@sentry/vite-plugin": "2.22.6",
"@tanstack/react-table": "8.20.6",
"@tanstack/react-virtual": "3.11.2",
"@uiw/codemirror-theme-copilot": "4.23.11",
"@uiw/codemirror-theme-github": "4.24.1",
"@uiw/react-codemirror": "4.23.10",
"@visx/group": "4.0.0",
"@visx/hierarchy": "4.0.0",
"@visx/shape": "4.0.0",
"@visx/tooltip": "4.0.0",
"@visx/vendor": "4.0.0",
"@vitejs/plugin-react": "5.1.4",
"ansi-to-html": "0.7.2",
"antd": "5.29.3",
"antd-table-saveas-excel": "2.2.1",
"antlr4": "4.13.2",
"axios": "1.16.0",
"babel-jest": "^29.6.4",
"chart.js": "3.9.1",
"chartjs-adapter-date-fns": "^2.0.0",
"chartjs-plugin-annotation": "^1.4.0",
"class-variance-authority": "0.7.1",
"classnames": "2.3.2",
"clsx": "2.1.1",
"cmdk": "1.1.1",
"color": "^4.2.1",
"crypto-js": "4.2.0",
"d3-hierarchy": "3.1.2",
"dayjs": "^1.10.7",
"dompurify": "3.4.0",
"event-source-polyfill": "1.0.31",
"eventemitter3": "5.0.1",
"geist": "1.3.1",
"history": "4.10.1",
"http-proxy-middleware": "4.0.0",
"http-status-codes": "2.3.0",
"i18next": "^21.6.12",
"i18next-browser-languagedetector": "^6.1.3",
"i18next-http-backend": "^4.0.0",
"immer": "11.1.3",
"jest": "30.2.0",
"js-base64": "^3.7.2",
"lodash-es": "^4.17.21",
"lucide-react": "0.498.0",
"monaco-editor": "0.55.1",
"motion": "12.4.13",
"nuqs": "2.8.8",
"overlayscrollbars": "^2.8.1",
"overlayscrollbars-react": "^0.5.6",
"papaparse": "5.4.1",
"rc-tween-one": "3.0.6",
"react": "19.2.7",
"react-addons-update": "15.6.3",
"react-day-picker": "^9.0.0",
"react-dnd": "16.0.1",
"react-dnd-html5-backend": "16.0.1",
"react-dom": "19.2.7",
"react-drag-listview": "2.0.0",
"react-force-graph-2d": "^1.29.1",
"react-full-screen": "1.1.1",
"react-grid-layout": "^1.3.4",
"react-helmet-async": "3.0.0",
"react-hook-form": "7.71.2",
"react-i18next": "^11.16.1",
"react-json-tree": "^0.20.0",
"react-markdown": "9.0.3",
"react-query": "3.39.3",
"react-redux": "9.3.0",
"react-resizable-panels": "^4.0.0",
"react-rnd": "^10.5.3",
"react-router-dom": "^5.2.0",
"react-router-dom-v5-compat": "6.30.3",
"react-syntax-highlighter": "15.5.0",
"react-use": "^17.3.2",
"react-virtuoso": "4.0.3",
"redux": "^4.0.5",
"redux-thunk": "^2.3.0",
"rehype-raw": "7.0.0",
"remark-gfm": "^4.0.0",
"rollup-plugin-visualizer": "7.0.0",
"rrule": "2.8.1",
"sonner": "2.0.7",
"styled-components": "^5.3.11",
"tailwind-merge": "3.5.0",
"timestamp-nano": "^1.0.0",
"typescript": "5.9.3",
"uplot": "1.6.31",
"uuid": "^8.3.2",
"vaul": "1.1.2",
"@ant-design/colors": "catalog:",
"@ant-design/v5-patch-for-react-19": "catalog:",
"@codemirror/autocomplete": "catalog:",
"@codemirror/lang-javascript": "catalog:",
"@codemirror/state": "catalog:",
"@codemirror/view": "catalog:",
"@dnd-kit/core": "catalog:",
"@dnd-kit/modifiers": "catalog:",
"@dnd-kit/sortable": "catalog:",
"@dnd-kit/utilities": "catalog:",
"@grafana/data": "catalog:",
"@hanzo/insights": "catalog:",
"@hanzo/ui": "catalog:",
"@monaco-editor/react": "catalog:",
"@radix-ui/react-avatar": "catalog:",
"@radix-ui/react-checkbox": "catalog:",
"@radix-ui/react-dialog": "catalog:",
"@radix-ui/react-dropdown-menu": "catalog:",
"@radix-ui/react-popover": "catalog:",
"@radix-ui/react-progress": "catalog:",
"@radix-ui/react-radio-group": "catalog:",
"@radix-ui/react-select": "catalog:",
"@radix-ui/react-slider": "catalog:",
"@radix-ui/react-slot": "catalog:",
"@radix-ui/react-switch": "catalog:",
"@radix-ui/react-tabs": "catalog:",
"@radix-ui/react-toggle-group": "catalog:",
"@radix-ui/react-tooltip": "catalog:",
"@sentry/react": "catalog:",
"@sentry/vite-plugin": "catalog:",
"@tanstack/react-table": "catalog:",
"@tanstack/react-virtual": "catalog:",
"@uiw/codemirror-theme-copilot": "catalog:",
"@uiw/codemirror-theme-github": "catalog:",
"@uiw/react-codemirror": "catalog:",
"@visx/group": "catalog:",
"@visx/hierarchy": "catalog:",
"@visx/shape": "catalog:",
"@visx/tooltip": "catalog:",
"@visx/vendor": "catalog:",
"@vitejs/plugin-react": "catalog:",
"ansi-to-html": "catalog:",
"antd": "catalog:",
"antd-table-saveas-excel": "catalog:",
"antlr4": "catalog:",
"axios": "catalog:",
"babel-jest": "catalog:",
"chart.js": "catalog:",
"chartjs-adapter-date-fns": "catalog:",
"chartjs-plugin-annotation": "catalog:",
"class-variance-authority": "catalog:",
"classnames": "catalog:",
"clsx": "catalog:",
"cmdk": "catalog:",
"color": "catalog:",
"crypto-js": "catalog:",
"d3-hierarchy": "catalog:",
"dayjs": "catalog:",
"dompurify": "catalog:",
"event-source-polyfill": "catalog:",
"eventemitter3": "catalog:",
"geist": "catalog:",
"history": "catalog:",
"http-proxy-middleware": "catalog:",
"http-status-codes": "catalog:",
"i18next": "catalog:",
"i18next-browser-languagedetector": "catalog:",
"i18next-http-backend": "catalog:",
"immer": "catalog:",
"jest": "catalog:",
"js-base64": "catalog:",
"lodash-es": "catalog:",
"lucide-react": "catalog:",
"monaco-editor": "catalog:",
"motion": "catalog:",
"nuqs": "catalog:",
"overlayscrollbars": "catalog:",
"overlayscrollbars-react": "catalog:",
"papaparse": "catalog:",
"rc-tween-one": "catalog:",
"react": "catalog:",
"react-addons-update": "catalog:",
"react-day-picker": "catalog:",
"react-dnd": "catalog:",
"react-dnd-html5-backend": "catalog:",
"react-dom": "catalog:",
"react-drag-listview": "catalog:",
"react-force-graph-2d": "catalog:",
"react-full-screen": "catalog:",
"react-grid-layout": "catalog:",
"react-helmet-async": "catalog:",
"react-hook-form": "catalog:",
"react-i18next": "catalog:",
"react-json-tree": "catalog:",
"react-markdown": "catalog:",
"react-query": "catalog:",
"react-redux": "catalog:",
"react-resizable-panels": "catalog:",
"react-rnd": "catalog:",
"react-router-dom": "catalog:",
"react-router-dom-v5-compat": "catalog:",
"react-syntax-highlighter": "catalog:",
"react-use": "catalog:",
"react-virtuoso": "catalog:",
"redux": "catalog:",
"redux-thunk": "catalog:",
"rehype-raw": "catalog:",
"remark-gfm": "catalog:",
"rollup-plugin-visualizer": "catalog:",
"rrule": "catalog:",
"sonner": "catalog:",
"styled-components": "catalog:",
"tailwind-merge": "catalog:",
"timestamp-nano": "catalog:",
"typescript": "catalog:",
"uplot": "catalog:",
"uuid": "catalog:",
"vaul": "catalog:",
"vite": "npm:[email protected]",
"vite-plugin-html": "3.2.2",
"zod": "4.3.6",
"zustand": "5.0.11"
"vite-plugin-html": "catalog:",
"zod": "catalog:",
"zustand": "catalog:"
},
"browserslist": {
"production": [
@@ -172,67 +172,67 @@
]
},
"devDependencies": {
"@babel/core": "^7.22.11",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/preset-env": "^7.22.14",
"@babel/preset-react": "^7.12.13",
"@babel/preset-typescript": "^7.21.4",
"@commitlint/cli": "20.4.4",
"@commitlint/config-conventional": "20.4.4",
"@jest/globals": "30.4.1",
"@jest/types": "30.2.0",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/react": "16.3.2",
"@testing-library/user-event": "14.4.3",
"@types/color": "^3.0.3",
"@types/crypto-js": "4.2.2",
"@types/d3-hierarchy": "1.1.11",
"@types/event-source-polyfill": "^1.0.0",
"@types/history": "4.7.11",
"@types/jest": "30.0.0",
"@types/lodash-es": "^4.17.4",
"@types/node": "^16.10.3",
"@types/papaparse": "5.3.7",
"@types/react": "19.2.17",
"@types/react-addons-update": "0.14.21",
"@types/react-dom": "19.2.3",
"@types/react-grid-layout": "^1.1.2",
"@types/react-resizable": "3.0.3",
"@types/react-router-dom": "^5.1.6",
"@types/react-syntax-highlighter": "15.5.13",
"@types/redux-mock-store": "1.0.4",
"@types/styled-components": "^5.1.4",
"@types/testing-library__jest-dom": "^5.14.5",
"@types/uuid": "^8.3.1",
"@typescript/native-preview": "7.0.0-dev.20260430.1",
"eslint-plugin-sonarjs": "4.0.2",
"glob": "^13.0.6",
"husky": "^7.0.4",
"is-ci": "^3.0.1",
"jest-environment-jsdom": "29.7.0",
"jest-styled-components": "^7.2.0",
"json-schema-to-typescript": "^15.0.4",
"lint-staged": "^17.0.4",
"msw": "1.3.2",
"orval": "8.9.1",
"oxfmt": "0.47.0",
"oxlint": "1.62.0",
"oxlint-tsgolint": "0.22.1",
"postcss": "8.5.14",
"postcss-scss": "4.0.9",
"react-resizable": "3.0.4",
"redux-mock-store": "1.5.4",
"sass": "1.97.3",
"sharp": "0.34.5",
"stylelint": "17.7.0",
"svgo": "4.0.1",
"ts-jest": "29.4.9",
"typescript-plugin-css-modules": "5.2.0",
"use-sync-external-store": "1.6.0",
"vite-plugin-checker": "0.12.0",
"vite-plugin-compression": "0.5.1",
"vite-plugin-image-optimizer": "2.0.3",
"vite-tsconfig-paths": "6.1.1"
"@babel/core": "catalog:",
"@babel/plugin-proposal-class-properties": "catalog:",
"@babel/preset-env": "catalog:",
"@babel/preset-react": "catalog:",
"@babel/preset-typescript": "catalog:",
"@commitlint/cli": "catalog:",
"@commitlint/config-conventional": "catalog:",
"@jest/globals": "catalog:",
"@jest/types": "catalog:",
"@testing-library/jest-dom": "catalog:",
"@testing-library/react": "catalog:",
"@testing-library/user-event": "catalog:",
"@types/color": "catalog:",
"@types/crypto-js": "catalog:",
"@types/d3-hierarchy": "catalog:",
"@types/event-source-polyfill": "catalog:",
"@types/history": "catalog:",
"@types/jest": "catalog:",
"@types/lodash-es": "catalog:",
"@types/node": "catalog:",
"@types/papaparse": "catalog:",
"@types/react": "catalog:",
"@types/react-addons-update": "catalog:",
"@types/react-dom": "catalog:",
"@types/react-grid-layout": "catalog:",
"@types/react-resizable": "catalog:",
"@types/react-router-dom": "catalog:",
"@types/react-syntax-highlighter": "catalog:",
"@types/redux-mock-store": "catalog:",
"@types/styled-components": "catalog:",
"@types/testing-library__jest-dom": "catalog:",
"@types/uuid": "catalog:",
"@typescript/native-preview": "catalog:",
"eslint-plugin-sonarjs": "catalog:",
"glob": "catalog:",
"husky": "catalog:",
"is-ci": "catalog:",
"jest-environment-jsdom": "catalog:",
"jest-styled-components": "catalog:",
"json-schema-to-typescript": "catalog:",
"lint-staged": "catalog:",
"msw": "catalog:",
"orval": "catalog:",
"oxfmt": "catalog:",
"oxlint": "catalog:",
"oxlint-tsgolint": "catalog:",
"postcss": "catalog:",
"postcss-scss": "catalog:",
"react-resizable": "catalog:",
"redux-mock-store": "catalog:",
"sass": "catalog:",
"sharp": "catalog:",
"stylelint": "catalog:",
"svgo": "catalog:",
"ts-jest": "catalog:",
"typescript-plugin-css-modules": "catalog:",
"use-sync-external-store": "catalog:",
"vite-plugin-checker": "catalog:",
"vite-plugin-compression": "catalog:",
"vite-plugin-image-optimizer": "catalog:",
"vite-tsconfig-paths": "catalog:"
},
"lint-staged": {
"*.(js|jsx|ts|tsx)": [
+276
View File
@@ -4,3 +4,279 @@ minimumReleaseAge: 2880 # 2d
minimumReleaseAgeStrict: true
minimumReleaseAgeExclude:
blockExoticSubdeps: true
overrides:
'@noble/hashes': '1.8.0'
'zod': '^4.5.2'
'@hanzogui/adapt': '8.3.1'
'@hanzogui/animate': '8.3.1'
'@hanzogui/animate-presence': '8.3.1'
'@hanzogui/animations-css': '8.3.1'
'@hanzogui/animations-moti': '8.3.1'
'@hanzogui/animations-react-native': '8.3.1'
'@hanzogui/babel-plugin': '8.3.1'
'@hanzogui/build': '8.3.1'
'@hanzogui/button': '8.3.1'
'@hanzogui/checkbox-headless': '8.3.1'
'@hanzogui/cli': '8.3.1'
'@hanzogui/collapsible': '8.3.1'
'@hanzogui/collection': '8.3.1'
'@hanzogui/colors': '8.3.1'
'@hanzogui/component-helpers': '8.3.1'
'@hanzogui/compose-refs': '8.3.1'
'@hanzogui/config': '8.3.1'
'@hanzogui/config-default': '8.3.1'
'@hanzogui/constants': '8.3.1'
'@hanzogui/core': '8.3.1'
'@hanzogui/create-context': '8.3.1'
'@hanzogui/create-menu': '8.3.1'
'@hanzogui/dialog': '8.3.1'
'@hanzogui/dismissable': '8.3.1'
'@hanzogui/element': '8.3.1'
'@hanzogui/floating': '8.3.1'
'@hanzogui/focus-guard': '8.3.1'
'@hanzogui/focus-scope': '8.3.1'
'@hanzogui/focusable': '8.3.1'
'@hanzogui/font-size': '8.3.1'
'@hanzogui/get-button-sized': '8.3.1'
'@hanzogui/get-font-sized': '8.3.1'
'@hanzogui/get-token': '8.3.1'
'@hanzogui/group': '8.3.1'
'@hanzogui/helpers': '8.3.1'
'@hanzogui/helpers-icon': '8.3.1'
'@hanzogui/image': '8.3.1'
'@hanzogui/input': '8.3.1'
'@hanzogui/label': '8.3.1'
'@hanzogui/list-item': '8.3.1'
'@hanzogui/loader': '8.3.1'
'@hanzogui/lucide-icons-2': '8.3.1'
'@hanzogui/native': '8.3.1'
'@hanzogui/next-plugin': '8.3.1'
'@hanzogui/next-theme': '8.3.1'
'@hanzogui/polyfill-dev': '8.3.1'
'@hanzogui/popover': '8.3.1'
'@hanzogui/popper': '8.3.1'
'@hanzogui/portal': '8.3.1'
'@hanzogui/radio-headless': '8.3.1'
'@hanzogui/react-native-media-driver': '8.3.1'
'@hanzogui/react-native-svg': '8.3.1'
'@hanzogui/react-native-use-pressable': '8.3.1'
'@hanzogui/react-native-use-responder-events': '8.3.1'
'@hanzogui/react-native-web-lite': '8.3.1'
'@hanzogui/remove-scroll': '8.3.1'
'@hanzogui/roving-focus': '8.3.1'
'@hanzogui/scroll-view': '8.3.1'
'@hanzogui/separator': '8.3.1'
'@hanzogui/shapes': '8.3.1'
'@hanzogui/sheet': '8.3.1'
'@hanzogui/shell': '8.3.1'
'@hanzogui/sizable-context': '8.3.1'
'@hanzogui/spacer': '8.3.1'
'@hanzogui/stacks': '8.3.1'
'@hanzogui/start-transition': '8.3.1'
'@hanzogui/switch-headless': '8.3.1'
'@hanzogui/telemetry': '8.3.1'
'@hanzogui/text': '8.3.1'
'@hanzogui/theme': '8.3.1'
'@hanzogui/theme-base': '8.3.1'
'@hanzogui/themes': '8.3.1'
'@hanzogui/toast': '8.3.1'
'@hanzogui/use-async': '8.3.1'
'@hanzogui/use-callback-ref': '8.3.1'
'@hanzogui/use-constant': '8.3.1'
'@hanzogui/use-controllable-state': '8.3.1'
'@hanzogui/use-debounce': '8.3.1'
'@hanzogui/use-did-finish-ssr': '8.3.1'
'@hanzogui/use-direction': '8.3.1'
'@hanzogui/use-escape-keydown': '8.3.1'
'@hanzogui/use-event': '8.3.1'
'@hanzogui/use-force-update': '8.3.1'
'@hanzogui/use-keyboard-visible': '8.3.1'
'@hanzogui/use-presence': '8.3.1'
'@hanzogui/use-previous': '8.3.1'
'@hanzogui/visually-hidden': '8.3.1'
'@hanzogui/vite-plugin': '8.3.1'
'@hanzogui/web': '8.3.1'
'@hanzogui/z-index-stack': '8.3.1'
catalog:
'@ant-design/colors': '^8.0.1'
'@ant-design/v5-patch-for-react-19': '^1.0.3'
'@babel/core': '^8.0.1'
'@babel/plugin-proposal-class-properties': '^7.18.6'
'@babel/preset-env': '^8.0.2'
'@babel/preset-react': '^8.0.1'
'@babel/preset-typescript': '^8.0.1'
'@codemirror/autocomplete': '^6.20.3'
'@codemirror/lang-javascript': '^6.2.5'
'@codemirror/state': '^6.7.1'
'@codemirror/view': '^6.43.9'
'@commitlint/cli': '^21.2.2'
'@commitlint/config-conventional': '^21.2.2'
'@dnd-kit/core': '^6.3.1'
'@dnd-kit/modifiers': '^9.0.0'
'@dnd-kit/sortable': '^10.0.0'
'@dnd-kit/utilities': '^3.2.2'
'@grafana/data': '^13.2.0'
'@hanzo/insights': '1.358.1'
'@hanzo/ui': '8.27.2'
'@jest/globals': '^30.5.0'
'@jest/types': '^30.5.0'
'@monaco-editor/react': '^4.7.0'
'@radix-ui/react-avatar': '^1.2.6'
'@radix-ui/react-checkbox': '^1.3.11'
'@radix-ui/react-dialog': '^1.1.23'
'@radix-ui/react-dropdown-menu': '^2.1.24'
'@radix-ui/react-popover': '^1.1.23'
'@radix-ui/react-progress': '^1.1.16'
'@radix-ui/react-radio-group': '^1.4.7'
'@radix-ui/react-select': '^2.3.7'
'@radix-ui/react-slider': '^1.4.7'
'@radix-ui/react-slot': '^1.3.3'
'@radix-ui/react-switch': '^1.3.7'
'@radix-ui/react-tabs': '^1.1.21'
'@radix-ui/react-toggle-group': '^1.1.19'
'@radix-ui/react-tooltip': '^1.2.16'
'@sentry/react': '^10.72.0'
'@sentry/vite-plugin': '^5.4.0'
'@tanstack/react-table': '^9.2.4'
'@tanstack/react-virtual': '^3.14.10'
'@testing-library/jest-dom': '^7.0.1'
'@testing-library/react': '^16.3.3'
'@testing-library/user-event': '^14.6.6'
'@types/color': '^4.2.1'
'@types/crypto-js': '^4.2.2'
'@types/d3-hierarchy': '^3.1.7'
'@types/event-source-polyfill': '^1.0.5'
'@types/history': '^5.0.0'
'@types/jest': '^30.0.0'
'@types/lodash-es': '^4.17.12'
'@types/node': '26.4.0'
'@types/papaparse': '^5.5.2'
'@types/react': '19.2.18'
'@types/react-addons-update': '^0.14.24'
'@types/react-dom': '19.2.5'
'@types/react-grid-layout': '^2.1.0'
'@types/react-resizable': '^4.0.0'
'@types/react-router-dom': '^5.3.3'
'@types/react-syntax-highlighter': '^15.5.13'
'@types/redux-mock-store': '^1.5.0'
'@types/styled-components': '^5.1.36'
'@types/testing-library__jest-dom': '^6.0.0'
'@types/uuid': '^11.0.0'
'@typescript/native-preview': '^7.0.0-dev.20260707.2'
'@uiw/codemirror-theme-copilot': '^4.25.11'
'@uiw/codemirror-theme-github': '^4.25.11'
'@uiw/react-codemirror': '^4.25.11'
'@visx/group': '^4.0.0'
'@visx/hierarchy': '^4.0.0'
'@visx/shape': '^4.0.0'
'@visx/tooltip': '^4.0.0'
'@visx/vendor': '^4.0.0'
'@vitejs/plugin-react': '^6.0.2'
'ansi-to-html': '^0.7.2'
'antd': '^6.6.2'
'antd-table-saveas-excel': '^2.2.1'
'antlr4': '^4.13.2'
'axios': '^1.20.0'
'babel-jest': '^30.5.0'
'chart.js': '^4.5.1'
'chartjs-adapter-date-fns': '^3.0.0'
'chartjs-plugin-annotation': '^3.1.0'
'class-variance-authority': '^0.7.1'
'classnames': '^2.5.1'
'clsx': '^2.1.1'
'cmdk': '^1.1.1'
'color': '^5.0.3'
'crypto-js': '^4.2.0'
'd3-hierarchy': '^3.1.2'
'dayjs': '^1.11.23'
'dompurify': '^3.4.14'
'eslint-plugin-sonarjs': '^4.2.0'
'event-source-polyfill': '^1.0.31'
'eventemitter3': '^5.0.4'
'geist': '^1.7.2'
'glob': '^13.0.6'
'history': '^5.3.0'
'http-proxy-middleware': '^4.2.0'
'http-status-codes': '^2.3.0'
'husky': '^9.1.7'
'i18next': '^26.4.0'
'i18next-browser-languagedetector': '^8.2.1'
'i18next-http-backend': '^4.0.1'
'immer': '^11.1.18'
'is-ci': '^4.1.0'
'jest': '^30.5.0'
'jest-environment-jsdom': '^30.5.0'
'jest-styled-components': '^7.4.0'
'js-base64': '^3.9.3'
'json-schema-to-typescript': '^16.0.0'
'lint-staged': '^17.4.1'
'lodash-es': '^4.18.1'
'lucide-react': '^1.33.0'
'monaco-editor': '^0.56.0'
'motion': '^13.1.1'
'msw': '^2.15.0'
'nuqs': '^2.10.1'
'orval': '^8.27.0'
'overlayscrollbars': '^2.16.0'
'overlayscrollbars-react': '^0.5.6'
'oxfmt': '^0.65.0'
'oxlint': '^1.80.0'
'oxlint-tsgolint': '^7.0.2001'
'papaparse': '^5.7.0'
'postcss': '^8.5.26'
'postcss-scss': '^4.0.9'
'rc-tween-one': '^3.0.6'
'react': '19.2.8'
'react-addons-update': '^15.6.3'
'react-day-picker': '^10.0.1'
'react-dnd': '^16.0.1'
'react-dnd-html5-backend': '^16.0.1'
'react-dom': '19.2.8'
'react-drag-listview': '^2.0.0'
'react-force-graph-2d': '^1.29.1'
'react-full-screen': '^1.1.1'
'react-grid-layout': '^2.2.4'
'react-helmet-async': '^3.0.0'
'react-hook-form': '^7.86.0'
'react-i18next': '^17.0.12'
'react-json-tree': '^0.20.0'
'react-markdown': '^10.1.0'
'react-query': '^3.39.3'
'react-redux': '^9.3.0'
'react-resizable': '^4.0.2'
'react-resizable-panels': '^2.1.9'
'react-rnd': '^10.5.3'
'react-router-dom': '^7.18.3'
'react-router-dom-v5-compat': '^6.30.6'
'react-syntax-highlighter': '^16.1.1'
'react-use': '^17.6.1'
'react-virtuoso': '^4.18.12'
'redux': '^5.0.1'
'redux-mock-store': '^1.5.5'
'redux-thunk': '^3.1.0'
'rehype-raw': '^7.0.0'
'remark-gfm': '^4.0.1'
'rollup-plugin-visualizer': '^7.1.1'
'rrule': '^2.8.1'
'sass': '^1.103.1'
'sharp': '^0.35.4'
'sonner': '^2.0.8'
'styled-components': '^6.5.3'
'stylelint': '^17.14.1'
'svgo': '^4.1.0'
'tailwind-merge': '^3.6.0'
'timestamp-nano': '^1.0.1'
'ts-jest': '^29.4.12'
'typescript': '7.0.2'
'typescript-plugin-css-modules': '^5.2.0'
'uplot': '^1.6.32'
'use-sync-external-store': '^1.6.0'
'uuid': '^14.0.2'
'vaul': '^1.1.2'
'vite-plugin-checker': '^0.14.5'
'vite-plugin-compression': '^0.5.1'
'vite-plugin-html': '^3.2.2'
'vite-plugin-image-optimizer': '^2.0.3'
'vite-tsconfig-paths': '^6.1.1'
'zod': '^4.5.2'
'zustand': '^5.0.15'
@@ -4,12 +4,14 @@ import { QueryKey } from 'react-query';
import { useSelector } from 'react-redux';
import localStorageGet from 'api/browser/localstorage/get';
import localStorageSet from 'api/browser/localstorage/set';
import ErrorInPlace from 'components/ErrorInPlace/ErrorInPlace';
import Spinner from 'components/Spinner';
import { SKIP_ONBOARDING } from 'constants/onboarding';
import useGetTopLevelOperations from 'hooks/useGetTopLevelOperations';
import useResourceAttribute from 'hooks/useResourceAttribute';
import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
import { AppState } from 'store/reducers';
import APIError from 'types/api/error';
import { GlobalReducer } from 'types/reducer/globalTime';
import { Tags } from 'types/reducer/trace';
@@ -34,7 +36,7 @@ function ServicesUsingMetrics(): JSX.Element {
selectedTags,
globalSelectedInterval,
];
const { data, isLoading, isError } = useGetTopLevelOperations(queryKey, {
const { data, error, isLoading, isError } = useGetTopLevelOperations(queryKey, {
start: minTime,
end: maxTime,
});
@@ -50,14 +52,26 @@ function ServicesUsingMetrics(): JSX.Element {
const topLevelOperations = Object.entries(data || {});
if (isLoading === false && !skipOnboarding && isError === true) {
return <SkipOnBoardingModal onContinueClick={onContinueClick} />;
// A FAILED READ IS NOT AN EMPTY ONE — and this branch was worse than its
// sibling in ServiceTraces: it never looked at the data at all. `isError ===
// true` was the WHOLE condition, so the "instrument your application" modal
// was shown if and only if the request FAILED, and a genuinely empty
// deployment — the one case onboarding exists for — never saw it.
//
// Now: a failure shows the failure, a successful empty read offers
// onboarding, and neither is mistaken for the other.
if (isError) {
return <ErrorInPlace error={error as APIError} />;
}
if (isLoading) {
return <Spinner tip="Loading..." />;
}
if (topLevelOperations.length === 0 && !skipOnboarding) {
return <SkipOnBoardingModal onContinueClick={onContinueClick} />;
}
return <ServiceMetricsApplication topLevelOperations={topLevelOperations} />;
}
@@ -4,6 +4,7 @@ import { useSelector } from 'react-redux';
import localStorageGet from 'api/browser/localstorage/get';
import localStorageSet from 'api/browser/localstorage/set';
import logEvent from 'api/common/logEvent';
import ErrorInPlace from 'components/ErrorInPlace/ErrorInPlace';
import { SKIP_ONBOARDING } from 'constants/onboarding';
import useErrorNotification from 'hooks/useErrorNotification';
import { useQueryService } from 'hooks/useQueryService';
@@ -14,6 +15,7 @@ import {
} from 'hooks/useResourceAttribute/utils';
import { isUndefined } from 'lodash-es';
import { AppState } from 'store/reducers';
import APIError from 'types/api/error';
import { GlobalReducer } from 'types/reducer/globalTime';
import { Tags } from 'types/reducer/trace';
@@ -78,12 +80,23 @@ function ServiceTraces(): JSX.Element {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [data]);
if (
services.length === 0 &&
isLoading === false &&
!skipOnboarding &&
isError === true
) {
// A FAILED READ IS NOT AN EMPTY ONE.
//
// This branch used to require `isError === true` and then render the
// "instrument your application" onboarding modal — so the ONLY way to reach
// that modal was for the request to FAIL. When HIP-0132 dropped the database
// this list reads, every customer with a fully instrumented fleet was told
// their fleet was not instrumented, and the actual reason
// (`Code: 81 UNKNOWN_DATABASE`) appeared nowhere on the page.
//
// The two states are separate now and each says what is true: a failed read
// shows the failure, and only a SUCCESSFUL read that returned nothing offers
// onboarding.
if (isError) {
return <ErrorInPlace error={error as unknown as APIError} />;
}
if (services.length === 0 && isLoading === false && !skipOnboarding) {
return <SkipOnBoardingModal onContinueClick={onContinueClick} />;
}
+24 -6
View File
@@ -5,10 +5,28 @@ import (
"github.com/hanzoai/o11y/pkg/http/handler"
"github.com/hanzoai/o11y/pkg/http/routing"
"github.com/hanzoai/o11y/pkg/telemetrylogs"
"github.com/hanzoai/o11y/pkg/telemetryplane"
"github.com/hanzoai/o11y/pkg/telemetrytraces"
"github.com/hanzoai/o11y/pkg/types"
qbtypes "github.com/hanzoai/o11y/pkg/types/querybuildertypes/querybuildertypesv5"
)
// The Datastore-SQL request examples below are the ONLY documentation a customer
// has for writing raw SQL against the plane, and they are copied verbatim into
// real queries. They named o11y_traces and o11y_logs — databases HIP-0132 dropped
// — so every customer who followed the docs wrote a query that could only answer
// UNKNOWN_DATABASE. An example that teaches a dead name is worse than no example.
//
// They are composed from the same constants the readers use, so an example
// cannot drift from the plane again.
const (
exSpan = telemetryplane.DBName + "." + telemetrytraces.SpanTableName
exSpanResource = telemetryplane.DBName + "." + telemetrytraces.SpanResourceTableName
exLog = telemetryplane.DBName + "." + telemetrylogs.LogTableName
exLogResource = telemetryplane.DBName + "." + telemetrylogs.LogResourceTableName
)
// DUAL DISPATCH. These registrations stay: the standalone server reaches them
// directly, and the composed binary reaches the SAME handlers through the typed
// query-engine ops in the repo root's querycore.go (querierQueryRange,
@@ -374,10 +392,10 @@ func (provider *provider) addQuerierRoutes(router routing.Router) {
"spec": map[string]any{
"name": "span_rate",
"query": "WITH __resource_filter AS (" +
" SELECT fingerprint FROM o11y_traces.distributed_traces_v3_resource" +
" SELECT fingerprint FROM " + exSpanResource +
" WHERE seen_at_ts_bucket_start >= $start_timestamp - 1800 AND seen_at_ts_bucket_start <= $end_timestamp" +
" ) SELECT toStartOfInterval(timestamp, INTERVAL 60 SECOND) AS ts, count() AS value" +
" FROM o11y_traces.distributed_o11y_index_v3" +
" FROM " + exSpan +
" WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter)" +
" AND timestamp >= $start_datetime AND timestamp <= $end_datetime" +
" AND ts_bucket_start >= $start_timestamp - 1800 AND ts_bucket_start <= $end_timestamp" +
@@ -403,10 +421,10 @@ func (provider *provider) addQuerierRoutes(router routing.Router) {
"spec": map[string]any{
"name": "recent_errors",
"query": "WITH __resource_filter AS (" +
" SELECT fingerprint FROM o11y_logs.distributed_logs_v2_resource" +
" SELECT fingerprint FROM " + exLogResource +
" WHERE seen_at_ts_bucket_start >= $start_timestamp - 1800 AND seen_at_ts_bucket_start <= $end_timestamp" +
" ) SELECT timestamp, body" +
" FROM o11y_logs.distributed_logs_v2" +
" FROM " + exLog +
" WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter)" +
" AND timestamp >= $start_timestamp_nano AND timestamp <= $end_timestamp_nano" +
" AND ts_bucket_start >= $start_timestamp - 1800 AND ts_bucket_start <= $end_timestamp" +
@@ -433,10 +451,10 @@ func (provider *provider) addQuerierRoutes(router routing.Router) {
"spec": map[string]any{
"name": "total_spans",
"query": "WITH __resource_filter AS (" +
" SELECT fingerprint FROM o11y_traces.distributed_traces_v3_resource" +
" SELECT fingerprint FROM " + exSpanResource +
" WHERE seen_at_ts_bucket_start >= $start_timestamp - 1800 AND seen_at_ts_bucket_start <= $end_timestamp" +
" ) SELECT count() AS value" +
" FROM o11y_traces.distributed_o11y_index_v3" +
" FROM " + exSpan +
" WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter)" +
" AND timestamp >= $start_datetime AND timestamp <= $end_datetime" +
" AND ts_bucket_start >= $start_timestamp - 1800 AND ts_bucket_start <= $end_timestamp",
+1 -1
View File
@@ -16,7 +16,7 @@
//
// WHY THIS EXISTS. Before it, the log lane's only wired path was the embedded
// otelcol pipeline in hanzoai/cloud (datastorelogsexporter), which writes the
// OTLP-fork family (o11y_logs.distributed_logs_v2 + its four side tables) that
// OTLP-fork family (the retired o11y_logs.distributed_logs_v2 + its four side tables) that
// the event-plane readers do not read. This driver is the one log write path
// onto the envelope those readers query.
//
+1 -1
View File
@@ -17,7 +17,7 @@
// WHY THIS EXISTS. Before it, the span lane had NO event-plane writer: the only
// wired path was the embedded otelcol pipeline in hanzoai/cloud
// (datastoretracesexporter), which writes the OTLP-fork family
// (o11y_traces.o11y_index_v3) that the event-plane readers do not read. This
// (the retired o11y_traces.o11y_index_v3) that the event-plane readers do not read. This
// driver is the one span write path onto the envelope those readers query.
//
// IDEMPOTENT RE-INGEST. event.span is ReplacingMergeTree(ingested_at) with
@@ -8,7 +8,7 @@ import (
)
// OccurrenceSink is the OPTIONAL bridge that would also persist each occurrence to
// the shared telemetry store (o11y_logs as an ERROR-severity record) for OTel-native
// the shared telemetry store (event.log as an ERROR-severity record) for OTel-native
// drill-down. It is deliberately a seam, and the default is NoopSink:
//
// - Authoritative storage is o11y_issues — the grouped issue plus the latest
@@ -8,6 +8,7 @@ import (
"github.com/hanzoai/o11y/pkg/datastoresql"
"github.com/hanzo-ds/sqlbuilder"
"github.com/hanzoai/o11y/pkg/errors"
"github.com/hanzoai/o11y/pkg/flagger"
"github.com/hanzoai/o11y/pkg/querybuilder"
@@ -17,7 +18,6 @@ import (
qbtypes "github.com/hanzoai/o11y/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/hanzoai/o11y/pkg/types/telemetrytypes"
"github.com/hanzoai/o11y/pkg/valuer"
"github.com/hanzo-ds/sqlbuilder"
)
// quoteIdentifier wraps s in backticks for use as a Datastore identifier,
@@ -469,7 +469,7 @@ func (m *module) getEarliestMetricTime(ctx context.Context, metricNames []string
}
// getMetricsExistence returns, for each requested metric name, whether it has ever
// been reported (present in o11y_metrics.distributed_metadata). No time window.
// been reported (present in the metric-metadata table). No time window.
func (m *module) getMetricsExistence(ctx context.Context, metricNames []string) (map[string]bool, error) {
present := make(map[string]bool, len(metricNames))
for _, n := range metricNames {
@@ -9,17 +9,22 @@ import (
"github.com/hanzoai/o11y/pkg/datastoresql"
sqlbuilder "github.com/hanzo-ds/sqlbuilder"
"github.com/hanzoai/o11y/pkg/querybuilder"
"github.com/hanzoai/o11y/pkg/telemetryplane"
"github.com/hanzoai/o11y/pkg/telemetrystore"
qbtypes "github.com/hanzoai/o11y/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/hanzoai/o11y/pkg/types/rulestatehistorytypes"
"github.com/hanzoai/o11y/pkg/types/ruletypes"
"github.com/hanzoai/o11y/pkg/types/telemetrytypes"
sqlbuilder "github.com/hanzo-ds/sqlbuilder"
)
// o11y_analytics is NOT the event plane and must not be repointed at it:
// AddRuleStateHistory in this same file WRITES it, so the rows are there. It was
// spelled here AND in datastorereader, which is two places for one name; it is
// spelled once now, in pkg/telemetryplane, beside the plane it is not part of.
const (
o11yHistoryDBName = "o11y_analytics"
o11yHistoryDBName = telemetryplane.AnalyticsDBName
ruleStateHistoryTableName = "distributed_rule_state_history_v0"
)
+16 -6
View File
@@ -3,8 +3,18 @@ package tracefunnel
import (
"fmt"
"strings"
"github.com/hanzoai/o11y/pkg/telemetryplane"
"github.com/hanzoai/o11y/pkg/telemetrytraces"
)
// spanTable is the one table every funnel query reads. All six templates below
// spelled `o11y_traces.distributed_o11y_index_v3` — a database HIP-0132 dropped
// and a table whose successor is event.span — so every funnel answered
// `Code: 81 ... UNKNOWN_DATABASE`. Six copies of a name is six chances to miss
// one; it is spelled here and interpolated.
var spanTable = telemetryplane.DBName + "." + telemetrytraces.SpanTableName
// BuildFunnelValidationQuery builds a validation query for n-step funnels.
func BuildFunnelValidationQuery(
steps []struct {
@@ -57,7 +67,7 @@ SELECT
FROM (
SELECT
%s
FROM o11y_traces.distributed_o11y_index_v3
FROM ` + spanTable + `
WHERE
%s
GROUP BY trace_id
@@ -182,7 +192,7 @@ WITH
, funnel AS (
SELECT
%s
FROM o11y_traces.distributed_o11y_index_v3
FROM ` + spanTable + `
WHERE
%s
GROUP BY trace_id
@@ -297,7 +307,7 @@ SELECT
FROM (
SELECT
%s
FROM o11y_traces.distributed_o11y_index_v3
FROM ` + spanTable + `
WHERE
%s
GROUP BY trace_id
@@ -431,7 +441,7 @@ FROM (
FROM (
SELECT
%s
FROM o11y_traces.distributed_o11y_index_v3
FROM ` + spanTable + `
WHERE
%s
GROUP BY trace_id
@@ -506,7 +516,7 @@ FROM (
%[11]s AS t1_time,
%[12]s AS t2_time,
count() AS span_count
FROM o11y_traces.distributed_o11y_index_v3
FROM ` + spanTable + `
WHERE
timestamp BETWEEN start_ts AND end_ts
AND (
@@ -584,7 +594,7 @@ FROM (
toUInt8(anyIf(has_error, resource_string_service$$name = step1.1 AND name = step1.2)) AS t1_error,
toUInt8(anyIf(has_error, resource_string_service$$name = step2.1 AND name = step2.2)) AS t2_error,
count() AS span_count
FROM o11y_traces.distributed_o11y_index_v3
FROM ` + spanTable + `
WHERE
timestamp BETWEEN start_ts AND end_ts
AND (
+80
View File
@@ -0,0 +1,80 @@
package tracefunnel
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/hanzoai/o11y/pkg/telemetryplane"
"github.com/hanzoai/o11y/pkg/telemetrytraces"
)
// All six funnel queries named `o11y_traces.distributed_o11y_index_v3` — a
// database HIP-0132 dropped — and the tests in this package stayed green through
// all of it, because every one of them asserts on the WITH clause and the step
// predicates and none of them ever looked at the FROM.
//
// A test that checks everything except the one thing that breaks is not
// coverage. This checks the FROM.
func TestEveryFunnelQueryReadsThePlane(t *testing.T) {
type step2 = struct {
ServiceName string
SpanName string
ContainsError int
Clause string
}
type stepLatency = struct {
ServiceName string
SpanName string
ContainsError int
LatencyPointer string
Clause string
}
type stepLatencyTyped = struct {
ServiceName string
SpanName string
ContainsError int
LatencyPointer string
LatencyType string
Clause string
}
plain := []step2{
{ServiceName: "a", SpanName: "s1", ContainsError: 0, Clause: ""},
{ServiceName: "b", SpanName: "s2", ContainsError: 0, Clause: ""},
}
latency := []stepLatency{
{ServiceName: "a", SpanName: "s1", ContainsError: 0, LatencyPointer: "start", Clause: ""},
{ServiceName: "b", SpanName: "s2", ContainsError: 0, LatencyPointer: "start", Clause: ""},
}
latencyTyped := []stepLatencyTyped{
{ServiceName: "a", SpanName: "s1", ContainsError: 0, LatencyPointer: "start", LatencyType: "p99", Clause: ""},
{ServiceName: "b", SpanName: "s2", ContainsError: 0, LatencyPointer: "start", LatencyType: "p99", Clause: ""},
}
const start, end = int64(1700000000000000000), int64(1700003600000000000)
queries := map[string]string{
"validation": BuildFunnelValidationQuery(plain, start, end),
"overview": BuildFunnelOverviewQuery(latency, start, end),
"count": BuildFunnelCountQuery(plain, start, end),
"stepOverview": BuildFunnelStepOverviewQuery(latencyTyped, start, end, 1, 2),
"topSlowTraces": BuildFunnelTopSlowTracesQuery(0, 0, start, end, "a", "s1", "b", "s2", "", "", "start", "start"),
"topSlowErrTraces": BuildFunnelTopSlowErrorTracesQuery(0, 1, start, end, "a", "s1", "b", "s2", "", "", "start", "start"),
}
wantTable := telemetryplane.DBName + "." + telemetrytraces.SpanTableName
for name, sql := range queries {
t.Run(name, func(t *testing.T) {
require.NotEmpty(t, sql)
assert.Containsf(t, sql, wantTable,
"funnel query must read the event plane's span table, got:\n%s", sql)
for _, gone := range []string{"o11y_traces", "o11y_logs", "o11y_metrics"} {
assert.NotContainsf(t, sql, gone,
"funnel query names %s, a database HIP-0132 dropped", gone)
}
})
}
}
+1 -1
View File
@@ -149,7 +149,7 @@ func NewModules(
ruleStore := sqlrulestore.NewRuleStore(sqlstore, queryParser, providerSettings)
authDomainModule := implauthdomain.NewModule(implauthdomain.NewStore(sqlstore), authNs, authz)
// Error tracking (o11y_issues lifecycle) and trace detail (o11y_traces waterfall)
// Error tracking (issue lifecycle) and trace detail (the event.span waterfall)
// are pulled into locals so the Sentry product face can COMPOSE them rather than
// reconstruct them — one issue lifecycle, one trace read, two product faces.
errorTrackingModule := implerrortracking.NewModule(
+15 -6
View File
@@ -1,13 +1,22 @@
package datastoreprometheus
import "time"
import (
"time"
"github.com/hanzoai/o11y/pkg/telemetrymetrics"
"github.com/hanzoai/o11y/pkg/telemetryplane"
)
// The names the PromQL adapter reads by. o11y_metrics was dropped by HIP-0132,
// so every PromQL panel — the whole alerting evaluation path included — answered
// `Code: 81 ... UNKNOWN_DATABASE`. Aliases of pkg/telemetrymetrics/tables.go:
// time_series_v4 IS event.series and samples_v4 IS event.metric.
const (
databaseName string = "o11y_metrics"
distributedTimeSeriesV4 string = "distributed_time_series_v4"
distributedTimeSeriesV46hrs string = "distributed_time_series_v4_6hrs"
distributedTimeSeriesV41day string = "distributed_time_series_v4_1day"
distributedSamplesV4 string = "distributed_samples_v4"
databaseName string = telemetryplane.DBName
distributedTimeSeriesV4 string = telemetrymetrics.SeriesTableName
distributedTimeSeriesV46hrs string = telemetrymetrics.Series6hTableName
distributedTimeSeriesV41day string = telemetrymetrics.Series1dTableName
distributedSamplesV4 string = telemetrymetrics.MetricTableName
)
var (
+33 -3
View File
@@ -20,7 +20,11 @@ import (
"github.com/hanzoai/o11y/pkg/prometheus"
"github.com/hanzoai/o11y/pkg/query-service/utils"
"github.com/hanzoai/o11y/pkg/querybuilder"
"github.com/hanzoai/o11y/pkg/telemetrylogs"
"github.com/hanzoai/o11y/pkg/telemetrymetrics"
"github.com/hanzoai/o11y/pkg/telemetryplane"
"github.com/hanzoai/o11y/pkg/telemetrystore"
"github.com/hanzoai/o11y/pkg/telemetrytraces"
"github.com/hanzoai/o11y/pkg/types/ctxtypes"
"github.com/hanzoai/o11y/pkg/types/instrumentationtypes"
"github.com/hanzoai/o11y/pkg/types/metrictypes"
@@ -296,14 +300,40 @@ func (q *querier) populateQBEvent(event *qbtypes.QBEvent, queries []qbtypes.Quer
case qbtypes.QueryTypeDatastoreSQL:
sql := query.GetQuery()
if strings.TrimSpace(sql) != "" {
event.MetricsUsed = strings.Contains(sql, "o11y_metrics")
event.LogsUsed = strings.Contains(sql, "o11y_logs")
event.TracesUsed = strings.Contains(sql, "o11y_traces")
m, l, t := SignalsInSQL(sql)
event.MetricsUsed, event.LogsUsed, event.TracesUsed = m, l, t
}
}
}
}
// SignalsInSQL reports which signals a raw Datastore-SQL query reads.
//
// EXPORTED because query-service/app/query_info.go asked the same question with
// its own copy of the answer, and two copies of a discriminator is how one of
// them gets fixed. One function, two callers.
//
// IT USED TO ASK WHICH DATABASE THE SQL NAMED — o11y_metrics, o11y_logs,
// o11y_traces. HIP-0132 unified all three into `event`, so all three tests went
// permanently false and every raw-SQL query was recorded as touching NO signal.
// That is a silent defect: nothing errors, the usage analytics just quietly stop
// counting, and the number they feed is a number nobody can tell is wrong.
//
// With one database the discriminator is the TABLE, which is what it should have
// been all along — a database is where rows live, a table is what they are. The
// prefixes are deliberate: event.log also matches log_attribute and log_resource,
// event.span matches span_attribute and span_resource, event.metric matches
// metric_5m and event.series matches series_6h — all of which are that signal's
// support tables and SHOULD count as that signal.
func SignalsInSQL(sql string) (metrics, logs, traces bool) {
const qualify = telemetryplane.DBName + "."
metrics = strings.Contains(sql, qualify+telemetrymetrics.MetricTableName) ||
strings.Contains(sql, qualify+telemetrymetrics.SeriesTableName)
logs = strings.Contains(sql, qualify+telemetrylogs.LogTableName)
traces = strings.Contains(sql, qualify+telemetrytraces.SpanTableName)
return metrics, logs, traces
}
// resolveMetricMetadata fetches metadata for every metric referenced by builder
// metric-aggregation queries, patches each query's aggregations in place with
// the resolved values, and classifies any metric that could not be resolved.
+1 -1
View File
@@ -30,7 +30,7 @@ type mockMetricStmtBuilder struct{}
func (m *mockMetricStmtBuilder) Build(_ context.Context, _, _ uint64, _ qbtypes.RequestType, _ qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation], _ map[string]qbtypes.VariableItem) (*qbtypes.Statement, error) {
return &qbtypes.Statement{
Query: "SELECT ts, value FROM o11y_metrics",
Query: "SELECT ts, value FROM event.metric",
Args: nil,
}, nil
}
@@ -139,14 +139,14 @@ func (r *DatastoreReader) getValuesForLogAttributes(
select * from (
(
select tag_key, string_value, number_value
from o11y_logs.distributed_tag_attributes_v2
from event.log_attribute
where tag_key = $1 and (
string_value != '' or number_value is not null
)
limit 2
) UNION DISTINCT (
select tag_key, string_value, number_value
from o11y_logs.distributed_tag_attributes_v2
from event.log_attribute
where tag_key = $2 and (
string_value != '' or number_value is not null
)
@@ -154,7 +154,7 @@ func (r *DatastoreReader) getValuesForLogAttributes(
)
) settings max_threads=2
```
Since tag_attributes table uses ReplacingMergeTree, the values would be distinct and no order by
Since log_attribute uses ReplacingMergeTree, the values would be distinct and no order by
is being used to ensure the `limit` clause minimizes the amount of data scanned.
*/
@@ -4,6 +4,11 @@ import (
"time"
"github.com/hanzo-ds/go"
"github.com/hanzoai/o11y/pkg/telemetrylogs"
"github.com/hanzoai/o11y/pkg/telemetrymetadata"
"github.com/hanzoai/o11y/pkg/telemetryplane"
"github.com/hanzoai/o11y/pkg/telemetrytraces"
)
type Encoding string
@@ -15,42 +20,74 @@ const (
EncodingProto Encoding = "protobuf"
)
// The defaults this reader is constructed with.
//
// EVERY NAME IS AN ALIAS. Both databases this file used to spell — o11y_traces
// and o11y_logs — were dropped by HIP-0132 and unified into the event plane, so
// every default here named something that no longer existed. The spellings live
// in pkg/telemetryplane and the table names in pkg/telemetry{traces,logs}/
// tables.go, which is the canonical statement of the plane's shape; this file
// now only says WHICH of them each role takes.
//
// THE V2-ERA NAMES ARE NOT ALIASED, and the second block below is where they
// stay. operations, index_v2, error_index_v2, durationSort, usage_explorer,
// o11y_spans and dependency_graph_minutes_v2 were dropped WITH o11y_traces and
// the plane has no successor with their columns. Pointing one at an event-plane
// table would swap `UNKNOWN_TABLE`, which names the retired table and is true,
// for `THERE_IS_NO_COLUMN`, which names a live table and reads like the plane is
// broken. The engine's error is the honest reason and it names the exact table;
// this comment is what an operator finds when they grep for it.
const (
defaultTraceDB string = "o11y_traces"
defaultOperationsTable string = "distributed_o11y_operations"
defaultIndexTable string = "distributed_o11y_index_v2"
defaultLocalIndexTable string = "o11y_index_v2"
defaultErrorTable string = "distributed_o11y_error_index_v2"
defaultDurationTable string = "distributed_durationSort"
defaultUsageExplorerTable string = "distributed_usage_explorer"
defaultSpansTable string = "distributed_o11y_spans"
defaultDependencyGraphTable string = "distributed_dependency_graph_minutes_v2"
defaultTopLevelOperationsTable string = "distributed_top_level_operations"
defaultSpanAttributeTableV2 string = "distributed_tag_attributes_v2"
defaultSpanAttributeKeysTable string = "distributed_span_attributes_keys"
defaultLogsDB string = "o11y_logs"
defaultLogsTable string = "distributed_logs"
defaultLogsLocalTable string = "logs"
defaultLogAttributeKeysTable string = "distributed_logs_attribute_keys"
defaultLogResourceKeysTable string = "distributed_logs_resource_keys"
defaultLogTagAttributeTableV2 string = "distributed_tag_attributes_v2"
defaultTraceDB string = telemetryplane.DBName
defaultTopLevelOperationsTable string = telemetrytraces.OperationTableName
defaultSpanAttributeTableV2 string = telemetrytraces.SpanAttributeTableName
defaultSpanAttributeKeysTable string = telemetrytraces.SpanKeyTableName
defaultLogsDB string = telemetryplane.DBName
defaultLogsTable string = telemetrylogs.LogTableName
defaultLogsLocalTable string = telemetrylogs.LogLocalTableName
defaultLogAttributeKeysTable string = telemetrylogs.LogKeyTableName
defaultLogResourceKeysTable string = telemetrylogs.LogResourceKeyTableName
defaultLogTagAttributeTableV2 string = telemetrylogs.LogAttributeTableName
defaultLiveTailRefreshSeconds int = 5
defaultWriteBatchDelay time.Duration = 5 * time.Second
defaultWriteBatchSize int = 10000
defaultEncoding Encoding = EncodingJSON
defaultLogsLocalTableV2 string = "logs_v2"
defaultLogsTableV2 string = "distributed_logs_v2"
defaultLogsResourceLocalTableV2 string = "logs_v2_resource"
defaultLogsResourceTableV2 string = "distributed_logs_v2_resource"
defaultLogsLocalTableV2 string = telemetrylogs.LogLocalTableName
defaultLogsTableV2 string = telemetrylogs.LogTableName
defaultLogsResourceLocalTableV2 string = telemetrylogs.LogResourceTableName
defaultLogsResourceTableV2 string = telemetrylogs.LogResourceTableName
defaultTraceIndexTableV3 string = "distributed_o11y_index_v3"
defaultTraceLocalTableName string = "o11y_index_v3"
defaultTraceResourceTableV3 string = "distributed_traces_v3_resource"
defaultTraceSummaryTable string = "distributed_trace_summary"
defaultTraceIndexTableV3 string = telemetrytraces.SpanTableName
defaultTraceLocalTableName string = telemetrytraces.SpanLocalTableName
defaultTraceResourceTableV3 string = telemetrytraces.SpanResourceTableName
defaultTraceSummaryTable string = telemetrytraces.TraceTableName
defaultMetadataDB string = "o11y_metadata"
defaultMetadataTable string = "distributed_attributes_metadata"
defaultMetadataDB string = telemetrymetadata.DBName
defaultMetadataTable string = telemetrymetadata.AttributesMetadataTableName
)
// RETIRED — the v2-era tables of the old trace schema. Every one of them was
// dropped with o11y_traces and NONE of them has an event-plane successor that
// carries its columns: the exceptions pages read errorID/groupID/exceptionType,
// the service map reads a precomputed edge table, and the usage explorer reads a
// per-hour count. The event plane stores the SPANS those were derived from and
// nothing else.
//
// They are kept as literals, in one block, so that the read that reaches one
// fails with `Code: 60 UNKNOWN_TABLE: event.distributed_o11y_error_index_v2` —
// a message that names the retired table, which is the true reason. The
// alternative is a false empty, and a customer told "no errors" by a page that
// cannot see the table burns a day before opening a ticket.
const (
defaultOperationsTable string = "distributed_o11y_operations"
defaultIndexTable string = "distributed_o11y_index_v2"
defaultLocalIndexTable string = "o11y_index_v2"
defaultErrorTable string = "distributed_o11y_error_index_v2"
defaultDurationTable string = "distributed_durationSort"
defaultUsageExplorerTable string = "distributed_usage_explorer"
defaultSpansTable string = "distributed_o11y_spans"
defaultDependencyGraphTable string = "distributed_dependency_graph_minutes_v2"
)
// NamespaceConfig is Datastore's internal configuration data
+72 -39
View File
@@ -41,6 +41,10 @@ import (
"github.com/hanzo-ds/go/lib/driver"
"github.com/hanzoai/o11y/pkg/cache"
"github.com/hanzoai/o11y/pkg/telemetrymetadata"
"github.com/hanzoai/o11y/pkg/telemetrymeter"
"github.com/hanzoai/o11y/pkg/telemetrymetrics"
"github.com/hanzoai/o11y/pkg/telemetryplane"
"log/slog"
@@ -58,51 +62,79 @@ import (
"github.com/hanzoai/o11y/pkg/query-service/utils"
)
// The names this reader reads by.
//
// EVERY DATABASE NAME HERE IS AN ALIAS, NOT A SPELLING. This file used to spell
// five of them — o11y_traces, o11y_metrics, o11y_metadata, o11y_meter,
// o11y_analytics — three of which had been dropped by HIP-0132, which is why
// every read through this reader answered `Code: 81 ... UNKNOWN_DATABASE`.
// A name that is spelled in two files drifts; a name that is spelled once cannot.
// pkg/telemetryplane holds the spellings and plane_test.go enforces it.
//
// THE TABLE NAMES MOVED TOO, and they are aliases for the same reason. The event
// plane names a table for WHAT IT IS — span, log, metric, series — and drops the
// `distributed_` prefix (there are no Distributed wrappers on this deployment)
// and the `_v2`/`_v3`/`_v4` suffix (a version in a name is a second table waiting
// to happen). The mapping below is one-to-one and comes from
// pkg/telemetry{traces,logs,metrics}/tables.go, which is the canonical statement
// of the plane's shape.
//
// THE V2-ERA TABLES ARE GONE AND ARE NOT ALIASED. durationSort, usage_explorer,
// o11y_spans, o11y_error_index_v2, o11y_index_v2 and dependency_graph_minutes_v2
// were dropped WITH o11y_traces and the event plane has no successor for them.
// Their constants are DELETED rather than repointed: an alias to `event` would
// name a table that has never existed there, and the read would fail as if the
// plane were broken. Where they were enumerated for TTL they are removed from the
// list — one dead name in an ALTER list fails the TTL run for every live table
// beside it.
const (
primaryNamespace = "datastore"
archiveNamespace = "datastore-archive"
o11yTraceDBName = "o11y_traces"
o11yHistoryDBName = "o11y_analytics"
primaryNamespace = "datastore"
archiveNamespace = "datastore-archive"
// The one plane. Traces, logs and metrics all resolve here.
o11yTraceDBName = telemetryplane.DBName
o11yMetricDBName = telemetryplane.DBName
// Not the plane; each has a live writer. See pkg/telemetryplane.
o11yHistoryDBName = telemetryplane.AnalyticsDBName
o11yMetadataDbName = telemetrymetadata.DBName
o11yMeterDBName = telemetryplane.MeterDBName
ruleStateHistoryTableName = "distributed_rule_state_history_v0"
o11yDurationMVTable = "distributed_durationSort"
o11yUsageExplorerTable = "distributed_usage_explorer"
o11ySpansTable = "distributed_o11y_spans"
o11yErrorIndexTable = "distributed_o11y_error_index_v2"
o11yTraceTableName = "distributed_o11y_index_v2"
o11yTraceLocalTableName = "o11y_index_v2"
o11yMetricDBName = "o11y_metrics"
o11yMetadataDbName = "o11y_metadata"
o11yMeterDBName = "o11y_meter"
o11yMeterSamplesName = "samples_agg_1d"
o11ySampleLocalTableName = "samples_v4"
o11ySampleTableName = "distributed_samples_v4"
o11yMeterSamplesName = telemetrymeter.SamplesAgg1dLocalTableName
o11ySamplesAgg5mLocalTableName = "samples_v4_agg_5m"
o11ySamplesAgg5mTableName = "distributed_samples_v4_agg_5m"
// samples_v4 -> metric: a row is one sample of one metric.
o11ySampleLocalTableName = telemetrymetrics.MetricLocalTableName
o11ySampleTableName = telemetrymetrics.MetricTableName
o11ySamplesAgg30mLocalTableName = "samples_v4_agg_30m"
o11ySamplesAgg30mTableName = "distributed_samples_v4_agg_30m"
o11ySamplesAgg5mLocalTableName = telemetrymetrics.Metric5mLocalTableName
o11ySamplesAgg5mTableName = telemetrymetrics.Metric5mTableName
o11yExpHistLocalTableName = "exp_hist"
o11yExpHistTableName = "distributed_exp_hist"
o11ySamplesAgg30mLocalTableName = telemetrymetrics.Metric30mLocalTableName
o11ySamplesAgg30mTableName = telemetrymetrics.Metric30mTableName
o11yTSLocalTableNameV4 = "time_series_v4"
o11yTSTableNameV4 = "distributed_time_series_v4"
o11yExpHistLocalTableName = telemetrymetrics.HistogramLocalTableName
o11yExpHistTableName = telemetrymetrics.HistogramTableName
o11yTSLocalTableNameV46Hrs = "time_series_v4_6hrs"
o11yTSTableNameV46Hrs = "distributed_time_series_v4_6hrs"
// time_series_v4 -> series: a row is one series.
o11yTSLocalTableNameV4 = telemetrymetrics.SeriesLocalTableName
o11yTSTableNameV4 = telemetrymetrics.SeriesTableName
o11yTSLocalTableNameV41Day = "time_series_v4_1day"
o11yTSTableNameV41Day = "distributed_time_series_v4_1day"
o11yTSLocalTableNameV46Hrs = telemetrymetrics.Series6hLocalTableName
o11yTSTableNameV46Hrs = telemetrymetrics.Series6hTableName
o11yTSLocalTableNameV41Week = "time_series_v4_1week"
o11yTSTableNameV41Week = "distributed_time_series_v4_1week"
o11yTSLocalTableNameV41Day = telemetrymetrics.Series1dLocalTableName
o11yTSTableNameV41Day = telemetrymetrics.Series1dTableName
o11yTSTableNameV4Reduced = "distributed_time_series_v4_reduced"
o11yTSLocalTableNameV41Week = telemetrymetrics.Series1wLocalTableName
o11yTSTableNameV41Week = telemetrymetrics.Series1wTableName
o11yTableAttributesMetadata = "distributed_attributes_metadata"
o11yLocalTableAttributesMetadata = "attributes_metadata"
o11yTSTableNameV4Reduced = telemetrymetrics.SeriesReducedTableName
// The metadata pair stays in o11y_metadata with it.
o11yTableAttributesMetadata = telemetrymetadata.AttributesMetadataTableName
o11yLocalTableAttributesMetadata = telemetrymetadata.AttributesMetadataLocalTableName
o11yUpdatedMetricsMetadataLocalTable = "updated_metadata"
o11yUpdatedMetricsMetadataTable = "distributed_updated_metadata"
@@ -1103,12 +1135,14 @@ func (r *DatastoreReader) setTTLTraces(ctx context.Context, orgID string, params
// uuid is used as transaction id
uuidWithHyphen := uuid.New()
uuid := strings.Replace(uuidWithHyphen.String(), "-", "", -1)
// The trace plane's tables, and only the ones that exist. The three v2-era
// names that used to sit here — error_index_v2, usage_explorer and
// dependency_graph_minutes_v2 — went with o11y_traces. ALTER TABLE names one
// table per statement, so one dead name did not degrade this run, it FAILED
// it: setting retention on traces stopped working for every live table too.
tableNames := []string{
r.TraceDB + "." + r.traceTableName,
r.TraceDB + "." + r.traceResourceTableV3,
r.TraceDB + "." + o11yErrorIndexTable,
r.TraceDB + "." + o11yUsageExplorerTable,
r.TraceDB + "." + defaultDependencyGraphTable,
r.TraceDB + "." + r.traceSummaryTable,
r.TraceDB + "." + r.spanAttributesKeysTable,
}
@@ -2135,12 +2169,11 @@ func (r *DatastoreReader) GetTTL(ctx context.Context, orgID string, ttlParams *r
switch ttlParams.Type {
case retentiontypes.TraceTTL:
// Same list as setTTLTraces, and it has to stay the same list: this one
// READS the status of what that one WROTE.
tableNameArray := []string{
r.TraceDB + "." + r.traceTableName,
r.TraceDB + "." + r.traceResourceTableV3,
r.TraceDB + "." + o11yErrorIndexTable,
r.TraceDB + "." + o11yUsageExplorerTable,
r.TraceDB + "." + defaultDependencyGraphTable,
r.TraceDB + "." + r.traceSummaryTable,
}
tableNameArray = getLocalTableNameArray(tableNameArray)
+1 -1
View File
@@ -121,7 +121,7 @@ func TestPrepareQuery(t *testing.T) {
{
name: "query text produces template exec error",
postData: &model.DashboardVars{
Query: "SELECT durationNano from o11y_traces.o11y_index_v2 WHERE id = {{if .X}}1{{else}}2{{else}}3{{end}}",
Query: "SELECT durationNano from event.span WHERE id = {{if .X}}1{{else}}2{{else}}3{{end}}",
},
expectedErr: true,
errMsg: "expected end; found {{else}}",
@@ -2,8 +2,18 @@ package kafka
import (
"fmt"
"github.com/hanzoai/o11y/pkg/telemetryplane"
"github.com/hanzoai/o11y/pkg/telemetrytraces"
)
// spanTable is the one table these thirteen templates read. Every one of them
// spelled “ + spanTable + “ — a database HIP-0132 dropped
// and a table whose successor is event.span — so the Kafka/messaging views
// answered `Code: 81 ... UNKNOWN_DATABASE` on every panel. Thirteen copies of a
// name is thirteen chances to miss one; it is spelled here and interpolated.
var spanTable = telemetryplane.DBName + "." + telemetrytraces.SpanTableName
func generateConsumerSQL(start, end int64, topic, partition, consumerGroup, queueType string) string {
timeRange := (end - start) / 1000000000
tsBucketStart := (start / 1000000000) - 1800
@@ -16,7 +26,7 @@ WITH consumer_query AS (
COUNT(*) AS total_requests,
sumIf(1, status_code = 2) AS error_count,
avg(CASE WHEN has(attributes_number, 'messaging.message.body.size') THEN attributes_number['messaging.message.body.size'] ELSE NULL END) AS avg_msg_size
FROM o11y_traces.distributed_o11y_index_v3
FROM `+spanTable+`
WHERE
timestamp >= '%d'
AND timestamp <= '%d'
@@ -56,7 +66,7 @@ WITH partition_query AS (
count(*) AS total_requests,
attributes_string['messaging.destination.name'] AS topic,
attributes_string['messaging.destination.partition.id'] AS partition
FROM o11y_traces.distributed_o11y_index_v3
FROM `+spanTable+`
WHERE
timestamp >= '%d'
AND timestamp <= '%d'
@@ -93,7 +103,7 @@ WITH consumer_pl AS (
quantile(0.99)(durationNano) / 1000000 AS p99,
COUNT(*) AS total_requests,
sumIf(1, status_code = 2) AS error_count
FROM o11y_traces.distributed_o11y_index_v3
FROM `+spanTable+`
WHERE
timestamp >= '%d'
AND timestamp <= '%d'
@@ -133,7 +143,7 @@ WITH producer_latency AS (
attributes_string['messaging.destination.name'] AS topic,
COUNT(*) AS total_requests,
sumIf(1, status_code = 2) AS error_count
FROM o11y_traces.distributed_o11y_index_v3
FROM `+spanTable+`
WHERE
timestamp >= '%d'
AND timestamp <= '%d'
@@ -168,7 +178,7 @@ WITH consumer_latency AS (
attributes_string['messaging.destination.partition.id'] AS partition,
COUNT(*) AS total_requests,
sumIf(1, status_code = 2) AS error_count
FROM o11y_traces.distributed_o11y_index_v3
FROM `+spanTable+`
WHERE
timestamp >= '%d'
AND timestamp <= '%d'
@@ -206,7 +216,7 @@ WITH consumer_latency AS (
COUNT(*) AS total_requests,
sumIf(1, status_code = 2) AS error_count,
SUM(attributes_number['messaging.message.body.size']) AS total_bytes
FROM o11y_traces.distributed_o11y_index_v3
FROM `+spanTable+`
WHERE
timestamp >= '%d'
AND timestamp <= '%d'
@@ -244,7 +254,7 @@ WITH consumer_latency AS (
attributes_string['messaging.destination.partition.id'] AS partition,
COUNT(*) AS total_requests,
sumIf(1, status_code = 2) AS error_count
FROM o11y_traces.distributed_o11y_index_v3
FROM `+spanTable+`
WHERE
timestamp >= '%d'
AND timestamp <= '%d'
@@ -281,9 +291,9 @@ WITH trace_data AS (
p.durationNano AS durationNano,
(toUnixTimestamp64Nano(c.timestamp) - toUnixTimestamp64Nano(p.timestamp)) + p.durationNano AS time_difference
FROM
o11y_traces.distributed_o11y_index_v3 p
`+spanTable+` p
GLOBAL INNER JOIN
o11y_traces.distributed_o11y_index_v3 c
`+spanTable+` c
ON p.trace_id = c.trace_id
AND c.parent_span_id = p.span_id
WHERE
@@ -329,7 +339,7 @@ WITH producer_query AS (
quantile(0.99)(durationNano) / 1000000 AS p99,
count(*) AS total_count,
sumIf(1, status_code = 2) AS error_count
FROM o11y_traces.distributed_o11y_index_v3
FROM `+spanTable+`
WHERE
timestamp >= '%d'
AND timestamp <= '%d'
@@ -365,7 +375,7 @@ SELECT
resources_string['service.instance.id'] AS service_instance_id,
resource_string_service$$name AS service_name,
count(*) / %d AS throughput
FROM o11y_traces.distributed_o11y_index_v3
FROM `+spanTable+`
WHERE
timestamp >= '%d'
AND timestamp <= '%d'
@@ -392,7 +402,7 @@ SELECT
COUNT(IF(has(attributes_string, 'messaging.destination.name'), 1, NULL)) = 0 AS destination,
COUNT(IF(has(attributes_string, 'messaging.destination.partition.id'), 1, NULL)) = 0 AS partition
FROM
o11y_traces.distributed_o11y_index_v3
`+spanTable+`
WHERE
timestamp >= '%d'
AND timestamp <= '%d'
@@ -416,7 +426,7 @@ SELECT
COUNT(IF(has(attributes_number, 'messaging.message.body.size'), 1, NULL)) = 0 AS bodysize,
COUNT(IF(has(attributes_string, 'messaging.client_id'), 1, NULL)) = 0 AS clientid,
COUNT(IF(has(resources_string, 'service.instance.id'), 1, NULL)) = 0 AS instanceid
FROM o11y_traces.distributed_o11y_index_v3
FROM `+spanTable+`
WHERE
timestamp >= '%d'
AND timestamp <= '%d'
@@ -0,0 +1,56 @@
package kafka
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/hanzoai/o11y/pkg/telemetryplane"
"github.com/hanzoai/o11y/pkg/telemetrytraces"
)
// This package had NO test at all, and thirteen SQL templates in it named
// `o11y_traces` — a database HIP-0132 dropped. Every messaging/Kafka panel
// answered `Code: 81 UNKNOWN_DATABASE` and nothing in the tree noticed, because
// nothing in the tree ever looked at the SQL these functions produce.
//
// This is that look. It does not pin the whole query — a golden that pins 40
// lines of SQL fails on every unrelated edit and gets regenerated without being
// read. It pins the ONE thing that was wrong and is invisible until production:
// which database and table the read names.
func TestEveryGeneratedQueryNamesThePlane(t *testing.T) {
const start, end = int64(1700000000000000000), int64(1700003600000000000)
queries := map[string]string{
"consumer": generateConsumerSQL(start, end, "topic", "0", "group", "kafka"),
"partitionLatency": generatePartitionLatencySQL(start, end, "kafka"),
"consumerPartitionLatency": generateConsumerPartitionLatencySQL(start, end, "topic", "0", "kafka"),
"producerPartitionThroughput": generateProducerPartitionThroughputSQL(start, end, "kafka"),
"producerTopicLatency": generateProducerTopicLatencySQL(start, end, "topic", "svc", "kafka"),
"consumerLatency": generateConsumerLatencySQL(start, end, "kafka"),
"consumerServiceLatency": generateConsumerServiceLatencySQL(start, end, "topic", "svc", "kafka"),
"producerConsumerEval": generateProducerConsumerEvalSQL(start, end, "kafka", 100),
"producer": generateProducerSQL(start, end, "topic", "0", "kafka"),
"networkLatencyThroughput": generateNetworkLatencyThroughputSQL(start, end, "group", "0", "kafka"),
}
wantTable := telemetryplane.DBName + "." + telemetrytraces.SpanTableName
for name, sql := range queries {
t.Run(name, func(t *testing.T) {
require.NotEmpty(t, sql)
assert.Containsf(t, sql, wantTable,
"query must read the event plane's span table, got:\n%s", sql)
for _, gone := range []string{"o11y_traces", "o11y_logs", "o11y_metrics"} {
assert.NotContainsf(t, sql, gone,
"query names %s, a database HIP-0132 dropped", gone)
}
// A qualifier with no database is the other way to reach the wrong
// plane: it resolves against the connection's default database.
assert.Falsef(t, strings.Contains(sql, " FROM "+telemetrytraces.SpanTableName+" "),
"query reads an UNQUALIFIED table; it must name the plane explicitly")
})
}
}
@@ -6,8 +6,14 @@ import (
v3 "github.com/hanzoai/o11y/pkg/query-service/model/v3"
format "github.com/hanzoai/o11y/pkg/query-service/utils"
"github.com/hanzoai/o11y/pkg/telemetryplane"
"github.com/hanzoai/o11y/pkg/telemetrytraces"
)
// spanTable — see the kafka sibling. o11y_traces was dropped by HIP-0132;
// distributed_o11y_index_v3 IS event.span.
var spanTable = telemetryplane.DBName + "." + telemetrytraces.SpanTableName
// generateOverviewSQL builds the Datastore SQL query with optional filters.
// If a filter slice is empty, the query does not constrain on that field.
func generateOverviewSQL(start, end int64, item []v3.FilterItem) string {
@@ -65,7 +71,7 @@ WITH
) AS destination,
durationNano,
status_code
FROM o11y_traces.distributed_o11y_index_v3
FROM `+spanTable+`
WHERE
ts_bucket_start >= toDateTime64(%f, 9)
AND ts_bucket_start <= toDateTime64(%f, 9)
+10 -3
View File
@@ -7,8 +7,15 @@ import (
"github.com/hanzoai/o11y/pkg/query-service/constants"
v3 "github.com/hanzoai/o11y/pkg/query-service/model/v3"
"github.com/hanzoai/o11y/pkg/query-service/utils"
"github.com/hanzoai/o11y/pkg/telemetrylogs"
"github.com/hanzoai/o11y/pkg/telemetryplane"
)
// The v3 logs builder read o11y_logs.distributed_logs — a database HIP-0132
// dropped and a table whose successor is event.log. Spelled once, here, and
// resolved from pkg/telemetryplane like every other reader.
const logsFrom = telemetryplane.DBName + "." + telemetrylogs.LogTableName
var AggregateOperatorToPercentile = map[v3.AggregateOperator]float64{
v3.AggregateOperatorP05: 0.05,
v3.AggregateOperatorP10: 0.10,
@@ -284,7 +291,7 @@ func buildLogsQuery(panelType v3.PanelType, start, end, step int64, mq *v3.Build
queryTmpl =
queryTmpl + selectLabels +
" %s as value " +
"from o11y_logs.distributed_logs " +
"from " + logsFrom + " " +
"where " + timeFilter + "%s" +
"%s%s" +
"%s"
@@ -356,7 +363,7 @@ func buildLogsQuery(panelType v3.PanelType, start, end, step int64, mq *v3.Build
query := fmt.Sprintf(queryTmpl, op, filterSubQuery, groupBy, having, orderBy)
return query, nil
case v3.AggregateOperatorNoOp:
queryTmpl := constants.LogsSQLSelect + "from o11y_logs.distributed_logs where %s%s order by %s"
queryTmpl := constants.LogsSQLSelect + "from " + logsFrom + " where %s%s order by %s"
query := fmt.Sprintf(queryTmpl, timeFilter, filterSubQuery, orderBy)
return query, nil
default:
@@ -372,7 +379,7 @@ func buildLogsLiveTailQuery(mq *v3.BuilderQuery) (string, error) {
switch mq.AggregateOperator {
case v3.AggregateOperatorNoOp:
query := constants.LogsSQLSelect + "from o11y_logs.distributed_logs where "
query := constants.LogsSQLSelect + "from " + logsFrom + " where "
if len(filterSubQuery) > 0 {
query = query + filterSubQuery + " AND "
}
@@ -365,7 +365,7 @@ var testBuildLogsQueryData = []struct {
Expression: "A",
},
TableName: "logs",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, toFloat64(count(*)) as value from o11y_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) group by ts order by value DESC",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, toFloat64(count(*)) as value from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) group by ts order by value DESC",
},
{
Name: "Test aggregate count on a attribute",
@@ -380,7 +380,7 @@ var testBuildLogsQueryData = []struct {
Expression: "A",
},
TableName: "logs",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, toFloat64(count(*)) as value from o11y_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND has(attributes_string_key, 'user_name') group by ts order by value DESC",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, toFloat64(count(*)) as value from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND has(attributes_string_key, 'user_name') group by ts order by value DESC",
},
{
Name: "Test aggregate count on a with filter",
@@ -398,7 +398,7 @@ var testBuildLogsQueryData = []struct {
Expression: "A",
},
TableName: "logs",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, toFloat64(count(*)) as value from o11y_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND attributes_float64_value[indexOf(attributes_float64_key, 'bytes')] > 100.000000 AND has(attributes_string_key, 'user_name') group by ts order by value DESC",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, toFloat64(count(*)) as value from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND attributes_float64_value[indexOf(attributes_float64_key, 'bytes')] > 100.000000 AND has(attributes_string_key, 'user_name') group by ts order by value DESC",
},
{
Name: "Test aggregate count distinct and order by value",
@@ -414,7 +414,7 @@ var testBuildLogsQueryData = []struct {
OrderBy: []v3.OrderBy{{ColumnName: "#O11Y_VALUE", Order: "ASC"}},
},
TableName: "logs",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, toFloat64(count(distinct(`attribute_string_name`))) as value from o11y_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND `attribute_string_name_exists`=true group by ts order by value ASC",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, toFloat64(count(distinct(`attribute_string_name`))) as value from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND `attribute_string_name_exists`=true group by ts order by value ASC",
},
{
Name: "Test aggregate count distinct on non selected field",
@@ -429,7 +429,7 @@ var testBuildLogsQueryData = []struct {
Expression: "A",
},
TableName: "logs",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from o11y_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND has(attributes_string_key, 'name') group by ts order by value DESC",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND has(attributes_string_key, 'name') group by ts order by value DESC",
},
{
Name: "Test aggregate count distinct on non selected field containing dot",
@@ -447,7 +447,7 @@ var testBuildLogsQueryData = []struct {
},
TableName: "logs",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, attributes_string_value[indexOf(attributes_string_key, 'host.name')] as `host.name`, " +
"toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'method.name')]))) as value from o11y_logs.distributed_logs " +
"toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'method.name')]))) as value from event.log " +
"where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND has(attributes_string_key, 'host.name') AND has(attributes_string_key, 'method.name') " +
"group by `host.name`,ts order by `host.name` ASC",
},
@@ -468,7 +468,7 @@ var testBuildLogsQueryData = []struct {
TableName: "logs",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, `attribute_string_host$$name` as `host.name`, toFloat64(count(distinct(`attribute_string_method$$name`))) as value" +
" from o11y_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) " +
" from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) " +
"AND `attribute_string_host$$name_exists`=true AND `attribute_string_method$$name_exists`=true " +
"group by `host.name`,ts " +
"order by `host.name` ASC",
@@ -495,7 +495,7 @@ var testBuildLogsQueryData = []struct {
TableName: "logs",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts," +
" attributes_string_value[indexOf(attributes_string_key, 'method')] as `method`, " +
"toFloat64(count(distinct(`attribute_string_name`))) as value from o11y_logs.distributed_logs " +
"toFloat64(count(distinct(`attribute_string_name`))) as value from event.log " +
"where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) " +
"AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' AND resources_string_value[indexOf(resources_string_key, 'x')] != 'abc' " +
"AND has(attributes_string_key, 'method') " +
@@ -526,7 +526,7 @@ var testBuildLogsQueryData = []struct {
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts," +
" attributes_string_value[indexOf(attributes_string_key, 'method')] as `method`, " +
"resources_string_value[indexOf(resources_string_key, 'x')] as `x`, " +
"toFloat64(count(distinct(`attribute_string_name`))) as value from o11y_logs.distributed_logs " +
"toFloat64(count(distinct(`attribute_string_name`))) as value from event.log " +
"where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) " +
"AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' AND resources_string_value[indexOf(resources_string_key, 'x')] != 'abc' " +
"AND has(attributes_string_key, 'method') " +
@@ -557,7 +557,7 @@ var testBuildLogsQueryData = []struct {
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts," +
" attributes_string_value[indexOf(attributes_string_key, 'method')] as `method`, " +
"avg(attributes_float64_value[indexOf(attributes_float64_key, 'bytes')]) as value " +
"from o11y_logs.distributed_logs " +
"from event.log " +
"where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) " +
"AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' " +
"AND has(attributes_string_key, 'method') " +
@@ -587,7 +587,7 @@ var testBuildLogsQueryData = []struct {
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts," +
" attributes_string_value[indexOf(attributes_string_key, 'method')] as `method`, " +
"sum(`attribute_float64_bytes`) as value " +
"from o11y_logs.distributed_logs " +
"from event.log " +
"where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) " +
"AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' " +
"AND has(attributes_string_key, 'method') " +
@@ -617,7 +617,7 @@ var testBuildLogsQueryData = []struct {
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts," +
" attributes_string_value[indexOf(attributes_string_key, 'method')] as `method`, " +
"min(`attribute_float64_bytes`) as value " +
"from o11y_logs.distributed_logs " +
"from event.log " +
"where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) " +
"AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' " +
"AND has(attributes_string_key, 'method') " +
@@ -647,7 +647,7 @@ var testBuildLogsQueryData = []struct {
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts," +
" attributes_string_value[indexOf(attributes_string_key, 'method')] as `method`, " +
"max(`attribute_float64_bytes`) as value " +
"from o11y_logs.distributed_logs " +
"from event.log " +
"where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) " +
"AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' " +
"AND has(attributes_string_key, 'method') " +
@@ -674,7 +674,7 @@ var testBuildLogsQueryData = []struct {
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts," +
" attributes_string_value[indexOf(attributes_string_key, 'method')] as `method`, " +
"quantile(0.05)(`attribute_float64_bytes`) as value " +
"from o11y_logs.distributed_logs " +
"from event.log " +
"where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) " +
"AND has(attributes_string_key, 'method') " +
"AND `attribute_float64_bytes_exists`=true " +
@@ -698,7 +698,7 @@ var testBuildLogsQueryData = []struct {
},
TableName: "logs",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, attributes_string_value[indexOf(attributes_string_key, 'method')] as `method`" +
", sum(`attribute_float64_bytes`)/60.000000 as value from o11y_logs.distributed_logs " +
", sum(`attribute_float64_bytes`)/60.000000 as value from event.log " +
"where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) " +
"AND has(attributes_string_key, 'method') " +
"AND `attribute_float64_bytes_exists`=true " +
@@ -722,7 +722,7 @@ var testBuildLogsQueryData = []struct {
TableName: "logs",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, attributes_string_value[indexOf(attributes_string_key, 'method')] as `method`" +
", count(attributes_float64_value[indexOf(attributes_float64_key, 'bytes')])/60.000000 as value " +
"from o11y_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) " +
"from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) " +
"AND has(attributes_string_key, 'method') " +
"AND has(attributes_float64_key, 'bytes') " +
"group by `method`,ts " +
@@ -747,7 +747,7 @@ var testBuildLogsQueryData = []struct {
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, " +
"attributes_string_value[indexOf(attributes_string_key, 'method')] as `method`, " +
"sum(attributes_float64_value[indexOf(attributes_float64_key, 'bytes')])/60.000000 as value " +
"from o11y_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) " +
"from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) " +
"AND has(attributes_string_key, 'method') " +
"AND has(attributes_float64_key, 'bytes') " +
"group by `method`,ts " +
@@ -771,7 +771,7 @@ var testBuildLogsQueryData = []struct {
"CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool," +
"CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string," +
"CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope " +
"from o11y_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) order by timestamp DESC",
"from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) order by timestamp DESC",
},
{
Name: "Test Noop order by custom",
@@ -791,7 +791,7 @@ var testBuildLogsQueryData = []struct {
"CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool," +
"CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string," +
"CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope " +
"from o11y_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) order by `attribute_string_method` ASC",
"from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) order by `attribute_string_method` ASC",
},
{
Name: "Test Noop with filter",
@@ -812,7 +812,7 @@ var testBuildLogsQueryData = []struct {
"CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool," +
"CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string," +
"CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope " +
"from o11y_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND severity_number != 0 order by timestamp DESC",
"from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND severity_number != 0 order by timestamp DESC",
},
{
Name: "Test aggregate with having clause",
@@ -834,7 +834,7 @@ var testBuildLogsQueryData = []struct {
},
},
TableName: "logs",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from o11y_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND has(attributes_string_key, 'name') group by ts having value > 10 order by value DESC",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND has(attributes_string_key, 'name') group by ts having value > 10 order by value DESC",
},
{
Name: "Test aggregate with having clause and filters",
@@ -860,7 +860,7 @@ var testBuildLogsQueryData = []struct {
},
},
TableName: "logs",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from o11y_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' AND has(attributes_string_key, 'name') group by ts having value > 10 order by value DESC",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' AND has(attributes_string_key, 'name') group by ts having value > 10 order by value DESC",
},
{
Name: "Test top level key",
@@ -886,7 +886,7 @@ var testBuildLogsQueryData = []struct {
},
},
TableName: "logs",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from o11y_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND lower(body) LIKE lower('%test%') AND has(attributes_string_key, 'name') group by ts having value > 10 order by value DESC",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND lower(body) LIKE lower('%test%') AND has(attributes_string_key, 'name') group by ts having value > 10 order by value DESC",
},
{
Name: "Test attribute with same name as top level key",
@@ -912,7 +912,7 @@ var testBuildLogsQueryData = []struct {
},
},
TableName: "logs",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from o11y_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND attributes_string_value[indexOf(attributes_string_key, 'body')] ILIKE '%test%' AND has(attributes_string_key, 'name') group by ts having value > 10 order by value DESC",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND attributes_string_value[indexOf(attributes_string_key, 'body')] ILIKE '%test%' AND has(attributes_string_key, 'name') group by ts having value > 10 order by value DESC",
},
// Tests for table panel type
@@ -928,7 +928,7 @@ var testBuildLogsQueryData = []struct {
Expression: "A",
},
TableName: "logs",
ExpectedQuery: "SELECT now() as ts, toFloat64(count(*)) as value from o11y_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) order by value DESC",
ExpectedQuery: "SELECT now() as ts, toFloat64(count(*)) as value from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) order by value DESC",
},
{
Name: "TABLE: Test count with groupBy",
@@ -945,7 +945,7 @@ var testBuildLogsQueryData = []struct {
},
},
TableName: "logs",
ExpectedQuery: "SELECT now() as ts, attributes_string_value[indexOf(attributes_string_key, 'name')] as `name`, toFloat64(count(*)) as value from o11y_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND has(attributes_string_key, 'name') group by `name` order by value DESC",
ExpectedQuery: "SELECT now() as ts, attributes_string_value[indexOf(attributes_string_key, 'name')] as `name`, toFloat64(count(*)) as value from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND has(attributes_string_key, 'name') group by `name` order by value DESC",
},
{
Name: "TABLE: Test rate with groupBy",
@@ -962,7 +962,7 @@ var testBuildLogsQueryData = []struct {
},
},
TableName: "logs",
ExpectedQuery: "SELECT now() as ts, attributes_string_value[indexOf(attributes_string_key, 'name')] as `name`, count()/97.000000 as value from o11y_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND has(attributes_string_key, 'name') group by `name` order by value DESC",
ExpectedQuery: "SELECT now() as ts, attributes_string_value[indexOf(attributes_string_key, 'name')] as `name`, count()/97.000000 as value from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND has(attributes_string_key, 'name') group by `name` order by value DESC",
},
{
Name: "TABLE: Test count with groupBy, orderBy",
@@ -982,7 +982,7 @@ var testBuildLogsQueryData = []struct {
},
},
TableName: "logs",
ExpectedQuery: "SELECT now() as ts, attributes_string_value[indexOf(attributes_string_key, 'name')] as `name`, toFloat64(count(*)) as value from o11y_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND has(attributes_string_key, 'name') group by `name` order by `name` DESC",
ExpectedQuery: "SELECT now() as ts, attributes_string_value[indexOf(attributes_string_key, 'name')] as `name`, toFloat64(count(*)) as value from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND has(attributes_string_key, 'name') group by `name` order by `name` DESC",
},
{
Name: "TABLE: Test count with JSON Filter, groupBy, orderBy",
@@ -1016,7 +1016,7 @@ var testBuildLogsQueryData = []struct {
},
},
TableName: "logs",
ExpectedQuery: "SELECT now() as ts, attributes_string_value[indexOf(attributes_string_key, 'name')] as `name`, toFloat64(count(*)) as value from o11y_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND lower(body) like lower('%message%') AND JSON_EXISTS(body, '$.\"message\"') AND JSON_VALUE(body, '$.\"message\"') ILIKE '%a%' AND has(attributes_string_key, 'name') group by `name` order by `name` DESC",
ExpectedQuery: "SELECT now() as ts, attributes_string_value[indexOf(attributes_string_key, 'name')] as `name`, toFloat64(count(*)) as value from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND lower(body) like lower('%message%') AND JSON_EXISTS(body, '$.\"message\"') AND JSON_VALUE(body, '$.\"message\"') ILIKE '%a%' AND has(attributes_string_key, 'name') group by `name` order by `name` DESC",
},
{
Name: "TABLE: Test count with JSON Filter Array, groupBy, orderBy",
@@ -1050,7 +1050,7 @@ var testBuildLogsQueryData = []struct {
},
},
TableName: "logs",
ExpectedQuery: "SELECT now() as ts, attributes_string_value[indexOf(attributes_string_key, 'name')] as `name`, toFloat64(count(*)) as value from o11y_logs.distributed_logs where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND lower(body) like lower('%requestor_list%') AND lower(body) like lower('%index_service%') AND has(JSONExtract(JSON_QUERY(body, '$.\"requestor_list\"[*]'), 'Array(String)'), 'index_service') AND has(attributes_string_key, 'name') group by `name` order by `name` DESC",
ExpectedQuery: "SELECT now() as ts, attributes_string_value[indexOf(attributes_string_key, 'name')] as `name`, toFloat64(count(*)) as value from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND lower(body) like lower('%requestor_list%') AND lower(body) like lower('%index_service%') AND has(JSONExtract(JSON_QUERY(body, '$.\"requestor_list\"[*]'), 'Array(String)'), 'index_service') AND has(attributes_string_key, 'name') group by `name` order by `name` DESC",
},
}
@@ -1233,7 +1233,7 @@ var testPrepLogsQueryData = []struct {
GroupBy: []v3.AttributeKey{{Key: "method", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}},
},
TableName: "logs",
ExpectedQuery: "SELECT `method` from (SELECT attributes_string_value[indexOf(attributes_string_key, 'method')] as `method`, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from o11y_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' AND has(attributes_string_key, 'method') AND has(attributes_string_key, 'name') group by `method` order by value DESC) LIMIT 10",
ExpectedQuery: "SELECT `method` from (SELECT attributes_string_value[indexOf(attributes_string_key, 'method')] as `method`, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from event.log where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' AND has(attributes_string_key, 'method') AND has(attributes_string_key, 'name') group by `method` order by value DESC) LIMIT 10",
Options: v3.QBOptions{GraphLimitQtype: constants.FirstQueryGraphLimit},
},
{
@@ -1256,7 +1256,7 @@ var testPrepLogsQueryData = []struct {
OrderBy: []v3.OrderBy{{ColumnName: constants.O11yOrderByValue, Order: "ASC"}},
},
TableName: "logs",
ExpectedQuery: "SELECT `method` from (SELECT attributes_string_value[indexOf(attributes_string_key, 'method')] as `method`, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from o11y_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' AND has(attributes_string_key, 'method') AND has(attributes_string_key, 'name') group by `method` order by value ASC) LIMIT 10",
ExpectedQuery: "SELECT `method` from (SELECT attributes_string_value[indexOf(attributes_string_key, 'method')] as `method`, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from event.log where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' AND has(attributes_string_key, 'method') AND has(attributes_string_key, 'name') group by `method` order by value ASC) LIMIT 10",
Options: v3.QBOptions{GraphLimitQtype: constants.FirstQueryGraphLimit},
},
{
@@ -1279,7 +1279,7 @@ var testPrepLogsQueryData = []struct {
OrderBy: []v3.OrderBy{{ColumnName: "method", Order: "ASC"}},
},
TableName: "logs",
ExpectedQuery: "SELECT `method` from (SELECT attributes_string_value[indexOf(attributes_string_key, 'method')] as `method`, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from o11y_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' AND has(attributes_string_key, 'method') AND has(attributes_string_key, 'name') group by `method` order by `method` ASC) LIMIT 10",
ExpectedQuery: "SELECT `method` from (SELECT attributes_string_value[indexOf(attributes_string_key, 'method')] as `method`, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from event.log where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' AND has(attributes_string_key, 'method') AND has(attributes_string_key, 'name') group by `method` order by `method` ASC) LIMIT 10",
Options: v3.QBOptions{GraphLimitQtype: constants.FirstQueryGraphLimit},
},
{
@@ -1301,7 +1301,7 @@ var testPrepLogsQueryData = []struct {
Limit: 2,
},
TableName: "logs",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, attributes_string_value[indexOf(attributes_string_key, 'method')] as `method`, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from o11y_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' AND has(attributes_string_key, 'method') AND has(attributes_string_key, 'name') AND (`method`) GLOBAL IN (#LIMIT_PLACEHOLDER) group by `method`,ts order by value DESC",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, attributes_string_value[indexOf(attributes_string_key, 'method')] as `method`, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from event.log where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' AND has(attributes_string_key, 'method') AND has(attributes_string_key, 'name') AND (`method`) GLOBAL IN (#LIMIT_PLACEHOLDER) group by `method`,ts order by value DESC",
Options: v3.QBOptions{GraphLimitQtype: constants.SecondQueryGraphLimit},
},
{
@@ -1324,7 +1324,7 @@ var testPrepLogsQueryData = []struct {
Limit: 2,
},
TableName: "logs",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, attributes_string_value[indexOf(attributes_string_key, 'method')] as `method`, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from o11y_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' AND has(attributes_string_key, 'method') AND has(attributes_string_key, 'name') AND (`method`) GLOBAL IN (#LIMIT_PLACEHOLDER) group by `method`,ts order by `method` ASC",
ExpectedQuery: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, attributes_string_value[indexOf(attributes_string_key, 'method')] as `method`, toFloat64(count(distinct(attributes_string_value[indexOf(attributes_string_key, 'name')]))) as value from event.log where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' AND has(attributes_string_key, 'method') AND has(attributes_string_key, 'name') AND (`method`) GLOBAL IN (#LIMIT_PLACEHOLDER) group by `method`,ts order by `method` ASC",
Options: v3.QBOptions{GraphLimitQtype: constants.SecondQueryGraphLimit},
},
// Live tail
@@ -1344,7 +1344,7 @@ var testPrepLogsQueryData = []struct {
},
},
TableName: "logs",
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string,CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope from o11y_logs.distributed_logs where attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' AND ",
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string,CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope from event.log where attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' AND ",
Options: v3.QBOptions{IsLivetailQuery: true},
},
{
@@ -1363,7 +1363,7 @@ var testPrepLogsQueryData = []struct {
},
},
TableName: "logs",
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string,CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope from o11y_logs.distributed_logs where attributes_string_value[indexOf(attributes_string_key, 'method')] ILIKE '%GET%' AND ",
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string,CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope from event.log where attributes_string_value[indexOf(attributes_string_key, 'method')] ILIKE '%GET%' AND ",
Options: v3.QBOptions{IsLivetailQuery: true},
},
{
@@ -1379,7 +1379,7 @@ var testPrepLogsQueryData = []struct {
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{}},
},
TableName: "logs",
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string,CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope from o11y_logs.distributed_logs where ",
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string,CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope from event.log where ",
Options: v3.QBOptions{IsLivetailQuery: true},
},
{
@@ -1395,7 +1395,7 @@ var testPrepLogsQueryData = []struct {
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{}},
},
TableName: "logs",
ExpectedQuery: "SELECT now() as ts, toFloat64(count(*)) as value from o11y_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) order by value DESC",
ExpectedQuery: "SELECT now() as ts, toFloat64(count(*)) as value from event.log where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) order by value DESC",
Options: v3.QBOptions{},
},
{
@@ -1412,7 +1412,7 @@ var testPrepLogsQueryData = []struct {
Limit: 10,
},
TableName: "logs",
ExpectedQuery: "SELECT now() as ts, toFloat64(count(*)) as value from o11y_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) order by value DESC LIMIT 10",
ExpectedQuery: "SELECT now() as ts, toFloat64(count(*)) as value from event.log where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) order by value DESC LIMIT 10",
Options: v3.QBOptions{},
},
{
@@ -1443,7 +1443,7 @@ var testPrepLogsQueryData = []struct {
"attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as " +
"attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string," +
"CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope " +
"from o11y_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND id < 'logid' order by " +
"from event.log where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND id < 'logid' order by " +
"timestamp DESC LIMIT 100",
},
{
@@ -1474,7 +1474,7 @@ var testPrepLogsQueryData = []struct {
"attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as " +
"attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string," +
"CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope " +
"from o11y_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' order by " +
"from event.log where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' order by " +
"resources_string_value[indexOf(resources_string_key, 'mycolumn')] DESC LIMIT 100 OFFSET 100",
},
}
@@ -1520,7 +1520,7 @@ var testPrepLogsQueryLimitOffsetData = []struct {
PageSize: 5,
},
TableName: "logs",
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string,CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope from o11y_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) order by timestamp desc LIMIT 1",
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string,CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope from event.log where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) order by timestamp desc LIMIT 1",
},
{
Name: "Test limit greater than pageSize - order by ts",
@@ -1541,7 +1541,7 @@ var testPrepLogsQueryLimitOffsetData = []struct {
PageSize: 10,
},
TableName: "logs",
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string,CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope from o11y_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND id < '2TNh4vp2TpiWyLt3SzuadLJF2s4' order by timestamp desc LIMIT 10",
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string,CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope from event.log where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND id < '2TNh4vp2TpiWyLt3SzuadLJF2s4' order by timestamp desc LIMIT 10",
},
{
Name: "Test limit less than pageSize - order by custom",
@@ -1560,7 +1560,7 @@ var testPrepLogsQueryLimitOffsetData = []struct {
PageSize: 5,
},
TableName: "logs",
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string,CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope from o11y_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) order by attributes_string_value[indexOf(attributes_string_key, 'method')] desc LIMIT 1 OFFSET 0",
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string,CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope from event.log where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) order by attributes_string_value[indexOf(attributes_string_key, 'method')] desc LIMIT 1 OFFSET 0",
},
{
Name: "Test limit greater than pageSize - order by custom",
@@ -1581,7 +1581,7 @@ var testPrepLogsQueryLimitOffsetData = []struct {
PageSize: 50,
},
TableName: "logs",
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string,CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope from o11y_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND id < '2TNh4vp2TpiWyLt3SzuadLJF2s4' order by attributes_string_value[indexOf(attributes_string_key, 'method')] desc LIMIT 50 OFFSET 50",
ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string,CAST((scope_string_key, scope_string_value), 'Map(String, String)') as scope from event.log where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND id < '2TNh4vp2TpiWyLt3SzuadLJF2s4' order by attributes_string_value[indexOf(attributes_string_key, 'method')] desc LIMIT 50 OFFSET 50",
},
}
+15 -8
View File
@@ -9,6 +9,8 @@ import (
"github.com/hanzoai/o11y/pkg/query-service/constants"
v3 "github.com/hanzoai/o11y/pkg/query-service/model/v3"
"github.com/hanzoai/o11y/pkg/query-service/utils"
"github.com/hanzoai/o11y/pkg/telemetrylogs"
"github.com/hanzoai/o11y/pkg/telemetryplane"
)
var logOperators = map[v3.FilterOperator]string{
@@ -42,11 +44,16 @@ var skipExistsFilter = map[v3.FilterOperator]struct{}{
}
const (
BODY = "body"
DISTRIBUTED_LOGS_V2 = "distributed_logs_v2"
DISTRIBUTED_LOGS_V2_RESOURCE = "distributed_logs_v2_resource"
DB_NAME = "o11y_logs"
NANOSECOND = 1000000000
BODY = "body"
// distributed_logs_v2 IS event.log and distributed_logs_v2_resource IS
// event.log_resource; o11y_logs was dropped by HIP-0132. Aliases, not
// spellings — see pkg/telemetryplane.
DISTRIBUTED_LOGS_V2 = telemetrylogs.LogTableName
DISTRIBUTED_LOGS_V2_RESOURCE = telemetrylogs.LogResourceTableName
DB_NAME = telemetryplane.DBName
NANOSECOND = 1000000000
)
func getDatastoreLogsColumnDataType(columnDataType v3.AttributeKeyDataType) string {
@@ -308,7 +315,7 @@ func generateAggregateClause(panelType v3.PanelType, start, end int64, aggOp v3.
having string,
orderBy string,
) (string, error) {
queryTmpl := " %s as value from o11y_logs." + DISTRIBUTED_LOGS_V2 +
queryTmpl := " %s as value from " + DB_NAME + "." + DISTRIBUTED_LOGS_V2 +
" where " + timeFilter + "%s" +
"%s%s" +
"%s"
@@ -405,7 +412,7 @@ func buildLogsQuery(panelType v3.PanelType, start, end, step int64, mq *v3.Build
if mq.AggregateOperator == v3.AggregateOperatorNoOp {
// with noop any filter or different order by other than ts will use new table
sqlSelect := constants.LogsSQLSelectV2
queryTmpl := sqlSelect + "from o11y_logs.%s where %s%s order by %s"
queryTmpl := sqlSelect + "from " + DB_NAME + ".%s where %s%s order by %s"
query := fmt.Sprintf(queryTmpl, DISTRIBUTED_LOGS_V2, timeFilter, filterSubQuery, orderBy)
return query, nil
// ---- NOOP ends here ----
@@ -488,7 +495,7 @@ func buildLogsLiveTailQuery(mq *v3.BuilderQuery) (string, error) {
// the reader will add the timestamp and id filters
switch mq.AggregateOperator {
case v3.AggregateOperatorNoOp:
query := constants.LogsSQLSelectV2 + "from o11y_logs." + DISTRIBUTED_LOGS_V2 + " where "
query := constants.LogsSQLSelectV2 + "from " + DB_NAME + "." + DISTRIBUTED_LOGS_V2 + " where "
if len(filterSubQuery) > 0 {
query = query + filterSubQuery + " AND "
}
@@ -692,7 +692,7 @@ func Test_generateAggregateClause(t *testing.T) {
having: "",
orderBy: " order by `user_name` desc",
},
want: " count(test)/60.000000 as value from o11y_logs.distributed_logs_v2 where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND " +
want: " count(test)/60.000000 as value from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND " +
"(ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND attributes_string['service.name'] = 'test' " +
"group by `user_name` order by `user_name` desc",
},
@@ -709,7 +709,7 @@ func Test_generateAggregateClause(t *testing.T) {
having: " having value > 10",
orderBy: " order by `user_name` desc",
},
want: " count(test)/60.000000 as value from o11y_logs.distributed_logs_v2 where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND " +
want: " count(test)/60.000000 as value from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND " +
"(ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND attributes_string['service.name'] = 'test' group by `user_name` having value > 10 order by " +
"`user_name` desc",
},
@@ -728,7 +728,7 @@ func Test_generateAggregateClause(t *testing.T) {
having: "",
orderBy: " order by `user_name` desc",
},
want: " count(test)/3600.000000 as value from o11y_logs.distributed_logs_v2 where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND " +
want: " count(test)/3600.000000 as value from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND " +
"(ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND attributes_string['service.name'] = 'test' " +
"group by `user_name` order by `user_name` desc",
},
@@ -799,7 +799,7 @@ func Test_buildLogsQuery(t *testing.T) {
},
},
},
want: "SELECT attributes_string['user_name'] as `user_name`, toFloat64(count(*)) as value from o11y_logs.distributed_logs_v2 " +
want: "SELECT attributes_string['user_name'] as `user_name`, toFloat64(count(*)) as value from event.log " +
"where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"AND attributes_string['service.name'] = 'test' AND mapContains(attributes_string, 'service.name') AND mapContains(attributes_string, 'user_name') " +
"group by `user_name` order by `user_name` desc",
@@ -835,7 +835,7 @@ func Test_buildLogsQuery(t *testing.T) {
},
},
want: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string " +
"from o11y_logs.distributed_logs_v2 where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"AND attributes_string['service.name'] = 'test' AND mapContains(attributes_string, 'service.name') order by timestamp desc",
},
{
@@ -890,9 +890,9 @@ func Test_buildLogsQuery(t *testing.T) {
},
},
want: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, resources_string['host'] as `host`, avg(attributes_number['duration']) as value " +
"from o11y_logs.distributed_logs_v2 where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"from event.log where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"AND attributes_number['duration'] > 1000.000000 AND mapContains(attributes_number, 'duration') AND mapContains(attributes_number, 'duration') AND " +
"(resource_fingerprint GLOBAL IN (SELECT fingerprint FROM o11y_logs.distributed_logs_v2_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) " +
"(resource_fingerprint GLOBAL IN (SELECT fingerprint FROM event.log_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) " +
"AND simpleJSONExtractString(labels, 'service.name') = 'test' AND labels like '%service.name\":\"test%' AND ( (simpleJSONHas(labels, 'host') AND labels like '%host%') ))) " +
"group by `host`,ts order by `host` desc",
},
@@ -960,10 +960,10 @@ func TestPrepareLogsQuery(t *testing.T) {
},
},
},
want: "SELECT attributes_string['name'] as `name`, resources_string['host'] as `host`, toFloat64(count(*)) as value from o11y_logs.distributed_logs_v2 where " +
want: "SELECT attributes_string['name'] as `name`, resources_string['host'] as `host`, toFloat64(count(*)) as value from event.log where " +
"(timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND lower(body) like lower('%requestor_list%') " +
"AND lower(body) like lower('%index_service%') AND has(JSONExtract(JSON_QUERY(body, '$.\"requestor_list\"[*]'), 'Array(String)'), 'index_service') AND mapContains(attributes_string, 'name') AND " +
"(resource_fingerprint GLOBAL IN (SELECT fingerprint FROM o11y_logs.distributed_logs_v2_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND " +
"(resource_fingerprint GLOBAL IN (SELECT fingerprint FROM event.log_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND " +
"( (simpleJSONHas(labels, 'host') AND labels like '%host%') ))) group by `name`,`host` order by `name` DESC",
},
{
@@ -989,10 +989,10 @@ func TestPrepareLogsQuery(t *testing.T) {
},
options: v3.QBOptions{GraphLimitQtype: constants.FirstQueryGraphLimit},
},
want: "SELECT `user` from (SELECT attributes_string['user'] as `user`, toFloat64(count(distinct(attributes_string['name']))) as value from o11y_logs.distributed_logs_v2 " +
want: "SELECT `user` from (SELECT attributes_string['user'] as `user`, toFloat64(count(distinct(attributes_string['name']))) as value from event.log " +
"where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND attributes_string['method'] = 'GET' " +
"AND mapContains(attributes_string, 'method') AND mapContains(attributes_string, 'user') AND mapContains(attributes_string, 'name') AND (resource_fingerprint GLOBAL IN " +
"(SELECT fingerprint FROM o11y_logs.distributed_logs_v2_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND simpleJSONExtractString(labels, 'service.name') = 'app' " +
"(SELECT fingerprint FROM event.log_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND simpleJSONExtractString(labels, 'service.name') = 'app' " +
"AND labels like '%service.name\":\"app%')) group by `user` order by value DESC) LIMIT 10",
},
{
@@ -1019,9 +1019,9 @@ func TestPrepareLogsQuery(t *testing.T) {
options: v3.QBOptions{GraphLimitQtype: constants.SecondQueryGraphLimit},
},
want: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, attributes_string['user'] as `user`, toFloat64(count(distinct(attributes_string['name']))) as value " +
"from o11y_logs.distributed_logs_v2 where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND " +
"from event.log where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND " +
"attributes_string['method'] = 'GET' AND mapContains(attributes_string, 'method') AND mapContains(attributes_string, 'user') AND mapContains(attributes_string, 'name') AND " +
"(resource_fingerprint GLOBAL IN (SELECT fingerprint FROM o11y_logs.distributed_logs_v2_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND " +
"(resource_fingerprint GLOBAL IN (SELECT fingerprint FROM event.log_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND " +
"simpleJSONExtractString(labels, 'service.name') = 'app' AND labels like '%service.name\":\"app%')) AND (`user`) GLOBAL IN (#LIMIT_PLACEHOLDER) group by `user`,ts order by value DESC",
},
{
@@ -1044,7 +1044,7 @@ func TestPrepareLogsQuery(t *testing.T) {
options: v3.QBOptions{IsLivetailQuery: true},
},
want: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string " +
"from o11y_logs.distributed_logs_v2 where attributes_string['method'] = 'GET' AND mapContains(attributes_string, 'method') AND ",
"from event.log where attributes_string['method'] = 'GET' AND mapContains(attributes_string, 'method') AND ",
},
{
name: "Live Tail Query with resource attribute",
@@ -1067,8 +1067,8 @@ func TestPrepareLogsQuery(t *testing.T) {
options: v3.QBOptions{IsLivetailQuery: true},
},
want: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string from " +
"o11y_logs.distributed_logs_v2 where attributes_string['method'] = 'GET' AND mapContains(attributes_string, 'method') AND " +
"(resource_fingerprint GLOBAL IN (SELECT fingerprint FROM o11y_logs.distributed_logs_v2_resource WHERE simpleJSONExtractString(lower(labels), 'service.name') LIKE '%app%' AND lower(labels) like '%service.name%app%' AND ",
"event.log where attributes_string['method'] = 'GET' AND mapContains(attributes_string, 'method') AND " +
"(resource_fingerprint GLOBAL IN (SELECT fingerprint FROM event.log_resource WHERE simpleJSONExtractString(lower(labels), 'service.name') LIKE '%app%' AND lower(labels) like '%service.name%app%' AND ",
},
{
name: "Live Tail Query W/O filter",
@@ -1087,7 +1087,7 @@ func TestPrepareLogsQuery(t *testing.T) {
options: v3.QBOptions{IsLivetailQuery: true},
},
want: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string " +
"from o11y_logs.distributed_logs_v2 where ",
"from event.log where ",
},
{
name: "Table query with limit",
@@ -1105,7 +1105,7 @@ func TestPrepareLogsQuery(t *testing.T) {
Limit: 10,
},
},
want: "SELECT toFloat64(count(*)) as value from o11y_logs.distributed_logs_v2 where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) order by value DESC LIMIT 10",
want: "SELECT toFloat64(count(*)) as value from event.log where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) order by value DESC LIMIT 10",
},
{
name: "Test limit less than pageSize - order by ts",
@@ -1127,7 +1127,7 @@ func TestPrepareLogsQuery(t *testing.T) {
},
},
want: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string from " +
"o11y_logs.distributed_logs_v2 where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"event.log where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"order by timestamp desc LIMIT 1 OFFSET 0",
},
{
@@ -1152,7 +1152,7 @@ func TestPrepareLogsQuery(t *testing.T) {
},
},
want: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string from " +
"o11y_logs.distributed_logs_v2 where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"event.log where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"AND id < '2TNh4vp2TpiWyLt3SzuadLJF2s4' order by timestamp desc LIMIT 10 OFFSET 10",
},
{
@@ -1175,7 +1175,7 @@ func TestPrepareLogsQuery(t *testing.T) {
},
},
want: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string from " +
"o11y_logs.distributed_logs_v2 where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"event.log where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"order by attributes_string['method'] desc LIMIT 1 OFFSET 0",
},
{
@@ -1200,7 +1200,7 @@ func TestPrepareLogsQuery(t *testing.T) {
},
},
want: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, scope_name, scope_version, body, attributes_string, attributes_number, attributes_bool, resources_string, scope_string from " +
"o11y_logs.distributed_logs_v2 where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND " +
"event.log where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND " +
"id < '2TNh4vp2TpiWyLt3SzuadLJF2s4' order by attributes_string['method'] desc LIMIT 50 OFFSET 50",
},
}
@@ -38,7 +38,7 @@ func TestPanelTableForCumulative(t *testing.T) {
},
Expression: "A",
},
expected: "SELECT toStartOfHour(now()) as ts, sum(rate_value)/29 as value FROM (SELECT ts, If((value - lagInFrame(value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (value - lagInFrame(value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as rate_value FROM(SELECT fingerprint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['o11y_latency_count'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1689253200000 AND unix_milli < 1689257640000 AND JSONExtractString(labels, 'service_name') IN ['frontend'] AND JSONExtractString(labels, 'operation') IN ['HTTP GET /dispatch']) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_count'] AND unix_milli >= 1689255866000 AND unix_milli <= 1689257640000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(rate_value) = 0 GROUP BY ts ORDER BY ts",
expected: "SELECT toStartOfHour(now()) as ts, sum(rate_value)/29 as value FROM (SELECT ts, If((value - lagInFrame(value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (value - lagInFrame(value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as rate_value FROM(SELECT fingerprint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as value FROM event.metric INNER JOIN (SELECT DISTINCT fingerprint FROM event.series WHERE metric_name IN ['o11y_latency_count'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1689253200000 AND unix_milli < 1689257640000 AND JSONExtractString(labels, 'service_name') IN ['frontend'] AND JSONExtractString(labels, 'operation') IN ['HTTP GET /dispatch']) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_count'] AND unix_milli >= 1689255866000 AND unix_milli <= 1689257640000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(rate_value) = 0 GROUP BY ts ORDER BY ts",
},
{
name: "latency p50",
@@ -66,7 +66,7 @@ func TestPanelTableForCumulative(t *testing.T) {
},
},
},
expected: "SELECT toStartOfHour(now()) as ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.500) as value FROM (SELECT le, toStartOfHour(now()) as ts, sum(rate_value)/29 as value FROM (SELECT le, ts, If((value - lagInFrame(value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (value - lagInFrame(value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as rate_value FROM(SELECT fingerprint, le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'le') as le, fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1689253200000 AND unix_milli < 1689257640000 AND JSONExtractString(labels, 'service_name') = 'frontend') as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1689255866000 AND unix_milli <= 1689257640000 GROUP BY fingerprint, le,ts ORDER BY fingerprint, le ASC, ts) WINDOW rate_window as (PARTITION BY fingerprint, le ORDER BY fingerprint, le ASC, ts)) WHERE isNaN(rate_value) = 0 GROUP BY le,ts ORDER BY le ASC, ts) GROUP BY ts ORDER BY ts",
expected: "SELECT toStartOfHour(now()) as ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.500) as value FROM (SELECT le, toStartOfHour(now()) as ts, sum(rate_value)/29 as value FROM (SELECT le, ts, If((value - lagInFrame(value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (value - lagInFrame(value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as rate_value FROM(SELECT fingerprint, le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'le') as le, fingerprint FROM event.series WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1689253200000 AND unix_milli < 1689257640000 AND JSONExtractString(labels, 'service_name') = 'frontend') as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1689255866000 AND unix_milli <= 1689257640000 GROUP BY fingerprint, le,ts ORDER BY fingerprint, le ASC, ts) WINDOW rate_window as (PARTITION BY fingerprint, le ORDER BY fingerprint, le ASC, ts)) WHERE isNaN(rate_value) = 0 GROUP BY le,ts ORDER BY le ASC, ts) GROUP BY ts ORDER BY ts",
},
{
name: "latency p99 with group by",
@@ -88,7 +88,7 @@ func TestPanelTableForCumulative(t *testing.T) {
},
Expression: "A",
},
expected: "SELECT service_name, toStartOfHour(now()) as ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT service_name,le, toStartOfHour(now()) as ts, sum(rate_value)/29 as value FROM (SELECT service_name,le, ts, If((value - lagInFrame(value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (value - lagInFrame(value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as rate_value FROM(SELECT fingerprint, service_name,le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'le') as le, fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1689253200000 AND unix_milli < 1689257640000) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1689255866000 AND unix_milli <= 1689257640000 GROUP BY fingerprint, service_name,le,ts ORDER BY fingerprint, service_name ASC,le ASC, ts) WINDOW rate_window as (PARTITION BY fingerprint, service_name,le ORDER BY fingerprint, service_name ASC,le ASC, ts)) WHERE isNaN(rate_value) = 0 GROUP BY service_name,le,ts ORDER BY service_name ASC,le ASC, ts) GROUP BY service_name,ts ORDER BY service_name ASC, ts",
expected: "SELECT service_name, toStartOfHour(now()) as ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT service_name,le, toStartOfHour(now()) as ts, sum(rate_value)/29 as value FROM (SELECT service_name,le, ts, If((value - lagInFrame(value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (value - lagInFrame(value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as rate_value FROM(SELECT fingerprint, service_name,le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'le') as le, fingerprint FROM event.series WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1689253200000 AND unix_milli < 1689257640000) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1689255866000 AND unix_milli <= 1689257640000 GROUP BY fingerprint, service_name,le,ts ORDER BY fingerprint, service_name ASC,le ASC, ts) WINDOW rate_window as (PARTITION BY fingerprint, service_name,le ORDER BY fingerprint, service_name ASC,le ASC, ts)) WHERE isNaN(rate_value) = 0 GROUP BY service_name,le,ts ORDER BY service_name ASC,le ASC, ts) GROUP BY service_name,ts ORDER BY service_name ASC, ts",
},
}
@@ -38,7 +38,7 @@ func TestPanelTableForDelta(t *testing.T) {
},
Expression: "A",
},
expected: "SELECT toStartOfHour(now()) as ts, sum(value)/1800 as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['o11y_latency_count'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1689253200000 AND unix_milli < 1689257640000 AND JSONExtractString(labels, 'service_name') IN ['frontend'] AND JSONExtractString(labels, 'operation') IN ['HTTP GET /dispatch']) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_count'] AND unix_milli >= 1689255866000 AND unix_milli <= 1689257640000 GROUP BY ts ORDER BY ts",
expected: "SELECT toStartOfHour(now()) as ts, sum(value)/1800 as value FROM event.metric INNER JOIN (SELECT DISTINCT fingerprint FROM event.series WHERE metric_name IN ['o11y_latency_count'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1689253200000 AND unix_milli < 1689257640000 AND JSONExtractString(labels, 'service_name') IN ['frontend'] AND JSONExtractString(labels, 'operation') IN ['HTTP GET /dispatch']) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_count'] AND unix_milli >= 1689255866000 AND unix_milli <= 1689257640000 GROUP BY ts ORDER BY ts",
},
{
name: "latency p50",
@@ -61,7 +61,7 @@ func TestPanelTableForDelta(t *testing.T) {
},
Expression: "A",
},
expected: "SELECT ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.500) as value FROM (SELECT le, toStartOfHour(now()) as ts, sum(value)/1800 as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1689253200000 AND unix_milli < 1689257640000 AND JSONExtractString(labels, 'service_name') = 'frontend') as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1689255866000 AND unix_milli <= 1689257640000 GROUP BY le,ts ORDER BY le ASC, ts) GROUP BY ts ORDER BY ts",
expected: "SELECT ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.500) as value FROM (SELECT le, toStartOfHour(now()) as ts, sum(value)/1800 as value FROM event.metric INNER JOIN (SELECT DISTINCT fingerprint FROM event.series WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1689253200000 AND unix_milli < 1689257640000 AND JSONExtractString(labels, 'service_name') = 'frontend') as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1689255866000 AND unix_milli <= 1689257640000 GROUP BY le,ts ORDER BY le ASC, ts) GROUP BY ts ORDER BY ts",
},
{
name: "latency p99 with group by",
@@ -80,7 +80,7 @@ func TestPanelTableForDelta(t *testing.T) {
},
Expression: "A",
},
expected: "SELECT service_name, ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT service_name,le, toStartOfHour(now()) as ts, sum(value)/1800 as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1689253200000 AND unix_milli < 1689257640000) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1689255866000 AND unix_milli <= 1689257640000 GROUP BY service_name,le,ts ORDER BY service_name ASC,le ASC, ts) GROUP BY service_name,ts ORDER BY service_name ASC, ts",
expected: "SELECT service_name, ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT service_name,le, toStartOfHour(now()) as ts, sum(value)/1800 as value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM event.series WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1689253200000 AND unix_milli < 1689257640000) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1689255866000 AND unix_milli <= 1689257640000 GROUP BY service_name,le,ts ORDER BY service_name ASC,le ASC, ts) GROUP BY service_name,ts ORDER BY service_name ASC, ts",
},
}
@@ -105,7 +105,7 @@ func TestBuildQueryWithMultipleQueries(t *testing.T) {
func TestBuildQueryXRate(t *testing.T) {
t.Run("TestBuildQueryXRate", func(t *testing.T) {
tmpl := `SELECT ts, %s(rate_value) as value FROM (SELECT ts, If((value - lagInFrame(value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (value - lagInFrame(value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as rate_value FROM(SELECT fingerprint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['name'] AND temporality = '' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['name'] AND unix_milli >= 1650991920000 AND unix_milli < 1651078380000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts) ) WHERE isNaN(rate_value) = 0 GROUP BY ts ORDER BY ts`
tmpl := `SELECT ts, %s(rate_value) as value FROM (SELECT ts, If((value - lagInFrame(value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (value - lagInFrame(value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as rate_value FROM(SELECT fingerprint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as value FROM event.metric INNER JOIN (SELECT DISTINCT fingerprint FROM event.series_1d WHERE metric_name IN ['name'] AND temporality = '' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['name'] AND unix_milli >= 1650991920000 AND unix_milli < 1651078380000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts) ) WHERE isNaN(rate_value) = 0 GROUP BY ts ORDER BY ts`
cases := []struct {
aggregateOperator v3.AggregateOperator
@@ -403,7 +403,7 @@ func TestBuildQueryWithDotInMetricAndAttributes(t *testing.T) {
},
},
},
expected: "SELECT *, now() AS ts FROM (SELECT avgIf(value, toUnixTimestamp(ts) != 0) as value, anyIf(ts, toUnixTimestamp(ts) != 0) AS timestamp FROM (SELECT `os.type`, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'os.type') as `os.type`, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['system.memory.usage'] AND temporality = '' AND __normalized = false AND unix_milli >= 1734998400000 AND unix_milli < 1735637880000 AND JSONExtractString(labels, 'os.type') = 'linux') as filtered_time_series USING fingerprint WHERE metric_name IN ['system.memory.usage'] AND unix_milli >= 1735036080000 AND unix_milli < 1735637880000 GROUP BY `os.type`, ts ORDER BY `os.type` asc, ts) )",
expected: "SELECT *, now() AS ts FROM (SELECT avgIf(value, toUnixTimestamp(ts) != 0) as value, anyIf(ts, toUnixTimestamp(ts) != 0) AS timestamp FROM (SELECT `os.type`, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'os.type') as `os.type`, fingerprint FROM event.series_1d WHERE metric_name IN ['system.memory.usage'] AND temporality = '' AND __normalized = false AND unix_milli >= 1734998400000 AND unix_milli < 1735637880000 AND JSONExtractString(labels, 'os.type') = 'linux') as filtered_time_series USING fingerprint WHERE metric_name IN ['system.memory.usage'] AND unix_milli >= 1735036080000 AND unix_milli < 1735637880000 GROUP BY `os.type`, ts ORDER BY `os.type` asc, ts) )",
},
{
name: "TestBuildQueryWithDotInMetricAndAttributes with dot in metric and attributes with rate_avg aggregation",
@@ -474,7 +474,7 @@ func TestBuildQueryWithDotInMetricAndAttributes(t *testing.T) {
},
},
},
expected: "SELECT *, now() AS ts FROM (SELECT avgIf(value, toUnixTimestamp(ts) != 0) as value, anyIf(ts, toUnixTimestamp(ts) != 0) AS timestamp FROM (SELECT `os.type`, ts, If((value - lagInFrame(value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (value - lagInFrame(value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as value FROM(SELECT `os.type`, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'os.type') as `os.type`, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['system.memory.usage'] AND temporality = '' AND __normalized = false AND unix_milli >= 1734998400000 AND unix_milli < 1735637880000 AND JSONExtractString(labels, 'os.type') = 'linux') as filtered_time_series USING fingerprint WHERE metric_name IN ['system.memory.usage'] AND unix_milli >= 1735036020000 AND unix_milli < 1735637880000 GROUP BY `os.type`, ts ORDER BY `os.type` asc, ts) WINDOW rate_window as (PARTITION BY `os.type` ORDER BY `os.type`, ts) ) )",
expected: "SELECT *, now() AS ts FROM (SELECT avgIf(value, toUnixTimestamp(ts) != 0) as value, anyIf(ts, toUnixTimestamp(ts) != 0) AS timestamp FROM (SELECT `os.type`, ts, If((value - lagInFrame(value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (value - lagInFrame(value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as value FROM(SELECT `os.type`, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'os.type') as `os.type`, fingerprint FROM event.series_1d WHERE metric_name IN ['system.memory.usage'] AND temporality = '' AND __normalized = false AND unix_milli >= 1734998400000 AND unix_milli < 1735637880000 AND JSONExtractString(labels, 'os.type') = 'linux') as filtered_time_series USING fingerprint WHERE metric_name IN ['system.memory.usage'] AND unix_milli >= 1735036020000 AND unix_milli < 1735637880000 GROUP BY `os.type`, ts ORDER BY `os.type` asc, ts) WINDOW rate_window as (PARTITION BY `os.type` ORDER BY `os.type`, ts) ) )",
},
}
for _, testCase := range cases {
@@ -51,7 +51,7 @@ func TestPrepareTableQuery(t *testing.T) {
},
start: 1701794980000,
end: 1701796780000,
expectedQueryContains: "SELECT ts, sum(per_series_value) as value FROM (SELECT fingerprint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['system_memory_usage'] AND temporality = 'Unspecified' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND JSONExtractString(labels, 'state') != 'idle') as filtered_time_series USING fingerprint WHERE metric_name IN ['system_memory_usage'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY ts ORDER BY ts ASC",
expectedQueryContains: "SELECT ts, sum(per_series_value) as value FROM (SELECT fingerprint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT fingerprint FROM event.series WHERE metric_name IN ['system_memory_usage'] AND temporality = 'Unspecified' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND JSONExtractString(labels, 'state') != 'idle') as filtered_time_series USING fingerprint WHERE metric_name IN ['system_memory_usage'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY ts ORDER BY ts ASC",
},
{
name: "test time aggregation = rate, space aggregation = sum, temporality = cumulative",
@@ -93,7 +93,7 @@ func TestPrepareTableQuery(t *testing.T) {
},
start: 1701794980000,
end: 1701796780000,
expectedQueryContains: "SELECT service_name, ts, sum(per_series_value) as value FROM (SELECT service_name, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['http_requests'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND like(JSONExtractString(labels, 'service_name'), '%payment_service%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['http_requests'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
expectedQueryContains: "SELECT service_name, ts, sum(per_series_value) as value FROM (SELECT service_name, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM event.series WHERE metric_name IN ['http_requests'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND like(JSONExtractString(labels, 'service_name'), '%payment_service%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['http_requests'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
},
{
name: "test time aggregation = avg, space aggregation = avg, temporality = unspecified, testing metrics and attribute name with dot",
@@ -148,7 +148,7 @@ func TestPrepareTableQuery(t *testing.T) {
},
start: 1735295140000,
end: 1735554340000,
expectedQueryContains: "SELECT state, ts, avg(per_series_value) as value FROM (SELECT fingerprint, any(state) as state, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(sum) / sum(count) as per_series_value FROM o11y_metrics.distributed_samples_v4_agg_5m INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'state') as state, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['system.memory.usage'] AND temporality = 'Unspecified' AND __normalized = false AND unix_milli >= 1735257600000 AND unix_milli < 1735554340000 AND JSONExtractString(labels, 'host.name') = 'o11y-host') as filtered_time_series USING fingerprint WHERE metric_name IN ['system.memory.usage'] AND unix_milli >= 1735295140000 AND unix_milli < 1735554340000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY state, ts ORDER BY state desc, ts ASC",
expectedQueryContains: "SELECT state, ts, avg(per_series_value) as value FROM (SELECT fingerprint, any(state) as state, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(sum) / sum(count) as per_series_value FROM event.metric_5m INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'state') as state, fingerprint FROM event.series_1d WHERE metric_name IN ['system.memory.usage'] AND temporality = 'Unspecified' AND __normalized = false AND unix_milli >= 1735257600000 AND unix_milli < 1735554340000 AND JSONExtractString(labels, 'host.name') = 'o11y-host') as filtered_time_series USING fingerprint WHERE metric_name IN ['system.memory.usage'] AND unix_milli >= 1735295140000 AND unix_milli < 1735554340000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY state, ts ORDER BY state desc, ts ASC",
},
}
@@ -66,7 +66,7 @@ func TestPrepareTimeAggregationSubQuery(t *testing.T) {
},
start: 1701794980000,
end: 1701796780000,
expectedQueryContains: "SELECT fingerprint, any(service_name) as service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['http_requests'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND JSONExtractString(labels, 'service_name') != 'payment_service' AND JSONExtractString(labels, 'endpoint') IN ['/paycallback','/payme','/paypal']) as filtered_time_series USING fingerprint WHERE metric_name IN ['http_requests'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts",
expectedQueryContains: "SELECT fingerprint, any(service_name) as service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM event.series WHERE metric_name IN ['http_requests'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND JSONExtractString(labels, 'service_name') != 'payment_service' AND JSONExtractString(labels, 'endpoint') IN ['/paycallback','/payme','/paypal']) as filtered_time_series USING fingerprint WHERE metric_name IN ['http_requests'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts",
},
{
name: "test time aggregation = rate, temporality = cumulative",
@@ -107,7 +107,7 @@ func TestPrepareTimeAggregationSubQuery(t *testing.T) {
},
start: 1701794980000,
end: 1701796780000,
expectedQueryContains: "SELECT service_name, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['http_requests'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND like(JSONExtractString(labels, 'service_name'), '%payment_service%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['http_requests'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)",
expectedQueryContains: "SELECT service_name, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM event.series WHERE metric_name IN ['http_requests'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND like(JSONExtractString(labels, 'service_name'), '%payment_service%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['http_requests'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)",
},
}
@@ -168,7 +168,7 @@ func TestPrepareTimeseriesQuery(t *testing.T) {
},
start: 1701794980000,
end: 1701796780000,
expectedQueryContains: "SELECT ts, sum(per_series_value) as value FROM (SELECT fingerprint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['system_memory_usage'] AND temporality = 'Unspecified' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND JSONExtractString(labels, 'state') != 'idle') as filtered_time_series USING fingerprint WHERE metric_name IN ['system_memory_usage'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY ts ORDER BY ts ASC",
expectedQueryContains: "SELECT ts, sum(per_series_value) as value FROM (SELECT fingerprint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT fingerprint FROM event.series WHERE metric_name IN ['system_memory_usage'] AND temporality = 'Unspecified' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND JSONExtractString(labels, 'state') != 'idle') as filtered_time_series USING fingerprint WHERE metric_name IN ['system_memory_usage'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY ts ORDER BY ts ASC",
},
{
name: "test time aggregation = rate, space aggregation = sum, temporality = cumulative",
@@ -210,7 +210,7 @@ func TestPrepareTimeseriesQuery(t *testing.T) {
},
start: 1701794980000,
end: 1701796780000,
expectedQueryContains: "SELECT service_name, ts, sum(per_series_value) as value FROM (SELECT service_name, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['http_requests'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND like(JSONExtractString(labels, 'service_name'), '%payment_service%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['http_requests'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
expectedQueryContains: "SELECT service_name, ts, sum(per_series_value) as value FROM (SELECT service_name, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM event.series WHERE metric_name IN ['http_requests'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND like(JSONExtractString(labels, 'service_name'), '%payment_service%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['http_requests'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
},
{
name: "test time aggregation = avg, space aggregation = avg, temporality = unspecified, testing metrics and attribute name with dot",
@@ -265,7 +265,7 @@ func TestPrepareTimeseriesQuery(t *testing.T) {
},
start: 1735295140000,
end: 1735554340000,
expectedQueryContains: "SELECT state, ts, avg(per_series_value) as value FROM (SELECT fingerprint, any(state) as state, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(sum) / sum(count) as per_series_value FROM o11y_metrics.distributed_samples_v4_agg_5m INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'state') as state, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['system.memory.usage'] AND temporality = 'Unspecified' AND __normalized = false AND unix_milli >= 1735257600000 AND unix_milli < 1735554340000 AND JSONExtractString(labels, 'host.name') = 'o11y-host') as filtered_time_series USING fingerprint WHERE metric_name IN ['system.memory.usage'] AND unix_milli >= 1735295140000 AND unix_milli < 1735554340000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY state, ts ORDER BY state desc, ts ASC",
expectedQueryContains: "SELECT state, ts, avg(per_series_value) as value FROM (SELECT fingerprint, any(state) as state, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(sum) / sum(count) as per_series_value FROM event.metric_5m INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'state') as state, fingerprint FROM event.series_1d WHERE metric_name IN ['system.memory.usage'] AND temporality = 'Unspecified' AND __normalized = false AND unix_milli >= 1735257600000 AND unix_milli < 1735554340000 AND JSONExtractString(labels, 'host.name') = 'o11y-host') as filtered_time_series USING fingerprint WHERE metric_name IN ['system.memory.usage'] AND unix_milli >= 1735295140000 AND unix_milli < 1735554340000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY state, ts ORDER BY state desc, ts ASC",
},
}
@@ -53,7 +53,7 @@ func TestPrepareTableQuery(t *testing.T) {
},
start: 1701794980000,
end: 1701796780000,
expectedQueryContains: "SELECT ts, sum(per_series_value) as value FROM (SELECT fingerprint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['system_memory_usage'] AND temporality = 'Unspecified' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND JSONExtractString(labels, 'state') != 'idle') as filtered_time_series USING fingerprint WHERE metric_name IN ['system_memory_usage'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY ts ORDER BY ts ASC",
expectedQueryContains: "SELECT ts, sum(per_series_value) as value FROM (SELECT fingerprint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT fingerprint FROM event.series WHERE metric_name IN ['system_memory_usage'] AND temporality = 'Unspecified' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND JSONExtractString(labels, 'state') != 'idle') as filtered_time_series USING fingerprint WHERE metric_name IN ['system_memory_usage'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY ts ORDER BY ts ASC",
},
{
name: "test time aggregation = rate, space aggregation = sum, temporality = delta",
@@ -95,7 +95,7 @@ func TestPrepareTableQuery(t *testing.T) {
},
start: 1701794980000,
end: 1701796780000,
expectedQueryContains: "SELECT service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['http_requests'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND like(JSONExtractString(labels, 'service_name'), '%payment_service%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['http_requests'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
expectedQueryContains: "SELECT service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM event.series WHERE metric_name IN ['http_requests'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND like(JSONExtractString(labels, 'service_name'), '%payment_service%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['http_requests'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
},
{
name: "test time aggregation = rate, space aggregation = avg, temporality = delta, testing metrics and attribute name with dot",
@@ -143,7 +143,7 @@ func TestPrepareTableQuery(t *testing.T) {
},
start: 1701794980000,
end: 1701796780000,
expectedQueryContains: "SELECT ts, avg(per_series_value) as value FROM (SELECT fingerprint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['o11y.latency.sum'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND JSONExtractString(labels, 'host.name') = '4f6ec470feea') as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y.latency.sum'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY ts ORDER BY ts ASC",
expectedQueryContains: "SELECT ts, avg(per_series_value) as value FROM (SELECT fingerprint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT fingerprint FROM event.series WHERE metric_name IN ['o11y.latency.sum'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND JSONExtractString(labels, 'host.name') = '4f6ec470feea') as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y.latency.sum'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY ts ORDER BY ts ASC",
},
}
@@ -66,7 +66,7 @@ func TestPrepareTimeAggregationSubQuery(t *testing.T) {
},
start: 1701794980000,
end: 1701796780000,
expectedQueryContains: "SELECT fingerprint, any(service_name) as service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['http_requests'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND JSONExtractString(labels, 'service_name') != 'payment_service' AND JSONExtractString(labels, 'endpoint') IN ['/paycallback','/payme','/paypal']) as filtered_time_series USING fingerprint WHERE metric_name IN ['http_requests'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts",
expectedQueryContains: "SELECT fingerprint, any(service_name) as service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM event.series WHERE metric_name IN ['http_requests'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND JSONExtractString(labels, 'service_name') != 'payment_service' AND JSONExtractString(labels, 'endpoint') IN ['/paycallback','/payme','/paypal']) as filtered_time_series USING fingerprint WHERE metric_name IN ['http_requests'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts",
},
{
name: "test time aggregation = rate, temporality = delta",
@@ -107,7 +107,7 @@ func TestPrepareTimeAggregationSubQuery(t *testing.T) {
},
start: 1701794980000,
end: 1701796780000,
expectedQueryContains: "SELECT fingerprint, any(service_name) as service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['http_requests'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND like(JSONExtractString(labels, 'service_name'), '%payment_service%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['http_requests'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts",
expectedQueryContains: "SELECT fingerprint, any(service_name) as service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM event.series WHERE metric_name IN ['http_requests'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND like(JSONExtractString(labels, 'service_name'), '%payment_service%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['http_requests'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts",
},
}
@@ -168,7 +168,7 @@ func TestPrepareTimeseriesQuery(t *testing.T) {
},
start: 1701794980000,
end: 1701796780000,
expectedQueryContains: "SELECT ts, sum(per_series_value) as value FROM (SELECT fingerprint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['system_memory_usage'] AND temporality = 'Unspecified' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND JSONExtractString(labels, 'state') != 'idle') as filtered_time_series USING fingerprint WHERE metric_name IN ['system_memory_usage'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY ts ORDER BY ts ASC",
expectedQueryContains: "SELECT ts, sum(per_series_value) as value FROM (SELECT fingerprint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT fingerprint FROM event.series WHERE metric_name IN ['system_memory_usage'] AND temporality = 'Unspecified' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND JSONExtractString(labels, 'state') != 'idle') as filtered_time_series USING fingerprint WHERE metric_name IN ['system_memory_usage'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY ts ORDER BY ts ASC",
},
{
name: "test time aggregation = rate, space aggregation = sum, temporality = delta",
@@ -210,7 +210,7 @@ func TestPrepareTimeseriesQuery(t *testing.T) {
},
start: 1701794980000,
end: 1701796780000,
expectedQueryContains: "SELECT service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['http_requests'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND like(JSONExtractString(labels, 'service_name'), '%payment_service%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['http_requests'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
expectedQueryContains: "SELECT service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM event.series WHERE metric_name IN ['http_requests'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND like(JSONExtractString(labels, 'service_name'), '%payment_service%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['http_requests'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
},
{
name: "test time aggregation = rate, space aggregation percentile99, type = ExponentialHistogram",
@@ -244,7 +244,7 @@ func TestPrepareTimeseriesQuery(t *testing.T) {
},
start: 1701794980000,
end: 1701796780000,
expectedQueryContains: "SELECT service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, quantilesDDMerge(0.01, 0.990000)(sketch)[1] as value FROM o11y_metrics.distributed_exp_hist INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['o11y_latency'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
expectedQueryContains: "SELECT service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, quantilesDDMerge(0.01, 0.990000)(sketch)[1] as value FROM event.histogram INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM event.series WHERE metric_name IN ['o11y_latency'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency'] AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 AND bitAnd(flags, 1) = 0 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
},
{
name: "test time aggregation = rate, space aggregation = max, temporality = delta, testing metrics and attribute name with dot",
@@ -299,7 +299,7 @@ func TestPrepareTimeseriesQuery(t *testing.T) {
},
start: 1735036101000,
end: 1735637901000,
expectedQueryContains: "SELECT `host.name`, ts, max(per_series_value) as value FROM (SELECT fingerprint, any(`host.name`) as `host.name`, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(sum)/60 as per_series_value FROM o11y_metrics.distributed_samples_v4_agg_5m INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'host.name') as `host.name`, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['o11y.latency.sum'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1734998400000 AND unix_milli < 1735637901000 AND JSONExtractString(labels, 'host_name') = '4f6ec470feea') as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y.latency.sum'] AND unix_milli >= 1735036101000 AND unix_milli < 1735637901000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY `host.name`, ts ORDER BY `host.name` ASC, ts ASC",
expectedQueryContains: "SELECT `host.name`, ts, max(per_series_value) as value FROM (SELECT fingerprint, any(`host.name`) as `host.name`, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(sum)/60 as per_series_value FROM event.metric_5m INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'host.name') as `host.name`, fingerprint FROM event.series_1d WHERE metric_name IN ['o11y.latency.sum'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1734998400000 AND unix_milli < 1735637901000 AND JSONExtractString(labels, 'host_name') = '4f6ec470feea') as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y.latency.sum'] AND unix_milli >= 1735036101000 AND unix_milli < 1735637901000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY `host.name`, ts ORDER BY `host.name` ASC, ts ASC",
},
}
@@ -48,7 +48,7 @@ func TestPrepareMetricQueryCumulativeRatePreAgg(t *testing.T) {
TimeAggregation: v3.TimeAggregationRate,
SpaceAggregation: v3.SpaceAggregationSum,
},
expectedQueryContains: "SELECT service_name, ts, sum(per_series_value) as value FROM (SELECT service_name, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['o11y_calls_total'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_calls_total'] AND unix_milli >= 1650991920000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
expectedQueryContains: "SELECT service_name, ts, sum(per_series_value) as value FROM (SELECT service_name, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM event.series_1d WHERE metric_name IN ['o11y_calls_total'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_calls_total'] AND unix_milli >= 1650991920000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
},
{
name: "test time aggregation = rate, space aggregation = sum, temporality = cumulative, multiple group by",
@@ -81,7 +81,7 @@ func TestPrepareMetricQueryCumulativeRatePreAgg(t *testing.T) {
TimeAggregation: v3.TimeAggregationRate,
SpaceAggregation: v3.SpaceAggregationSum,
},
expectedQueryContains: "SELECT service_name, endpoint, ts, sum(per_series_value) as value FROM (SELECT service_name, endpoint, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, any(endpoint) as endpoint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'endpoint') as endpoint, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['o11y_calls_total'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_calls_total'] AND unix_milli >= 1650991920000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY service_name, endpoint, ts ORDER BY service_name ASC, endpoint ASC, ts ASC",
expectedQueryContains: "SELECT service_name, endpoint, ts, sum(per_series_value) as value FROM (SELECT service_name, endpoint, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, any(endpoint) as endpoint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'endpoint') as endpoint, fingerprint FROM event.series_1d WHERE metric_name IN ['o11y_calls_total'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_calls_total'] AND unix_milli >= 1650991920000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY service_name, endpoint, ts ORDER BY service_name ASC, endpoint ASC, ts ASC",
},
}
@@ -121,7 +121,7 @@ func TestPrepareMetricQueryDeltaRatePreAgg(t *testing.T) {
TimeAggregation: v3.TimeAggregationRate,
SpaceAggregation: v3.SpaceAggregationSum,
},
expectedQueryContains: "SELECT toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['o11y_calls_total'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_calls_total'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY ts ORDER BY ts ASC",
expectedQueryContains: "SELECT toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM event.metric INNER JOIN (SELECT DISTINCT fingerprint FROM event.series_1d WHERE metric_name IN ['o11y_calls_total'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_calls_total'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY ts ORDER BY ts ASC",
},
{
name: "test time aggregation = rate, space aggregation = sum, temporality = delta, group by service_name",
@@ -147,7 +147,7 @@ func TestPrepareMetricQueryDeltaRatePreAgg(t *testing.T) {
TimeAggregation: v3.TimeAggregationRate,
SpaceAggregation: v3.SpaceAggregationSum,
},
expectedQueryContains: "SELECT service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['o11y_calls_total'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_calls_total'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
expectedQueryContains: "SELECT service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM event.series_1d WHERE metric_name IN ['o11y_calls_total'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_calls_total'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
},
}
@@ -201,7 +201,7 @@ func TestPrepreMetricQueryCumulativeQuantilePreAgg(t *testing.T) {
Disabled: false,
SpaceAggregation: v3.SpaceAggregationPercentile99,
},
expectedQueryContains: "SELECT service_name, ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT service_name, le, ts, sum(per_series_value) as value FROM (SELECT service_name, le, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, any(le) as le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'le') as le, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY service_name, le, ts ORDER BY service_name ASC, le ASC, ts ASC) GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
expectedQueryContains: "SELECT service_name, ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT service_name, le, ts, sum(per_series_value) as value FROM (SELECT service_name, le, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, any(le) as le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'le') as le, fingerprint FROM event.series_1d WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY service_name, le, ts ORDER BY service_name ASC, le ASC, ts ASC) GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
},
{
name: "test temporality = cumulative, quantile = 0.99 without group by",
@@ -231,7 +231,7 @@ func TestPrepreMetricQueryCumulativeQuantilePreAgg(t *testing.T) {
Disabled: false,
SpaceAggregation: v3.SpaceAggregationPercentile99,
},
expectedQueryContains: "SELECT ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT le, ts, sum(per_series_value) as value FROM (SELECT le, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(le) as le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'le') as le, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY le, ts ORDER BY le ASC, ts ASC) GROUP BY ts ORDER BY ts ASC",
expectedQueryContains: "SELECT ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT le, ts, sum(per_series_value) as value FROM (SELECT le, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(le) as le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'le') as le, fingerprint FROM event.series_1d WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY le, ts ORDER BY le ASC, ts ASC) GROUP BY ts ORDER BY ts ASC",
},
}
@@ -285,7 +285,7 @@ func TestPrepreMetricQueryDeltaQuantilePreAgg(t *testing.T) {
Disabled: false,
SpaceAggregation: v3.SpaceAggregationPercentile99,
},
expectedQueryContains: "SELECT service_name, ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT service_name, le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'le') as le, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY service_name, le, ts ORDER BY service_name ASC, le ASC, ts ASC) GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
expectedQueryContains: "SELECT service_name, ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT service_name, le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'le') as le, fingerprint FROM event.series_1d WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY service_name, le, ts ORDER BY service_name ASC, le ASC, ts ASC) GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
},
{
name: "test temporality = delta, quantile = 0.99 no group by",
@@ -315,7 +315,7 @@ func TestPrepreMetricQueryDeltaQuantilePreAgg(t *testing.T) {
Disabled: false,
SpaceAggregation: v3.SpaceAggregationPercentile99,
},
expectedQueryContains: "SELECT ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'le') as le, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY le, ts ORDER BY le ASC, ts ASC) GROUP BY ts ORDER BY ts ASC",
expectedQueryContains: "SELECT ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'le') as le, fingerprint FROM event.series_1d WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY le, ts ORDER BY le ASC, ts ASC) GROUP BY ts ORDER BY ts ASC",
},
}
@@ -355,7 +355,7 @@ func TestPrepareMetricQueryGaugePreAgg(t *testing.T) {
SpaceAggregation: v3.SpaceAggregationSum,
Disabled: false,
},
expectedQueryContains: "SELECT ts, sum(per_series_value) as value FROM (SELECT fingerprint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['system_cpu_usage'] AND temporality = 'Unspecified' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['system_cpu_usage'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY ts ORDER BY ts ASC",
expectedQueryContains: "SELECT ts, sum(per_series_value) as value FROM (SELECT fingerprint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT fingerprint FROM event.series_1d WHERE metric_name IN ['system_cpu_usage'] AND temporality = 'Unspecified' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['system_cpu_usage'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY ts ORDER BY ts ASC",
},
{
name: "test gauge query with group by host_name",
@@ -381,7 +381,7 @@ func TestPrepareMetricQueryGaugePreAgg(t *testing.T) {
Expression: "A",
Disabled: false,
},
expectedQueryContains: "SELECT host_name, ts, sum(per_series_value) as value FROM (SELECT fingerprint, any(host_name) as host_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'host_name') as host_name, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['system_cpu_usage'] AND temporality = 'Unspecified' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['system_cpu_usage'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY host_name, ts ORDER BY host_name ASC, ts ASC",
expectedQueryContains: "SELECT host_name, ts, sum(per_series_value) as value FROM (SELECT fingerprint, any(host_name) as host_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'host_name') as host_name, fingerprint FROM event.series_1d WHERE metric_name IN ['system_cpu_usage'] AND temporality = 'Unspecified' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['system_cpu_usage'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY host_name, ts ORDER BY host_name ASC, ts ASC",
},
}
@@ -33,7 +33,7 @@ func TestPrepareTimeseriesFilterQuery(t *testing.T) {
Disabled: false,
// remaining struct fields are not needed here
},
expectedQueryContains: "SELECT DISTINCT fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['http_requests'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1706428800000 AND unix_milli < 1706434026000",
expectedQueryContains: "SELECT DISTINCT fingerprint FROM event.series WHERE metric_name IN ['http_requests'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1706428800000 AND unix_milli < 1706434026000",
},
{
name: "test prepare time series with no filters and group by",
@@ -58,7 +58,7 @@ func TestPrepareTimeseriesFilterQuery(t *testing.T) {
Disabled: false,
// remaining struct fields are not needed here
},
expectedQueryContains: "SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['http_requests'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1706428800000 AND unix_milli < 1706434026000",
expectedQueryContains: "SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM event.series WHERE metric_name IN ['http_requests'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1706428800000 AND unix_milli < 1706434026000",
},
{
name: "test prepare time series with no filters and multiple group by",
@@ -90,7 +90,7 @@ func TestPrepareTimeseriesFilterQuery(t *testing.T) {
Disabled: false,
// remaining struct fields are not needed here
},
expectedQueryContains: "SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'endpoint') as endpoint, fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['http_requests'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1706428800000 AND unix_milli < 1706434026000",
expectedQueryContains: "SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'endpoint') as endpoint, fingerprint FROM event.series WHERE metric_name IN ['http_requests'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1706428800000 AND unix_milli < 1706434026000",
},
{
name: "test prepare time series with filters and multiple group by",
@@ -138,7 +138,7 @@ func TestPrepareTimeseriesFilterQuery(t *testing.T) {
Disabled: false,
// remaining struct fields are not needed here
},
expectedQueryContains: "SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['http_requests'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1706428800000 AND unix_milli < 1706434026000 AND JSONExtractString(labels, 'service_name') != 'payment_service' AND JSONExtractString(labels, 'endpoint') IN ['/paycallback','/payme','/paypal']",
expectedQueryContains: "SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM event.series WHERE metric_name IN ['http_requests'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1706428800000 AND unix_milli < 1706434026000 AND JSONExtractString(labels, 'service_name') != 'payment_service' AND JSONExtractString(labels, 'endpoint') IN ['/paycallback','/payme','/paypal']",
},
{
name: "test prepare time series with filters and multiple group by",
@@ -186,7 +186,7 @@ func TestPrepareTimeseriesFilterQuery(t *testing.T) {
Disabled: false,
// remaining struct fields are not needed here
},
expectedQueryContains: "SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['http_requests'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1706428800000 AND unix_milli < 1706434026000 AND ilike(JSONExtractString(labels, 'service_name'), 'payment_service') AND notILike(JSONExtractString(labels, 'endpoint'), 'payment_service')",
expectedQueryContains: "SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM event.series WHERE metric_name IN ['http_requests'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1706428800000 AND unix_milli < 1706434026000 AND ilike(JSONExtractString(labels, 'service_name'), 'payment_service') AND notILike(JSONExtractString(labels, 'endpoint'), 'payment_service')",
},
}
@@ -241,7 +241,7 @@ func TestPrepareMetricQueryCumulativeRate(t *testing.T) {
TimeAggregation: v3.TimeAggregationRate,
SpaceAggregation: v3.SpaceAggregationSum,
},
expectedQueryContains: "SELECT service_name, ts, sum(per_series_value) as value FROM (SELECT service_name, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['o11y_calls_total'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_calls_total'] AND unix_milli >= 1650991920000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
expectedQueryContains: "SELECT service_name, ts, sum(per_series_value) as value FROM (SELECT service_name, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM event.series_1d WHERE metric_name IN ['o11y_calls_total'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_calls_total'] AND unix_milli >= 1650991920000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
},
{
name: "test time aggregation = rate, space aggregation = sum, temporality = cumulative, multiple group by",
@@ -274,7 +274,7 @@ func TestPrepareMetricQueryCumulativeRate(t *testing.T) {
TimeAggregation: v3.TimeAggregationRate,
SpaceAggregation: v3.SpaceAggregationSum,
},
expectedQueryContains: "SELECT service_name, endpoint, ts, sum(per_series_value) as value FROM (SELECT service_name, endpoint, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, any(endpoint) as endpoint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'endpoint') as endpoint, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['o11y_calls_total'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_calls_total'] AND unix_milli >= 1650991920000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY service_name, endpoint, ts ORDER BY service_name ASC, endpoint ASC, ts ASC",
expectedQueryContains: "SELECT service_name, endpoint, ts, sum(per_series_value) as value FROM (SELECT service_name, endpoint, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, any(endpoint) as endpoint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'endpoint') as endpoint, fingerprint FROM event.series_1d WHERE metric_name IN ['o11y_calls_total'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_calls_total'] AND unix_milli >= 1650991920000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY service_name, endpoint, ts ORDER BY service_name ASC, endpoint ASC, ts ASC",
},
}
@@ -314,7 +314,7 @@ func TestPrepareMetricQueryDeltaRate(t *testing.T) {
TimeAggregation: v3.TimeAggregationRate,
SpaceAggregation: v3.SpaceAggregationSum,
},
expectedQueryContains: "SELECT toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['o11y_calls_total'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_calls_total'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY ts ORDER BY ts ASC",
expectedQueryContains: "SELECT toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM event.metric INNER JOIN (SELECT DISTINCT fingerprint FROM event.series_1d WHERE metric_name IN ['o11y_calls_total'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_calls_total'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY ts ORDER BY ts ASC",
},
{
name: "test time aggregation = rate, space aggregation = sum, temporality = delta, group by service_name",
@@ -340,7 +340,7 @@ func TestPrepareMetricQueryDeltaRate(t *testing.T) {
TimeAggregation: v3.TimeAggregationRate,
SpaceAggregation: v3.SpaceAggregationSum,
},
expectedQueryContains: "SELECT service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['o11y_calls_total'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_calls_total'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
expectedQueryContains: "SELECT service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM event.series_1d WHERE metric_name IN ['o11y_calls_total'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_calls_total'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
},
}
@@ -394,7 +394,7 @@ func TestPrepreMetricQueryCumulativeQuantile(t *testing.T) {
Disabled: false,
SpaceAggregation: v3.SpaceAggregationPercentile99,
},
expectedQueryContains: "SELECT service_name, ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT service_name, le, ts, sum(per_series_value) as value FROM (SELECT service_name, le, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, any(le) as le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'le') as le, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY service_name, le, ts ORDER BY service_name ASC, le ASC, ts ASC) GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
expectedQueryContains: "SELECT service_name, ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT service_name, le, ts, sum(per_series_value) as value FROM (SELECT service_name, le, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, any(le) as le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'le') as le, fingerprint FROM event.series_1d WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY service_name, le, ts ORDER BY service_name ASC, le ASC, ts ASC) GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
},
{
name: "test temporality = cumulative, quantile = 0.99 without group by",
@@ -424,7 +424,7 @@ func TestPrepreMetricQueryCumulativeQuantile(t *testing.T) {
Disabled: false,
SpaceAggregation: v3.SpaceAggregationPercentile99,
},
expectedQueryContains: "SELECT ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT le, ts, sum(per_series_value) as value FROM (SELECT le, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(le) as le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'le') as le, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY le, ts ORDER BY le ASC, ts ASC) GROUP BY ts ORDER BY ts ASC",
expectedQueryContains: "SELECT ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT le, ts, sum(per_series_value) as value FROM (SELECT le, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(le) as le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'le') as le, fingerprint FROM event.series_1d WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY le, ts ORDER BY le ASC, ts ASC) GROUP BY ts ORDER BY ts ASC",
},
}
@@ -478,7 +478,7 @@ func TestPrepreMetricQueryDeltaQuantile(t *testing.T) {
Disabled: false,
SpaceAggregation: v3.SpaceAggregationPercentile99,
},
expectedQueryContains: "SELECT service_name, ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT service_name, le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'le') as le, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY service_name, le, ts ORDER BY service_name ASC, le ASC, ts ASC) GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
expectedQueryContains: "SELECT service_name, ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT service_name, le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'le') as le, fingerprint FROM event.series_1d WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY service_name, le, ts ORDER BY service_name ASC, le ASC, ts ASC) GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC",
},
{
name: "test temporality = delta, quantile = 0.99 no group by",
@@ -508,7 +508,7 @@ func TestPrepreMetricQueryDeltaQuantile(t *testing.T) {
Disabled: false,
SpaceAggregation: v3.SpaceAggregationPercentile99,
},
expectedQueryContains: "SELECT ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'le') as le, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY le, ts ORDER BY le ASC, ts ASC) GROUP BY ts ORDER BY ts ASC",
expectedQueryContains: "SELECT ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'le') as le, fingerprint FROM event.series_1d WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY le, ts ORDER BY le ASC, ts ASC) GROUP BY ts ORDER BY ts ASC",
},
}
@@ -548,7 +548,7 @@ func TestPrepareMetricQueryGauge(t *testing.T) {
SpaceAggregation: v3.SpaceAggregationSum,
Disabled: false,
},
expectedQueryContains: "SELECT ts, sum(per_series_value) as value FROM (SELECT fingerprint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['system_cpu_usage'] AND temporality = 'Unspecified' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['system_cpu_usage'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY ts ORDER BY ts ASC",
expectedQueryContains: "SELECT ts, sum(per_series_value) as value FROM (SELECT fingerprint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT fingerprint FROM event.series_1d WHERE metric_name IN ['system_cpu_usage'] AND temporality = 'Unspecified' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['system_cpu_usage'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY ts ORDER BY ts ASC",
},
{
name: "test value filter with string value",
@@ -605,7 +605,7 @@ func TestPrepareMetricQueryGauge(t *testing.T) {
Expression: "A",
Disabled: false,
},
expectedQueryContains: "SELECT host_name, ts, sum(per_series_value) as value FROM (SELECT fingerprint, any(host_name) as host_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'host_name') as host_name, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['system_cpu_usage'] AND temporality = 'Unspecified' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['system_cpu_usage'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY host_name, ts ORDER BY host_name ASC, ts ASC",
expectedQueryContains: "SELECT host_name, ts, sum(per_series_value) as value FROM (SELECT fingerprint, any(host_name) as host_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'host_name') as host_name, fingerprint FROM event.series_1d WHERE metric_name IN ['system_cpu_usage'] AND temporality = 'Unspecified' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['system_cpu_usage'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY host_name, ts ORDER BY host_name ASC, ts ASC",
},
{
name: "test gauge query with multiple group by with metric and attribute name containing dot",
@@ -674,7 +674,7 @@ func TestPrepareMetricQueryGauge(t *testing.T) {
ReduceTo: v3.ReduceToOperatorAvg,
Having: []v3.Having{},
},
expectedQueryContains: "SELECT `os.type`, state, `host.name`, ts, max(per_series_value) as value FROM (SELECT fingerprint, any(`os.type`) as `os.type`, any(state) as state, any(`host.name`) as `host.name`, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'os.type') as `os.type`, JSONExtractString(labels, 'state') as state, JSONExtractString(labels, 'host.name') as `host.name`, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['system.memory.usage'] AND temporality = 'Unspecified' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND JSONExtractString(labels, 'host.name') = 'o11y-host') as filtered_time_series USING fingerprint WHERE metric_name IN ['system.memory.usage'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY `os.type`, state, `host.name`, ts ORDER BY `os.type` desc, state asc, `host.name` ASC, ts ASC",
expectedQueryContains: "SELECT `os.type`, state, `host.name`, ts, max(per_series_value) as value FROM (SELECT fingerprint, any(`os.type`) as `os.type`, any(state) as state, any(`host.name`) as `host.name`, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'os.type') as `os.type`, JSONExtractString(labels, 'state') as state, JSONExtractString(labels, 'host.name') as `host.name`, fingerprint FROM event.series_1d WHERE metric_name IN ['system.memory.usage'] AND temporality = 'Unspecified' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND JSONExtractString(labels, 'host.name') = 'o11y-host') as filtered_time_series USING fingerprint WHERE metric_name IN ['system.memory.usage'] AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY `os.type`, state, `host.name`, ts ORDER BY `os.type` desc, state asc, `host.name` ASC, ts ASC",
},
}
@@ -753,7 +753,7 @@ func TestPrepareMetricQueryValueTypePanelWithGroupBY(t *testing.T) {
},
},
},
expectedQueryContains: "SELECT max(value) as aggregated_value, ts FROM (SELECT state, ts, avg(per_series_value) as value FROM (SELECT fingerprint, any(state) as state, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, anyLast(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'state') as state, fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['system_memory_usage'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1735891200000 AND unix_milli < 1735894800000 AND JSONExtractString(labels, 'os_type') = 'linux') as filtered_time_series USING fingerprint WHERE metric_name IN ['system_memory_usage'] AND unix_milli >= 1735891800000 AND unix_milli < 1735894800000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY state, ts ORDER BY state desc, ts ASC) GROUP BY ts ORDER BY ts",
expectedQueryContains: "SELECT max(value) as aggregated_value, ts FROM (SELECT state, ts, avg(per_series_value) as value FROM (SELECT fingerprint, any(state) as state, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, anyLast(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'state') as state, fingerprint FROM event.series WHERE metric_name IN ['system_memory_usage'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1735891200000 AND unix_milli < 1735894800000 AND JSONExtractString(labels, 'os_type') = 'linux') as filtered_time_series USING fingerprint WHERE metric_name IN ['system_memory_usage'] AND unix_milli >= 1735891800000 AND unix_milli < 1735894800000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY state, ts ORDER BY state desc, ts ASC) GROUP BY ts ORDER BY ts",
},
{
name: "test temporality = cumulative, panel = value, series agg = max group by state, host_name",
@@ -819,7 +819,7 @@ func TestPrepareMetricQueryValueTypePanelWithGroupBY(t *testing.T) {
},
},
},
expectedQueryContains: "SELECT max(value) as aggregated_value, ts FROM (SELECT state, host_name, ts, avg(per_series_value) as value FROM (SELECT fingerprint, any(state) as state, any(host_name) as host_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, anyLast(value) as per_series_value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'state') as state, JSONExtractString(labels, 'host_name') as host_name, fingerprint FROM o11y_metrics.time_series_v4 WHERE metric_name IN ['system_memory_usage'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1735891200000 AND unix_milli < 1735894800000 AND JSONExtractString(labels, 'os_type') = 'linux') as filtered_time_series USING fingerprint WHERE metric_name IN ['system_memory_usage'] AND unix_milli >= 1735891800000 AND unix_milli < 1735894800000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY state, host_name, ts ORDER BY state desc, host_name ASC, ts ASC) GROUP BY ts ORDER BY ts",
expectedQueryContains: "SELECT max(value) as aggregated_value, ts FROM (SELECT state, host_name, ts, avg(per_series_value) as value FROM (SELECT fingerprint, any(state) as state, any(host_name) as host_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, anyLast(value) as per_series_value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'state') as state, JSONExtractString(labels, 'host_name') as host_name, fingerprint FROM event.series WHERE metric_name IN ['system_memory_usage'] AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1735891200000 AND unix_milli < 1735894800000 AND JSONExtractString(labels, 'os_type') = 'linux') as filtered_time_series USING fingerprint WHERE metric_name IN ['system_memory_usage'] AND unix_milli >= 1735891800000 AND unix_milli < 1735894800000 AND bitAnd(flags, 1) = 0 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY state, host_name, ts ORDER BY state desc, host_name ASC, ts ASC) GROUP BY ts ORDER BY ts",
},
}
@@ -337,7 +337,7 @@ func TestBuildQueryWithThreeOrMoreQueriesRefAndFormula(t *testing.T) {
qb := NewQueryBuilder(qbOptions)
queries, err := qb.PrepareQueries(q)
require.Contains(t, queries["F1"], "SELECT A.`os.type` as `os.type`, A.`ts` as `ts`, A.value + B.value as value FROM (SELECT `os.type`, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'os.type') as `os.type`, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['system.memory.usage'] AND temporality = '' AND __normalized = false AND unix_milli >= 1734998400000 AND unix_milli < 1735637880000 AND JSONExtractString(labels, 'os.type') = 'linux') as filtered_time_series USING fingerprint WHERE metric_name IN ['system.memory.usage'] AND unix_milli >= 1735036080000 AND unix_milli < 1735637880000 GROUP BY `os.type`, ts ORDER BY `os.type` ASC, ts) as A INNER JOIN (SELECT * FROM (SELECT `os.type`, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value) as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'os.type') as `os.type`, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['system.network.io'] AND temporality = '' AND __normalized = false AND unix_milli >= 1734998400000 AND unix_milli < 1735637880000) as filtered_time_series USING fingerprint WHERE metric_name IN ['system.network.io'] AND unix_milli >= 1735036020000 AND unix_milli < 1735637880000 GROUP BY `os.type`, ts ORDER BY `os.type` ASC, ts) HAVING value > 4) as B ON A.`os.type` = B.`os.type` AND A.`ts` = B.`ts`")
require.Contains(t, queries["F1"], "SELECT A.`os.type` as `os.type`, A.`ts` as `ts`, A.value + B.value as value FROM (SELECT `os.type`, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'os.type') as `os.type`, fingerprint FROM event.series_1d WHERE metric_name IN ['system.memory.usage'] AND temporality = '' AND __normalized = false AND unix_milli >= 1734998400000 AND unix_milli < 1735637880000 AND JSONExtractString(labels, 'os.type') = 'linux') as filtered_time_series USING fingerprint WHERE metric_name IN ['system.memory.usage'] AND unix_milli >= 1735036080000 AND unix_milli < 1735637880000 GROUP BY `os.type`, ts ORDER BY `os.type` ASC, ts) as A INNER JOIN (SELECT * FROM (SELECT `os.type`, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value) as value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'os.type') as `os.type`, fingerprint FROM event.series_1d WHERE metric_name IN ['system.network.io'] AND temporality = '' AND __normalized = false AND unix_milli >= 1734998400000 AND unix_milli < 1735637880000) as filtered_time_series USING fingerprint WHERE metric_name IN ['system.network.io'] AND unix_milli >= 1735036020000 AND unix_milli < 1735637880000 GROUP BY `os.type`, ts ORDER BY `os.type` ASC, ts) HAVING value > 4) as B ON A.`os.type` = B.`os.type` AND A.`ts` = B.`ts`")
require.NoError(t, err)
})
@@ -386,7 +386,7 @@ func TestDeltaQueryBuilder(t *testing.T) {
},
},
queryToTest: "A",
expected: "SELECT toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['o11y_latency_count'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND JSONExtractString(labels, 'service_name') IN ['frontend'] AND JSONExtractString(labels, 'operation') IN ['HTTP GET /dispatch'] AND JSONExtractString(labels, '__temporality__') = 'Delta') as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_count'] AND unix_milli >= 1650991980000 AND unix_milli <= 1651078380000 GROUP BY ts ORDER BY ts",
expected: "SELECT toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM event.metric INNER JOIN (SELECT DISTINCT fingerprint FROM event.series_1d WHERE metric_name IN ['o11y_latency_count'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND JSONExtractString(labels, 'service_name') IN ['frontend'] AND JSONExtractString(labels, 'operation') IN ['HTTP GET /dispatch'] AND JSONExtractString(labels, '__temporality__') = 'Delta') as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_count'] AND unix_milli >= 1650991980000 AND unix_milli <= 1651078380000 GROUP BY ts ORDER BY ts",
},
{
name: "TestQueryWithExpression - Error rate",
@@ -456,7 +456,7 @@ func TestDeltaQueryBuilder(t *testing.T) {
},
},
queryToTest: "C",
expected: "SELECT A.`ts` as `ts`, A.value * 100 / B.value as value FROM (SELECT toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['o11y_latency_count'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND JSONExtractString(labels, 'service_name') IN ['frontend'] AND JSONExtractString(labels, 'operation') IN ['HTTP GET /dispatch'] AND JSONExtractString(labels, 'status_code') IN ['STATUS_CODE_ERROR'] AND JSONExtractString(labels, '__temporality__') = 'Delta') as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_count'] AND unix_milli >= 1650991980000 AND unix_milli <= 1651078380000 GROUP BY ts ORDER BY ts) as A INNER JOIN (SELECT toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['o11y_latency_count'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND JSONExtractString(labels, 'service_name') IN ['frontend'] AND JSONExtractString(labels, 'operation') IN ['HTTP GET /dispatch'] AND JSONExtractString(labels, '__temporality__') = 'Delta') as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_count'] AND unix_milli >= 1650991980000 AND unix_milli <= 1651078380000 GROUP BY ts ORDER BY ts) as B ON A.`ts` = B.`ts`",
expected: "SELECT A.`ts` as `ts`, A.value * 100 / B.value as value FROM (SELECT toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM event.metric INNER JOIN (SELECT DISTINCT fingerprint FROM event.series_1d WHERE metric_name IN ['o11y_latency_count'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND JSONExtractString(labels, 'service_name') IN ['frontend'] AND JSONExtractString(labels, 'operation') IN ['HTTP GET /dispatch'] AND JSONExtractString(labels, 'status_code') IN ['STATUS_CODE_ERROR'] AND JSONExtractString(labels, '__temporality__') = 'Delta') as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_count'] AND unix_milli >= 1650991980000 AND unix_milli <= 1651078380000 GROUP BY ts ORDER BY ts) as A INNER JOIN (SELECT toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM event.metric INNER JOIN (SELECT DISTINCT fingerprint FROM event.series_1d WHERE metric_name IN ['o11y_latency_count'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND JSONExtractString(labels, 'service_name') IN ['frontend'] AND JSONExtractString(labels, 'operation') IN ['HTTP GET /dispatch'] AND JSONExtractString(labels, '__temporality__') = 'Delta') as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_count'] AND unix_milli >= 1650991980000 AND unix_milli <= 1651078380000 GROUP BY ts ORDER BY ts) as B ON A.`ts` = B.`ts`",
},
{
name: "TestQuery - Quantile",
@@ -484,7 +484,7 @@ func TestDeltaQueryBuilder(t *testing.T) {
},
},
queryToTest: "A",
expected: "SELECT service_name, ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.950) as value FROM (SELECT service_name,le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM o11y_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'le') as le, fingerprint FROM o11y_metrics.time_series_v4_1day WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1650991980000 AND unix_milli <= 1651078380000 GROUP BY service_name,le,ts ORDER BY service_name ASC,le ASC, ts) GROUP BY service_name,ts ORDER BY service_name ASC, ts",
expected: "SELECT service_name, ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.950) as value FROM (SELECT service_name,le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM event.metric INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'le') as le, fingerprint FROM event.series_1d WHERE metric_name IN ['o11y_latency_bucket'] AND temporality = 'Delta' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name IN ['o11y_latency_bucket'] AND unix_milli >= 1650991980000 AND unix_milli <= 1651078380000 GROUP BY service_name,le,ts ORDER BY service_name ASC,le ASC, ts) GROUP BY service_name,ts ORDER BY service_name ASC, ts",
},
}
@@ -563,9 +563,9 @@ var testLogsWithFormula = []struct {
},
ExpectedQuery: "SELECT A.`key_1` as `key_1`, A.`ts` as `ts`, A.value + B.value as value FROM " +
"(SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, attributes_bool_value[indexOf(attributes_bool_key, 'key_1')] as `key_1`, toFloat64(count(*)) as value from " +
"o11y_logs.distributed_logs where (timestamp >= 1702979275000000000 AND timestamp <= 1702981075000000000) AND attributes_bool_value[indexOf(attributes_bool_key, 'key_1')] = true AND " +
"event.log where (timestamp >= 1702979275000000000 AND timestamp <= 1702981075000000000) AND attributes_bool_value[indexOf(attributes_bool_key, 'key_1')] = true AND " +
"has(attributes_bool_key, 'key_1') group by `key_1`,ts order by value DESC) as A INNER JOIN (SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, " +
"attributes_bool_value[indexOf(attributes_bool_key, 'key_1')] as `key_1`, toFloat64(count(*)) as value from o11y_logs.distributed_logs where (timestamp >= 1702979275000000000 AND timestamp <= 1702981075000000000) " +
"attributes_bool_value[indexOf(attributes_bool_key, 'key_1')] as `key_1`, toFloat64(count(*)) as value from event.log where (timestamp >= 1702979275000000000 AND timestamp <= 1702981075000000000) " +
"AND attributes_bool_value[indexOf(attributes_bool_key, 'key_2')] = true AND has(attributes_bool_key, 'key_1') group by `key_1`,ts order by value DESC) as B ON A.`key_1` = B.`key_1` AND A.`ts` = B.`ts`",
},
{
@@ -623,9 +623,9 @@ var testLogsWithFormula = []struct {
},
},
ExpectedQuery: "SELECT A.`key1.1` as `key1.1`, A.value + B.value as value FROM (SELECT now() as ts, attributes_bool_value[indexOf(attributes_bool_key, 'key1.1')] as `key1.1`, " +
"toFloat64(count(*)) as value from o11y_logs.distributed_logs where (timestamp >= 1702979056000000000 AND timestamp <= 1702982656000000000) AND attributes_bool_value[indexOf(attributes_bool_key, 'key1.1')] = true AND " +
"toFloat64(count(*)) as value from event.log where (timestamp >= 1702979056000000000 AND timestamp <= 1702982656000000000) AND attributes_bool_value[indexOf(attributes_bool_key, 'key1.1')] = true AND " +
"has(attributes_bool_key, 'key1.1') group by `key1.1` order by value DESC) as A INNER JOIN (SELECT now() as ts, attributes_bool_value[indexOf(attributes_bool_key, 'key1.1')] as `key1.1`, " +
"toFloat64(count(*)) as value from o11y_logs.distributed_logs where (timestamp >= 1702979056000000000 AND timestamp <= 1702982656000000000) AND attributes_bool_value[indexOf(attributes_bool_key, 'key1.2')] = true AND " +
"toFloat64(count(*)) as value from event.log where (timestamp >= 1702979056000000000 AND timestamp <= 1702982656000000000) AND attributes_bool_value[indexOf(attributes_bool_key, 'key1.2')] = true AND " +
"has(attributes_bool_key, 'key1.1') group by `key1.1` order by value DESC) as B ON A.`key1.1` = B.`key1.1`",
},
{
@@ -683,9 +683,9 @@ var testLogsWithFormula = []struct {
},
},
ExpectedQuery: "SELECT A.`key1.1` as `key1.1`, A.`ts` as `ts`, A.value - B.value as value FROM (SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, " +
"`attribute_bool_key1$$1` as `key1.1`, toFloat64(count(*)) as value from o11y_logs.distributed_logs where (timestamp >= 1702980884000000000 AND timestamp <= 1702984484000000000) AND " +
"`attribute_bool_key1$$1` as `key1.1`, toFloat64(count(*)) as value from event.log where (timestamp >= 1702980884000000000 AND timestamp <= 1702984484000000000) AND " +
"`attribute_bool_key_2` = true AND `attribute_bool_key1$$1_exists`=true group by `key1.1`,ts order by value DESC) as A INNER JOIN (SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), " +
"INTERVAL 60 SECOND) AS ts, `attribute_bool_key1$$1` as `key1.1`, toFloat64(count(*)) as value from o11y_logs.distributed_logs where (timestamp >= 1702980884000000000 AND " +
"INTERVAL 60 SECOND) AS ts, `attribute_bool_key1$$1` as `key1.1`, toFloat64(count(*)) as value from event.log where (timestamp >= 1702980884000000000 AND " +
"timestamp <= 1702984484000000000) AND attributes_bool_value[indexOf(attributes_bool_key, 'key_1')] = true AND `attribute_bool_key1$$1_exists`=true group by `key1.1`,ts order by value DESC) as B " +
"ON A.`key1.1` = B.`key1.1` AND A.`ts` = B.`ts`",
},
@@ -770,10 +770,10 @@ var testLogsWithFormulaV2 = []struct {
},
ExpectedQuery: "SELECT A.`key_1` as `key_1`, A.`ts` as `ts`, A.value + B.value as value FROM " +
"(SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, attributes_bool['key_1'] as `key_1`, toFloat64(count(*)) as value from " +
"o11y_logs.distributed_logs_v2 where (timestamp >= 1702979275000000000 AND timestamp <= 1702981075000000000) AND (ts_bucket_start >= 1702977475 AND ts_bucket_start <= 1702981075) " +
"event.log where (timestamp >= 1702979275000000000 AND timestamp <= 1702981075000000000) AND (ts_bucket_start >= 1702977475 AND ts_bucket_start <= 1702981075) " +
"AND attributes_bool['key_1'] = true AND mapContains(attributes_bool, 'key_1') AND mapContains(attributes_bool, 'key_1') group by `key_1`,ts order by value DESC) as A INNER JOIN (SELECT " +
"toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, attributes_bool['key_1'] as `key_1`, toFloat64(count(*)) as value " +
"from o11y_logs.distributed_logs_v2 where (timestamp >= 1702979275000000000 AND timestamp <= 1702981075000000000) AND (ts_bucket_start >= 1702977475 AND ts_bucket_start <= 1702981075) " +
"from event.log where (timestamp >= 1702979275000000000 AND timestamp <= 1702981075000000000) AND (ts_bucket_start >= 1702977475 AND ts_bucket_start <= 1702981075) " +
"AND attributes_bool['key_2'] = true AND mapContains(attributes_bool, 'key_2') AND mapContains(attributes_bool, 'key_1') group by `key_1`,ts order by value DESC) as B ON A.`key_1` = B.`key_1` AND A.`ts` = B.`ts`",
},
{
@@ -831,9 +831,9 @@ var testLogsWithFormulaV2 = []struct {
},
},
ExpectedQuery: "SELECT A.`key1.1` as `key1.1`, A.value + B.value as value FROM (SELECT attributes_bool['key1.1'] as `key1.1`, " +
"toFloat64(count(*)) as value from o11y_logs.distributed_logs_v2 where (timestamp >= 1702979056000000000 AND timestamp <= 1702982656000000000) AND (ts_bucket_start >= 1702977256 AND ts_bucket_start <= 1702982656) " +
"toFloat64(count(*)) as value from event.log where (timestamp >= 1702979056000000000 AND timestamp <= 1702982656000000000) AND (ts_bucket_start >= 1702977256 AND ts_bucket_start <= 1702982656) " +
"AND attributes_bool['key1.1'] = true AND mapContains(attributes_bool, 'key1.1') AND mapContains(attributes_bool, 'key1.1') group by `key1.1` order by value DESC) as A INNER JOIN (SELECT " +
"attributes_bool['key1.1'] as `key1.1`, toFloat64(count(*)) as value from o11y_logs.distributed_logs_v2 where (timestamp >= 1702979056000000000 AND timestamp <= 1702982656000000000) " +
"attributes_bool['key1.1'] as `key1.1`, toFloat64(count(*)) as value from event.log where (timestamp >= 1702979056000000000 AND timestamp <= 1702982656000000000) " +
"AND (ts_bucket_start >= 1702977256 AND ts_bucket_start <= 1702982656) AND attributes_bool['key1.2'] = true AND mapContains(attributes_bool, 'key1.2') AND " +
"mapContains(attributes_bool, 'key1.1') group by `key1.1` order by value DESC) as B ON A.`key1.1` = B.`key1.1`",
},
@@ -892,10 +892,10 @@ var testLogsWithFormulaV2 = []struct {
},
},
ExpectedQuery: "SELECT A.`key1.1` as `key1.1`, A.`ts` as `ts`, A.value - B.value as value FROM (SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, " +
"`attribute_bool_key1$$1` as `key1.1`, toFloat64(count(*)) as value from o11y_logs.distributed_logs_v2 where (timestamp >= 1702980884000000000 AND timestamp <= 1702984484000000000) AND " +
"`attribute_bool_key1$$1` as `key1.1`, toFloat64(count(*)) as value from event.log where (timestamp >= 1702980884000000000 AND timestamp <= 1702984484000000000) AND " +
"(ts_bucket_start >= 1702979084 AND ts_bucket_start <= 1702984484) AND `attribute_bool_key_2` = true AND `attribute_bool_key1$$1_exists`=true group by `key1.1`,ts order by value DESC) as " +
"A INNER JOIN (SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, `attribute_bool_key1$$1` as `key1.1`, toFloat64(count(*)) as value from " +
"o11y_logs.distributed_logs_v2 where (timestamp >= 1702980884000000000 AND timestamp <= 1702984484000000000) AND (ts_bucket_start >= 1702979084 AND ts_bucket_start <= 1702984484) AND " +
"event.log where (timestamp >= 1702980884000000000 AND timestamp <= 1702984484000000000) AND (ts_bucket_start >= 1702979084 AND ts_bucket_start <= 1702984484) AND " +
"attributes_bool['key_1'] = true AND mapContains(attributes_bool, 'key_1') AND `attribute_bool_key1$$1_exists`=true group by `key1.1`,ts order by value DESC) as B " +
"ON A.`key1.1` = B.`key1.1` AND A.`ts` = B.`ts`",
},
+12 -8
View File
@@ -4,6 +4,7 @@ import (
"encoding/json"
"strings"
"github.com/hanzoai/o11y/pkg/querier"
v3 "github.com/hanzoai/o11y/pkg/query-service/model/v3"
)
@@ -67,16 +68,19 @@ func NewQueryInfoResult(postData *v3.QueryRangeParamsV3, version string) QueryIn
}
}
} else if postData.CompositeQuery.QueryType == v3.QueryTypeDatastoreSQL {
// This block asked which DATABASE the SQL named — o11y_metrics,
// o11y_logs, o11y_traces. HIP-0132 unified all three into `event`, so
// all three tests went permanently false and raw-SQL queries were
// recorded as touching no signal at all. querier.SignalsInSQL is the one
// answer to that question; there is no second copy of it here.
for _, query := range postData.CompositeQuery.DatastoreQueries {
if strings.Contains(query.Query, "o11y_metrics") && len(query.Query) > 0 {
queryInfoResult.MetricsUsed = true
}
if strings.Contains(query.Query, "o11y_logs") && len(query.Query) > 0 {
queryInfoResult.LogsUsed = true
}
if strings.Contains(query.Query, "o11y_traces") && len(query.Query) > 0 {
queryInfoResult.TracesUsed = true
if len(query.Query) == 0 {
continue
}
m, l, t := querier.SignalsInSQL(query.Query)
queryInfoResult.MetricsUsed = queryInfoResult.MetricsUsed || m
queryInfoResult.LogsUsed = queryInfoResult.LogsUsed || l
queryInfoResult.TracesUsed = queryInfoResult.TracesUsed || t
}
}
@@ -5,6 +5,8 @@ import (
"testing"
v3 "github.com/hanzoai/o11y/pkg/query-service/model/v3"
"github.com/hanzoai/o11y/pkg/telemetrylogs"
"github.com/hanzoai/o11y/pkg/telemetryplane"
)
func Test_buildResourceFilter(t *testing.T) {
@@ -530,7 +532,7 @@ func Test_buildResourceSubQuery(t *testing.T) {
Type: v3.AttributeKeyTypeResource,
},
},
want: "(SELECT fingerprint FROM o11y_logs.distributed_logs_v2_resource WHERE " +
want: "(SELECT fingerprint FROM event.log_resource WHERE " +
"(seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND " +
"simpleJSONExtractString(labels, 'service.name') = 'test' AND labels like '%service.name\":\"test%' " +
"AND simpleJSONExtractString(lower(labels), 'namespace') LIKE '%test1%' AND lower(labels) like '%namespace%test1%' " +
@@ -541,7 +543,7 @@ func Test_buildResourceSubQuery(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := BuildResourceSubQuery("o11y_logs", "distributed_logs_v2_resource", tt.args.bucketStart, tt.args.bucketEnd, tt.args.fs, tt.args.groupBy, tt.args.aggregateAttribute, false)
got, err := BuildResourceSubQuery(telemetryplane.DBName, telemetrylogs.LogResourceTableName, tt.args.bucketStart, tt.args.bucketEnd, tt.args.fs, tt.args.groupBy, tt.args.aggregateAttribute, false)
if (err != nil) != tt.wantErr {
t.Errorf("buildResourceSubQuery() error = %v, wantErr %v", err, tt.wantErr)
return
@@ -9,6 +9,7 @@ import (
"github.com/hanzoai/o11y/pkg/query-service/constants"
v3 "github.com/hanzoai/o11y/pkg/query-service/model/v3"
"github.com/hanzoai/o11y/pkg/query-service/utils"
"github.com/hanzoai/o11y/pkg/telemetrytraces"
)
const NANOSECOND = 1000000000
@@ -282,7 +283,9 @@ func buildTracesQuery(start, end, step int64, mq *v3.BuilderQuery, panelType v3.
filterSubQuery = filterSubQuery + " AND " + emptyValuesInGroupByFilter
}
resourceSubQuery, err := resource.BuildResourceSubQuery("o11y_traces", "distributed_traces_v3_resource", bucketStart, bucketEnd, mq.Filters, mq.GroupBy, mq.AggregateAttribute, false)
// The one place this builder still spelled a database. o11y_traces was
// dropped by HIP-0132 and distributed_traces_v3_resource IS event.span_resource.
resourceSubQuery, err := resource.BuildResourceSubQuery(constants.O11Y_TRACE_DBNAME, telemetrytraces.SpanResourceTableName, bucketStart, bucketEnd, mq.Filters, mq.GroupBy, mq.AggregateAttribute, false)
if err != nil {
return "", err
}
@@ -516,7 +516,7 @@ func Test_buildTracesQuery(t *testing.T) {
{ColumnName: "http.method", Order: "ASC"}},
},
},
want: "SELECT attributes_string['http.method'] as `http.method`, toFloat64(count()) as value from o11y_traces.distributed_o11y_index_v3 where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
want: "SELECT attributes_string['http.method'] as `http.method`, toFloat64(count()) as value from event.span where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
"AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND attributes_string['http.method'] = '100' AND mapContains(attributes_string, 'http.method') " +
"group by `http.method` order by `http.method` ASC",
},
@@ -544,7 +544,7 @@ func Test_buildTracesQuery(t *testing.T) {
{ColumnName: "http.method", Order: "ASC"}},
},
},
want: "SELECT attributes_string['http.method'] as `http.method`, toFloat64(count()) as value from o11y_traces.distributed_o11y_index_v3 where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
want: "SELECT attributes_string['http.method'] as `http.method`, toFloat64(count()) as value from event.span where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
"AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND attributes_string['http.method'] = '100' AND mapContains(attributes_string, 'http.method') AND mapContains(attributes_string, 'name') " +
"group by `http.method` order by `http.method` ASC",
},
@@ -572,7 +572,7 @@ func Test_buildTracesQuery(t *testing.T) {
{ColumnName: "http.method", Order: "ASC"}},
},
},
want: "SELECT attributes_string['http.method'] as `http.method`, toFloat64(count()) as value from o11y_traces.distributed_o11y_index_v3 where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
want: "SELECT attributes_string['http.method'] as `http.method`, toFloat64(count()) as value from event.span where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
"AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND attributes_string['http.method'] = '100' AND mapContains(attributes_string, 'http.method') AND name != '' " +
"group by `http.method` order by `http.method` ASC",
},
@@ -596,9 +596,9 @@ func Test_buildTracesQuery(t *testing.T) {
{ColumnName: "host", Order: "ASC"}},
},
},
want: "SELECT resources_number['host'] as `host`, toFloat64(count()) as value from o11y_traces.distributed_o11y_index_v3 where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
want: "SELECT resources_number['host'] as `host`, toFloat64(count()) as value from event.span where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
"AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND attributes_number['bytes'] > 100 AND " +
"(resource_fingerprint GLOBAL IN (SELECT fingerprint FROM o11y_traces.distributed_traces_v3_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND " +
"(resource_fingerprint GLOBAL IN (SELECT fingerprint FROM event.span_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND " +
"(seen_at_ts_bucket_start <= 1680066458) AND simpleJSONExtractString(labels, 'service.name') = 'myService' AND labels like '%service.name\":\"myService%' AND " +
"( (simpleJSONHas(labels, 'host') AND labels like '%host%') ))) " +
"group by `host` order by `host` ASC",
@@ -616,7 +616,7 @@ func Test_buildTracesQuery(t *testing.T) {
OrderBy: []v3.OrderBy{{ColumnName: "timestamp", Order: "ASC"}},
},
},
want: "SELECT timestamp as timestamp_datetime, span_id as spanID, trace_id as traceID, name as `name` from o11y_traces.distributed_o11y_index_v3 where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
want: "SELECT timestamp as timestamp_datetime, span_id as spanID, trace_id as traceID, name as `name` from event.span where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
"AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) order by timestamp ASC",
},
{
@@ -632,8 +632,8 @@ func Test_buildTracesQuery(t *testing.T) {
OrderBy: []v3.OrderBy{{ColumnName: "timestamp", Order: "ASC"}},
},
},
want: "SELECT timestamp as timestamp_datetime, span_id as spanID, trace_id as traceID, name as `name` from o11y_traces.distributed_o11y_index_v3 where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
"AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND ((name, `resource_string_service$$name`) GLOBAL IN ( SELECT DISTINCT name, serviceName from o11y_traces.distributed_top_level_operations )) AND parent_span_id != '' order by timestamp ASC",
want: "SELECT timestamp as timestamp_datetime, span_id as spanID, trace_id as traceID, name as `name` from event.span where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
"AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND ((name, `resource_string_service$$name`) GLOBAL IN ( SELECT DISTINCT name, serviceName from event.operation )) AND parent_span_id != '' order by timestamp ASC",
},
{
name: "test noop list view with root_spans",
@@ -648,7 +648,7 @@ func Test_buildTracesQuery(t *testing.T) {
OrderBy: []v3.OrderBy{{ColumnName: "timestamp", Order: "ASC"}},
},
},
want: "SELECT timestamp as timestamp_datetime, span_id as spanID, trace_id as traceID, name as `name` from o11y_traces.distributed_o11y_index_v3 where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
want: "SELECT timestamp as timestamp_datetime, span_id as spanID, trace_id as traceID, name as `name` from event.span where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
"AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND parent_span_id = '' order by timestamp ASC",
},
{
@@ -664,7 +664,7 @@ func Test_buildTracesQuery(t *testing.T) {
OrderBy: []v3.OrderBy{{ColumnName: "timestamp", Order: "ASC"}},
},
},
want: "SELECT timestamp as timestamp_datetime, span_id as spanID, trace_id as traceID, name as `name` from o11y_traces.distributed_o11y_index_v3 where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
want: "SELECT timestamp as timestamp_datetime, span_id as spanID, trace_id as traceID, name as `name` from event.span where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
"AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND parent_span_id = '' order by timestamp ASC",
},
{
@@ -680,8 +680,8 @@ func Test_buildTracesQuery(t *testing.T) {
OrderBy: []v3.OrderBy{{ColumnName: "timestamp", Order: "ASC"}},
},
},
want: "SELECT timestamp as timestamp_datetime, span_id as spanID, trace_id as traceID, name as `name` from o11y_traces.distributed_o11y_index_v3 where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
"AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND (resource_fingerprint GLOBAL IN (SELECT fingerprint FROM o11y_traces.distributed_traces_v3_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND simpleJSONExtractString(labels, 'service.name') = 'cartservice' AND labels like '%service.name\":\"cartservice%')) AND parent_span_id = '' order by timestamp ASC",
want: "SELECT timestamp as timestamp_datetime, span_id as spanID, trace_id as traceID, name as `name` from event.span where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
"AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND (resource_fingerprint GLOBAL IN (SELECT fingerprint FROM event.span_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND simpleJSONExtractString(labels, 'service.name') = 'cartservice' AND labels like '%service.name\":\"cartservice%')) AND parent_span_id = '' order by timestamp ASC",
},
{
name: "test noop list view-without ts",
@@ -695,7 +695,7 @@ func Test_buildTracesQuery(t *testing.T) {
SelectColumns: []v3.AttributeKey{{Key: "name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag, IsColumn: true}},
},
},
want: "SELECT timestamp as timestamp_datetime, span_id as spanID, trace_id as traceID, name as `name` from o11y_traces.distributed_o11y_index_v3 where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
want: "SELECT timestamp as timestamp_datetime, span_id as spanID, trace_id as traceID, name as `name` from event.span where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') " +
"AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) order by timestamp DESC",
},
{
@@ -715,10 +715,10 @@ func Test_buildTracesQuery(t *testing.T) {
},
},
want: "SELECT subQuery.serviceName as `subQuery.serviceName`, subQuery.name as `subQuery.name`, count() AS span_count, subQuery.durationNano as `subQuery.durationNano`, subQuery.traceID FROM " +
"(SELECT traceID AS dist_traceID, timestamp, ts_bucket_start FROM o11y_traces.distributed_o11y_index_v3 WHERE (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND attributes_string['method'] = 'GET' " +
"AND (resource_fingerprint GLOBAL IN (SELECT fingerprint FROM o11y_traces.distributed_traces_v3_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND simpleJSONExtractString(labels, 'service.name') = 'myService' AND labels like '%service.name\":\"myService%'))) as dist_table " +
"(SELECT traceID AS dist_traceID, timestamp, ts_bucket_start FROM event.span WHERE (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND attributes_string['method'] = 'GET' " +
"AND (resource_fingerprint GLOBAL IN (SELECT fingerprint FROM event.span_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND simpleJSONExtractString(labels, 'service.name') = 'myService' AND labels like '%service.name\":\"myService%'))) as dist_table " +
"INNER JOIN " +
"( SELECT * FROM (SELECT traceID, durationNano, serviceName, name FROM o11y_traces.o11y_index_v3 WHERE parentSpanID = '' AND (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"( SELECT * FROM (SELECT traceID, durationNano, serviceName, name FROM event.span WHERE parentSpanID = '' AND (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"ORDER BY durationNano DESC LIMIT 1 BY traceID) AS inner_subquery ) AS subQuery ON dist_table.dist_traceID = subQuery.traceID " +
"GROUP BY subQuery.traceID, subQuery.durationNano, subQuery.name, subQuery.serviceName ORDER BY " +
"subQuery.durationNano desc LIMIT 1 BY subQuery.traceID LIMIT 100 settings distributed_product_mode='allow', max_memory_usage=10000000000",
@@ -740,11 +740,11 @@ func Test_buildTracesQuery(t *testing.T) {
},
},
},
want: "SELECT serviceName, name, subQuery.span_count as span_count, durationNano, trace_id as traceID from o11y_traces.distributed_o11y_index_v3 GLOBAL INNER JOIN " +
"( SELECT * FROM (SELECT trace_id, count() as span_count FROM o11y_traces.o11y_index_v3 WHERE (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"AND attributes_string['method'] = 'GET' AND (resource_fingerprint GLOBAL IN (SELECT fingerprint FROM o11y_traces.distributed_traces_v3_resource WHERE " +
want: "SELECT serviceName, name, subQuery.span_count as span_count, durationNano, trace_id as traceID from event.span GLOBAL INNER JOIN " +
"( SELECT * FROM (SELECT trace_id, count() as span_count FROM event.span WHERE (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"AND attributes_string['method'] = 'GET' AND (resource_fingerprint GLOBAL IN (SELECT fingerprint FROM event.span_resource WHERE " +
"(seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND simpleJSONExtractString(labels, 'service.name') = 'myService' AND labels like '%service.name\":\"myService%')) " +
"GROUP BY trace_id ORDER BY span_count DESC LIMIT 1 BY trace_id LIMIT 100) AS inner_subquery ) AS subQuery ON o11y_traces.distributed_o11y_index_v3.trace_id = subQuery.trace_id " +
"GROUP BY trace_id ORDER BY span_count DESC LIMIT 1 BY trace_id LIMIT 100) AS inner_subquery ) AS subQuery ON event.span.trace_id = subQuery.trace_id " +
"WHERE parent_span_id = '' AND (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"ORDER BY subQuery.span_count DESC LIMIT 100 settings distributed_product_mode='allow', max_memory_usage=10000000000",
},
@@ -768,7 +768,7 @@ func Test_buildTracesQuery(t *testing.T) {
},
},
},
want: "SELECT toFloat64(count(distinct(name))) as value from o11y_traces.distributed_o11y_index_v3 where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') AND " +
want: "SELECT toFloat64(count(distinct(name))) as value from event.span where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') AND " +
"(ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) having value > 10 order by value ASC",
},
{
@@ -783,7 +783,7 @@ func Test_buildTracesQuery(t *testing.T) {
AggregateAttribute: v3.AttributeKey{Key: "timestamp", IsColumn: false, Type: v3.AttributeKeyTypeTag},
},
},
want: "SELECT max(toUnixTimestamp64Nano(timestamp)) as value from o11y_traces.distributed_o11y_index_v3 where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') AND " +
want: "SELECT max(toUnixTimestamp64Nano(timestamp)) as value from event.span where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') AND " +
"(ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) order by value DESC",
},
{
@@ -799,7 +799,7 @@ func Test_buildTracesQuery(t *testing.T) {
Filters: &v3.FilterSet{},
},
},
want: "SELECT toStartOfInterval(timestamp, INTERVAL 60 SECOND) AS ts, count()/60.000000 as value from o11y_traces.distributed_o11y_index_v3 where (timestamp >= '1745315470000000000' AND " +
want: "SELECT toStartOfInterval(timestamp, INTERVAL 60 SECOND) AS ts, count()/60.000000 as value from event.span where (timestamp >= '1745315470000000000' AND " +
"timestamp <= '1745319070000000000') AND (ts_bucket_start >= 1745313670 AND ts_bucket_start <= 1745319070) group by ts order by value DESC",
},
{
@@ -815,7 +815,7 @@ func Test_buildTracesQuery(t *testing.T) {
Filters: &v3.FilterSet{},
},
},
want: "SELECT count()/3600.000000 as value from o11y_traces.distributed_o11y_index_v3 where (timestamp >= '1745315470000000000' AND " +
want: "SELECT count()/3600.000000 as value from event.span where (timestamp >= '1745315470000000000' AND " +
"timestamp <= '1745319070000000000') AND (ts_bucket_start >= 1745313670 AND ts_bucket_start <= 1745319070) order by value DESC",
},
}
@@ -866,7 +866,7 @@ func TestPrepareTracesQuery(t *testing.T) {
GraphLimitQtype: constants.FirstQueryGraphLimit,
},
},
want: "SELECT `function` from (SELECT attributes_string['function'] as `function`, toFloat64(count(distinct(name))) as value from o11y_traces.distributed_o11y_index_v3 " +
want: "SELECT `function` from (SELECT attributes_string['function'] as `function`, toFloat64(count(distinct(name))) as value from event.span " +
"where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND mapContains(attributes_string, 'function') group by `function` order by value DESC) LIMIT 10",
},
{
@@ -888,7 +888,7 @@ func TestPrepareTracesQuery(t *testing.T) {
GraphLimitQtype: constants.SecondQueryGraphLimit,
},
},
want: "SELECT attributes_string['function'] as `function`, toFloat64(count(distinct(name))) as value from o11y_traces.distributed_o11y_index_v3 where " +
want: "SELECT attributes_string['function'] as `function`, toFloat64(count(distinct(name))) as value from event.span where " +
"(timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND mapContains(attributes_string, 'function') AND (`function`) GLOBAL IN (#LIMIT_PLACEHOLDER) group by `function` order by value DESC",
},
{
@@ -928,8 +928,8 @@ func TestPrepareTracesQuery(t *testing.T) {
},
},
want: "SELECT `function`,`service.name` from (SELECT `attribute_string_function` as `function`, `resource_string_service$$name` as `service.name`, toFloat64(count(distinct(name))) as value " +
"from o11y_traces.distributed_o11y_index_v3 where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"AND attributes_number['line'] = 100 AND (resource_fingerprint GLOBAL IN (SELECT fingerprint FROM o11y_traces.distributed_traces_v3_resource WHERE " +
"from event.span where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"AND attributes_number['line'] = 100 AND (resource_fingerprint GLOBAL IN (SELECT fingerprint FROM event.span_resource WHERE " +
"(seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND simpleJSONExtractString(labels, 'hostname') = 'server1' AND labels like '%hostname\":\"server1%' AND " +
"( (simpleJSONHas(labels, 'service.name') AND labels like '%service.name%') ))) group by `function`,`service.name` order by value DESC) LIMIT 10",
},
@@ -969,9 +969,9 @@ func TestPrepareTracesQuery(t *testing.T) {
GraphLimitQtype: constants.SecondQueryGraphLimit,
},
},
want: "SELECT `attribute_string_function` as `function`, resource_string_service$$name as `serviceName`, toFloat64(count(distinct(name))) as value from o11y_traces.distributed_o11y_index_v3 " +
want: "SELECT `attribute_string_function` as `function`, resource_string_service$$name as `serviceName`, toFloat64(count(distinct(name))) as value from event.span " +
"where (timestamp >= '1680066360726210000' AND timestamp <= '1680066458000000000') AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND attributes_number['line'] = 100 " +
"AND (resource_fingerprint GLOBAL IN (SELECT fingerprint FROM o11y_traces.distributed_traces_v3_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) " +
"AND (resource_fingerprint GLOBAL IN (SELECT fingerprint FROM event.span_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) " +
"AND simpleJSONExtractString(labels, 'hostname') = 'server1' AND labels like '%hostname\":\"server1%')) AND (`function`,`serviceName`) GLOBAL IN (#LIMIT_PLACEHOLDER) group by `function`,`serviceName` order by value DESC",
},
}
+54 -24
View File
@@ -8,6 +8,10 @@ import (
"github.com/hanzoai/o11y/pkg/query-service/model"
v3 "github.com/hanzoai/o11y/pkg/query-service/model/v3"
"github.com/hanzoai/o11y/pkg/telemetrymetadata"
"github.com/hanzoai/o11y/pkg/telemetrymetrics"
"github.com/hanzoai/o11y/pkg/telemetryplane"
"github.com/hanzoai/o11y/pkg/telemetrytraces"
"github.com/hanzoai/o11y/pkg/valuer"
)
@@ -92,35 +96,61 @@ var GroupByColMap = map[string]struct{}{
ResponseStatusCode: {},
}
// The names the v3/v4 query builders read by — twenty-seven call sites across
// metrics v3, metrics v4, inframetrics and traces v4 reach the plane through
// exactly these constants.
//
// BOTH DATABASE NAMES WERE DEAD. o11y_metrics and o11y_traces were dropped by
// HIP-0132 and unified into the event plane, so every one of those call sites
// answered `Code: 81 ... UNKNOWN_DATABASE`. They are ALIASES now — the plane is
// spelled once, in pkg/telemetryplane, and plane_test.go enforces that.
//
// The table names are aliases of pkg/telemetry{traces,metrics}/tables.go for the
// same reason: samples_v4 IS event.metric, time_series_v4 IS event.series,
// o11y_index_v3 IS event.span. A name that says `_v4` in one file and `metric`
// in another is one table with two names, which is how a migration leaves half
// a plane behind.
const (
O11Y_METRIC_DBNAME = "o11y_metrics"
O11Y_SAMPLES_V4_LOCAL_TABLENAME = "samples_v4"
O11Y_SAMPLES_V4_TABLENAME = "distributed_samples_v4"
O11Y_SAMPLES_V4_AGG_5M_TABLENAME = "distributed_samples_v4_agg_5m"
O11Y_SAMPLES_V4_AGG_30M_TABLENAME = "distributed_samples_v4_agg_30m"
O11Y_EXP_HISTOGRAM_TABLENAME = "distributed_exp_hist"
O11Y_EXP_HISTOGRAM_LOCAL_TABLENAME = "exp_hist"
O11Y_TRACE_DBNAME = "o11y_traces"
O11Y_SPAN_INDEX_TABLENAME = "distributed_o11y_index_v2"
O11Y_SPAN_INDEX_V3 = "distributed_o11y_index_v3"
O11Y_SPAN_INDEX_LOCAL_TABLENAME = "o11y_index_v2"
O11Y_SPAN_INDEX_V3_LOCAL_TABLENAME = "o11y_index_v3"
O11Y_TIMESERIES_v4_LOCAL_TABLENAME = "time_series_v4"
O11Y_TIMESERIES_V4_TABLENAME = "distributed_time_series_v4"
O11Y_TIMESERIES_v4_6HRS_LOCAL_TABLENAME = "time_series_v4_6hrs"
O11Y_TIMESERIES_v4_1DAY_LOCAL_TABLENAME = "time_series_v4_1day"
O11Y_TIMESERIES_v4_1WEEK_LOCAL_TABLENAME = "time_series_v4_1week"
O11Y_TIMESERIES_v4_1DAY_TABLENAME = "distributed_time_series_v4_1day"
O11Y_TOP_LEVEL_OPERATIONS_TABLENAME = "distributed_top_level_operations"
O11Y_TIMESERIES_v4_TABLENAME = "distributed_time_series_v4"
O11Y_TIMESERIES_v4_1WEEK_TABLENAME = "distributed_time_series_v4_1week"
O11Y_TIMESERIES_v4_6HRS_TABLENAME = "distributed_time_series_v4_6hrs"
O11Y_ATTRIBUTES_METADATA_TABLENAME = "distributed_attributes_metadata"
O11Y_ATTRIBUTES_METADATA_LOCAL_TABLENAME = "attributes_metadata"
O11Y_METRIC_DBNAME = telemetryplane.DBName
O11Y_SAMPLES_V4_LOCAL_TABLENAME = telemetrymetrics.MetricLocalTableName
O11Y_SAMPLES_V4_TABLENAME = telemetrymetrics.MetricTableName
O11Y_SAMPLES_V4_AGG_5M_TABLENAME = telemetrymetrics.Metric5mTableName
O11Y_SAMPLES_V4_AGG_30M_TABLENAME = telemetrymetrics.Metric30mTableName
O11Y_EXP_HISTOGRAM_TABLENAME = telemetrymetrics.HistogramTableName
O11Y_EXP_HISTOGRAM_LOCAL_TABLENAME = telemetrymetrics.HistogramLocalTableName
O11Y_TRACE_DBNAME = telemetryplane.DBName
O11Y_SPAN_INDEX_V3 = telemetrytraces.SpanTableName
O11Y_SPAN_INDEX_V3_LOCAL_TABLENAME = telemetrytraces.SpanLocalTableName
O11Y_TIMESERIES_v4_LOCAL_TABLENAME = telemetrymetrics.SeriesLocalTableName
O11Y_TIMESERIES_V4_TABLENAME = telemetrymetrics.SeriesTableName
O11Y_TIMESERIES_v4_TABLENAME = telemetrymetrics.SeriesTableName
O11Y_TIMESERIES_v4_6HRS_LOCAL_TABLENAME = telemetrymetrics.Series6hLocalTableName
O11Y_TIMESERIES_v4_6HRS_TABLENAME = telemetrymetrics.Series6hTableName
O11Y_TIMESERIES_v4_1DAY_LOCAL_TABLENAME = telemetrymetrics.Series1dLocalTableName
O11Y_TIMESERIES_v4_1DAY_TABLENAME = telemetrymetrics.Series1dTableName
O11Y_TIMESERIES_v4_1WEEK_LOCAL_TABLENAME = telemetrymetrics.Series1wLocalTableName
O11Y_TIMESERIES_v4_1WEEK_TABLENAME = telemetrymetrics.Series1wTableName
// top_level_operations IS event.operation — one (service, operation) pair.
O11Y_TOP_LEVEL_OPERATIONS_TABLENAME = telemetrytraces.OperationTableName
// The metadata pair is NOT the plane: o11y_metadata still has a live writer.
O11Y_ATTRIBUTES_METADATA_TABLENAME = telemetrymetadata.AttributesMetadataTableName
O11Y_ATTRIBUTES_METADATA_LOCAL_TABLENAME = telemetrymetadata.AttributesMetadataLocalTableName
O11Y_METADATA_TABLENAME = "distributed_metadata"
O11Y_METADATA_LOCAL_TABLENAME = "metadata"
)
// RETIRED — the v2 span index. It was dropped with o11y_traces and the event
// plane has no table with its columns. Kept as a literal so a read that reaches
// it fails naming the retired table, which is the true reason.
const (
O11Y_SPAN_INDEX_TABLENAME = "distributed_o11y_index_v2"
O11Y_SPAN_INDEX_LOCAL_TABLENAME = "o11y_index_v2"
)
// alert related constants
const (
// AlertHelpPage is used in case default alert repo url is not set
+2 -2
View File
@@ -40,7 +40,7 @@ func GenerateMetricQueryCHArgs(
// Adjust start time to nearest hour
oneHourInMilliseconds := uint64(time.Hour.Milliseconds())
// start time for filtering o11y_metrics.time_series_v4 with start time
// start time for filtering event.series with start time
timeSeriesCTEStartTime := start - (start % oneHourInMilliseconds)
queryArgs := []interface{}{
@@ -61,7 +61,7 @@ func GenerateMetricQueryCHArgs(
queryArgs = append(queryArgs, false)
// Step2: Add temporal aggregation args
// build args for filtering o11y_metrics.distributed_samples_v4 table
// build args for filtering the event.metric table
temporalAggArgs := []interface{}{
metricName,
start,
@@ -135,7 +135,7 @@ func TestDatastoreFilterExtractor_GroupByColumns(t *testing.T) {
JSONExtractString(labels, 'http.method') AS http_method,
metric_name as metricName,
rand() as value
FROM o11y_metrics.time_series_v4
FROM event.series
WHERE (metric_name IN ('test_metric_cardinality'))
GROUP BY
ts,
@@ -163,7 +163,7 @@ func TestDatastoreFilterExtractor_GroupByColumns(t *testing.T) {
JSONExtractString(labels, 'http.method'),
metric_name as metricName,
rand() as value
FROM o11y_metrics.time_series_v4
FROM event.series
WHERE (metric_name IN ('test_metric_cardinality'))
GROUP BY
ts,
@@ -845,13 +845,13 @@ func TestDatastoreFilterExtractor_NestedComplexCTEGroupByQueries(t *testing.T) {
service,
op,
sum(value) / 60 AS value
FROM o11y_metrics.distributed_samples_v4 AS points
FROM event.metric AS points
INNER JOIN (
SELECT
fingerprint,
JSONExtractString(labels, 'service.name') AS service,
JSONExtractString(labels, 'operation') AS op
FROM o11y_metrics.time_series_v4
FROM event.series
WHERE (metric_name IN ('app_requests_total')) AND (unix_milli >= 1731340800000) AND (unix_milli <= 1731344400000) AND (LOWER(temporality) LIKE LOWER('delta')) AND (__normalized = false)
GROUP BY
fingerprint,
@@ -882,10 +882,10 @@ func TestDatastoreFilterExtractor_NestedComplexCTEGroupByQueries(t *testing.T) {
fingerprint,
toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), toIntervalSecond(60)) AS ts,
avg(value) AS per_series_value
FROM o11y_metrics.distributed_samples_v4 AS points
FROM event.metric AS points
INNER JOIN (
SELECT fingerprint
FROM o11y_metrics.time_series_v4
FROM event.series
WHERE (metric_name IN ('node.cpu.usage')) AND (unix_milli >= 1731427200000) AND (unix_milli <= 1731430800000) AND (LOWER(temporality) LIKE LOWER('cumulative')) AND (__normalized = false)
GROUP BY fingerprint
) AS filtered_time_series
@@ -922,13 +922,13 @@ func TestDatastoreFilterExtractor_NestedComplexCTEGroupByQueries(t *testing.T) {
svc,
le,
sum(value)/60 AS value
FROM o11y_metrics.distributed_samples_v4 AS points
FROM event.metric AS points
INNER JOIN (
SELECT
fingerprint,
JSONExtractString(labels, 'service.name') AS svc,
JSONExtractString(labels, 'le') AS le
FROM o11y_metrics.time_series_v4
FROM event.series
WHERE
metric_name IN ('http_request_duration.bucket')
AND unix_milli >= 1731513600000
@@ -1001,12 +1001,12 @@ func TestDatastoreFilterExtractor_NestedComplexCTEGroupByQueries(t *testing.T) {
any(le) AS le,
toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) AS ts,
max(value) AS per_series_value
FROM o11y_metrics.distributed_samples_v4
FROM event.metric
INNER JOIN (
SELECT DISTINCT
JSONExtractString(labels, 'le') AS le,
fingerprint
FROM o11y_metrics.time_series_v4_1day
FROM event.series_1d
WHERE
metric_name IN ['o11y_latency_bucket']
AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')
@@ -1056,8 +1056,8 @@ func TestDatastoreFilterExtractor_NestedComplexCTEGroupByQueries(t *testing.T) {
maxIf(s.value, s.metric_name='k8s.job.failed_pods') AS failed,
maxIf(s.value, s.metric_name='k8s.job.successful_pods') AS success,
maxIf(s.value, s.metric_name='k8s.job.desired_successful_pods') AS desired
FROM o11y_metrics.distributed_samples_v4 AS s
JOIN o11y_metrics.time_series_v4_1day AS tsv
FROM event.metric AS s
JOIN event.series_1d AS tsv
ON s.fingerprint = tsv.fingerprint
WHERE s.metric_name IN (
'k8s.job.failed_pods',
@@ -1127,14 +1127,14 @@ func TestDatastoreFilterExtractor_NestedComplexCTEGroupByQueries(t *testing.T) {
any(` + "`host_name`" + `) AS ` + "`host_name`" + `,
toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) AS ts,
max(value) AS per_series_value
FROM o11y_metrics.distributed_samples_v4
FROM event.metric
INNER JOIN (
SELECT DISTINCT
JSONExtractString(labels, 'os.type') AS ` + "`os.type`" + `,
JSONExtractString(labels, 'state') AS state,
JSONExtractString(labels, 'host_name') AS ` + "`host_name`" + `,
fingerprint
FROM o11y_metrics.time_series_v4_1day
FROM event.series_1d
WHERE
metric_name IN ['system.memory.usage']
AND temporality = 'Unspecified' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND JSONExtractString(labels, 'host.name') = 'o11y-host' ) AS filtered_time_series
@@ -1178,8 +1178,8 @@ func TestDatastoreFilterExtractor_NestedComplexCTEGroupByQueries(t *testing.T) {
JSONExtractString(tsv.labels, 'k8s.job.name') AS job,
maxIf(s.value, s.metric_name = 'k8s.job.successful_pods') AS max_success,
maxIf(s.value, s.metric_name = 'k8s.job.desired_successful_pods') AS max_desired
FROM o11y_metrics.distributed_samples_v4 AS s
JOIN o11y_metrics.time_series_v4_1day AS tsv
FROM event.metric AS s
JOIN event.series_1d AS tsv
ON s.fingerprint = tsv.fingerprint
WHERE s.metric_name IN ('k8s.job.successful_pods', 'k8s.job.desired_successful_pods')
AND s.unix_milli >= start_ms AND s.unix_milli < end_ms
@@ -1243,12 +1243,12 @@ func TestDatastoreFilterExtractor_NestedComplexCTEGroupByQueries(t *testing.T) {
any(le) AS le,
toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) AS ts,
max(value) AS per_series_value
FROM o11y_metrics.distributed_samples_v4
FROM event.metric
INNER JOIN (
SELECT DISTINCT
JSONExtractString(labels, 'le') AS le,
fingerprint
FROM o11y_metrics.time_series_v4_1day
FROM event.series_1d
WHERE
metric_name IN ['o11y_latency_bucket']
AND temporality = 'Cumulative' AND __normalized = false AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')
@@ -12,10 +12,29 @@ import (
"github.com/hanzoai/o11y/pkg/errors"
"github.com/hanzoai/o11y/pkg/factory"
"github.com/hanzoai/o11y/pkg/sqlstore"
"github.com/hanzoai/o11y/pkg/telemetrylogs"
"github.com/hanzoai/o11y/pkg/telemetryplane"
"github.com/hanzoai/o11y/pkg/telemetrystore"
"github.com/hanzoai/o11y/pkg/telemetrytraces"
"github.com/hanzoai/o11y/pkg/transition"
)
// The three key tables both v5 migrations read. All three were qualified by
// databases HIP-0132 dropped — o11y_logs and o11y_traces — so each migration
// logged a warning and returned NO duplicate keys, which is indistinguishable
// from "there are none". A migration that silently decides nothing needs
// migrating is the false empty in its worst form: invisible in any UI, visible
// six weeks later as a rule querying the wrong column.
//
// distributed_logs_attribute_keys IS event.log_key,
// distributed_logs_resource_keys IS event.log_resource_key and
// distributed_span_attributes_keys IS event.span_key.
var (
logKeyTable = telemetryplane.DBName + "." + telemetrylogs.LogKeyTableName
logResourceKeyTable = telemetryplane.DBName + "." + telemetrylogs.LogResourceKeyTableName
spanKeyTable = telemetryplane.DBName + "." + telemetrytraces.SpanKeyTableName
)
type queryBuilderV5Migration struct {
store sqlstore.SQLStore
telemetryStore telemetrystore.TelemetryStore
@@ -52,7 +71,7 @@ func (migration *queryBuilderV5Migration) Register(migrations *migrate.Migration
func (migration *queryBuilderV5Migration) getTraceDuplicateKeys(ctx context.Context) ([]string, error) {
query := `
SELECT tagKey
FROM o11y_traces.distributed_span_attributes_keys
FROM ` + spanKeyTable + `
WHERE tagType IN ('tag', 'resource')
GROUP BY tagKey
HAVING COUNT(DISTINCT tagType) > 1
@@ -83,9 +102,9 @@ func (migration *queryBuilderV5Migration) getLogDuplicateKeys(ctx context.Contex
query := `
SELECT name
FROM (
SELECT DISTINCT name FROM o11y_logs.distributed_logs_attribute_keys
SELECT DISTINCT name FROM ` + logKeyTable + `
INTERSECT
SELECT DISTINCT name FROM o11y_logs.distributed_logs_resource_keys
SELECT DISTINCT name FROM ` + logResourceKeyTable + `
)
ORDER BY name
`
@@ -48,9 +48,9 @@ func (migration *migrateRulesV4ToV5) getLogDuplicateKeys(ctx context.Context) ([
query := `
SELECT name
FROM (
SELECT DISTINCT name FROM o11y_logs.distributed_logs_attribute_keys
SELECT DISTINCT name FROM ` + logKeyTable + `
INTERSECT
SELECT DISTINCT name FROM o11y_logs.distributed_logs_resource_keys
SELECT DISTINCT name FROM ` + logResourceKeyTable + `
)
ORDER BY name
`
@@ -78,7 +78,7 @@ func (migration *migrateRulesV4ToV5) getLogDuplicateKeys(ctx context.Context) ([
func (migration *migrateRulesV4ToV5) getTraceDuplicateKeys(ctx context.Context) ([]string, error) {
query := `
SELECT tagKey
FROM o11y_traces.distributed_span_attributes_keys
FROM ` + spanKeyTable + `
WHERE tagType IN ('tag', 'resource')
GROUP BY tagKey
HAVING COUNT(DISTINCT tagType) > 1
+1 -1
View File
@@ -12,7 +12,7 @@ import (
// addErrorTracking creates the one net-new table backing error/crash tracking:
// o11y_issues (grouped-error lifecycle). Occurrences stay in the telemetry store
// (o11y_traces / o11y_logs); only non-derivable lifecycle state lives here. The
// (event.span / event.log); only non-derivable lifecycle state lives here. The
// unique index on (org_id, fingerprint) is the grouping key the ingest upsert
// conflicts on; (org_id, last_seen) serves the default list ordering.
type addErrorTracking struct {
+7 -1
View File
@@ -1,7 +1,13 @@
package telemetryaudit
import "github.com/hanzoai/o11y/pkg/telemetryplane"
// DBName is NOT the event plane. o11y_audit holds the audit trail, which has a
// different retention and a different access gate from telemetry and was not in
// HIP-0132's scope. The name is spelled in pkg/telemetryplane with the plane and
// the other survivors.
const (
DBName = "o11y_audit"
DBName = telemetryplane.AuditDBName
AuditLogsTableName = "distributed_logs"
AuditLogsLocalTableName = "logs"
TagAttributesTableName = "distributed_tag_attributes"
+3 -1
View File
@@ -1,5 +1,7 @@
package telemetrylogs
import "github.com/hanzoai/o11y/pkg/telemetryplane"
// Logs live in the ONE event database beside the other signals, as event.log:
// singular, because a row is one log line. It carries the same 15-column envelope as
// event.event / event.error / event.span, and is sorted (org, service, time) — the
@@ -9,7 +11,7 @@ package telemetrylogs
// name of a table are the same string. The pair is kept because the query builders
// distinguish the two roles.
const (
DBName = "event"
DBName = telemetryplane.DBName
LogTableName = "log"
LogLocalTableName = "log"
+14 -1
View File
@@ -1,7 +1,20 @@
package telemetrymetadata
import "github.com/hanzoai/o11y/pkg/telemetryplane"
// DBName is NOT the event plane, and that is deliberate.
//
// HIP-0132 unified the three SIGNAL databases — o11y_logs, o11y_traces,
// o11y_metrics — into `event`, and dropped them. It did not touch o11y_metadata,
// which the otel-collector metadata exporter still WRITES on every flush. A
// reader repointed at `event` would stop reading a table that is being fed,
// which is the same defect as reading a database that was dropped, pointed the
// other way.
//
// The name is spelled in pkg/telemetryplane with the plane and the other three
// survivors, so "which databases does this binary name?" has one answer.
const (
DBName = "o11y_metadata"
DBName = telemetryplane.MetadataDBName
AttributesMetadataTableName = "distributed_attributes_metadata"
AttributesMetadataLocalTableName = "attributes_metadata"
ColumnEvolutionMetadataTableName = "distributed_column_evolution_metadata"
+6 -1
View File
@@ -4,11 +4,16 @@ import (
"time"
"github.com/hanzoai/o11y/pkg/errors"
"github.com/hanzoai/o11y/pkg/telemetryplane"
"github.com/hanzoai/o11y/pkg/types/metrictypes"
)
// DBName is NOT the event plane. o11y_meter holds METERING — the billing-grade
// usage counters — which HIP-0132 did not unify and no writer has left. The name
// is spelled in pkg/telemetryplane beside the plane it is not part of, so
// "which databases does this binary name?" has one answer.
const (
DBName = "o11y_meter"
DBName = telemetryplane.MeterDBName
SamplesTableName = "distributed_samples"
SamplesLocalTableName = "samples"
SamplesAgg1dTableName = "distributed_samples_agg_1d"
+2 -1
View File
@@ -5,6 +5,7 @@ import (
"time"
"github.com/hanzoai/o11y/pkg/errors"
"github.com/hanzoai/o11y/pkg/telemetryplane"
"github.com/hanzoai/o11y/pkg/types/metrictypes"
)
@@ -20,7 +21,7 @@ import (
// distinguish the two roles, and a sharded deployment would reintroduce the split in
// exactly one place: here.
const (
DBName = "event"
DBName = telemetryplane.DBName
DescriptorTableName = "descriptor"
DescriptorLocalTableName = "descriptor"
+62
View File
@@ -0,0 +1,62 @@
// Package telemetryplane is the single answer to "which databases does this
// binary name?".
//
// WHY IT EXISTS. HIP-0132 unified the three SIGNAL databases — o11y_logs,
// o11y_traces, o11y_metrics — into one `event` database and dropped them. The
// commit that restored the reads (5be9208c4e, "o11y: read the event plane")
// pointed THREE files at `event` and said so in its own message:
//
// NOTE, deliberately left as-is: DBName is still a compile-time constant. It
// is now a CORRECT hardcode rather than a wrong one, but one name in three
// files is still three places to change.
//
// It was thirty files, not three. The other twenty-seven kept naming databases
// that had been dropped, so every read through them answered
// `Code: 81 ... UNKNOWN_DATABASE` — and the views on top of them rendered an
// error boundary, or worse a FALSE EMPTY that told the customer to instrument an
// already-instrumented application.
//
// This is that cut. A database name is a VALUE, so it has ONE address, and every
// layer can reach it because this package imports nothing. The signal packages,
// the legacy v3/v4 query builders, the Prometheus adapter and the query-service
// constants sit at four different depths and some of them are imported BY
// telemetrystore, so the constants cannot live there without a cycle.
//
// plane_test.go asserts that no OTHER file in the tree spells a database name.
// That assertion is the point of the package: a new hardcode fails the build
// instead of failing a customer's dashboard six weeks later.
package telemetryplane
// DBName is the telemetry plane: ONE database holding every signal —
// event.log, event.span, event.error and the metric tables. Every table in
// every signal is qualified by this and nothing else.
//
// It is a constant, not config: a deployment that pointed half its readers at a
// second database would be two planes wearing one name, and the failure mode is
// the silent one — half the rows.
const DBName = "event"
// THE DATABASES THAT ARE NOT THE PLANE.
//
// Listing them here is not an invitation to add more. It is what makes the
// question answerable: "the plane, and these four" is checkable, while a name
// spelled in whichever file happened to need it is not. Each has a live WRITER,
// which is the whole reason it was out of HIP-0132's scope — repointing a reader
// at `event` when the rows are still landing somewhere else is the same defect as
// reading a dropped database, pointed the other way.
//
// Each name is aliased by exactly one package, which owns its table names too:
//
// MetadataDBName -> telemetrymetadata (otel-collector metadata exporter writes it)
// MeterDBName -> telemetrymeter (metering/usage counters)
// AuditDBName -> telemetryaudit (audit trail; separate retention and gate)
// AnalyticsDBName -> implrulestatehistory (AddRuleStateHistory writes it)
//
// A name leaves this block by being MIGRATED — a writer moves, the rows move,
// the alias becomes DBName — never by being deleted.
const (
MetadataDBName = "o11y_metadata"
MeterDBName = "o11y_meter"
AuditDBName = "o11y_audit"
AnalyticsDBName = "o11y_analytics"
)
+268
View File
@@ -0,0 +1,268 @@
package telemetryplane_test
import (
"go/ast"
"go/parser"
"go/token"
"os"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/hanzoai/o11y/pkg/telemetryaudit"
"github.com/hanzoai/o11y/pkg/telemetrylogs"
"github.com/hanzoai/o11y/pkg/telemetrymetadata"
"github.com/hanzoai/o11y/pkg/telemetrymeter"
"github.com/hanzoai/o11y/pkg/telemetrymetrics"
"github.com/hanzoai/o11y/pkg/telemetryplane"
"github.com/hanzoai/o11y/pkg/telemetrytraces"
)
// TestPlaneIsOneName pins the value. Everything below only checks that nothing
// else spells it; this checks it is right.
func TestPlaneIsOneName(t *testing.T) {
assert.Equal(t, "event", telemetryplane.DBName)
}
// TestEverySignalResolvesToThePlane is the assertion HIP-0132 needed and did not
// have. The three signal packages each declared their own DBName; two of the
// three were wrong for six weeks and nothing failed.
func TestEverySignalResolvesToThePlane(t *testing.T) {
for name, got := range map[string]string{
"telemetrylogs": telemetrylogs.DBName,
"telemetrytraces": telemetrytraces.DBName,
"telemetrymetrics": telemetrymetrics.DBName,
} {
assert.Equalf(t, telemetryplane.DBName, got,
"%s.DBName must BE the plane, not a copy of its current value", name)
}
}
// TestSurvivorsAreAliasedNotRespelled — the four databases HIP-0132 did not
// unify still exist and still have writers, so their names are real. What is not
// allowed is spelling one twice: that is exactly how o11y_metadata ended up in
// three files and o11y_analytics in two.
func TestSurvivorsAreAliasedNotRespelled(t *testing.T) {
assert.Equal(t, telemetryplane.MetadataDBName, telemetrymetadata.DBName)
assert.Equal(t, telemetryplane.MeterDBName, telemetrymeter.DBName)
assert.Equal(t, telemetryplane.AuditDBName, telemetryaudit.DBName)
// And none of the survivors is the plane — an alias that collapsed onto
// `event` would silently read a database nothing writes.
for _, survivor := range []string{
telemetryplane.MetadataDBName,
telemetryplane.MeterDBName,
telemetryplane.AuditDBName,
telemetryplane.AnalyticsDBName,
} {
assert.NotEqual(t, telemetryplane.DBName, survivor)
}
}
// dropped matches a database HIP-0132 DELETED, in either form a Go string
// literal can carry it: bare (`"o11y_logs"`, how five constants held it) or as a
// SQL qualifier (`"… FROM o11y_traces.distributed_o11y_index_v3"`, how nineteen
// templates held it). Both forms shipped; both must be unreachable.
//
// It enumerates three names rather than matching `o11y_*` because
// `o11y_calls_total` and `o11y_latency_bucket` are Prometheus metrics this fork
// emits about ITSELF, not databases.
var dropped = regexp.MustCompile(`o11y_(logs|traces|metrics)\b`)
// survivor matches the four databases HIP-0132 did NOT unify. They are real, so
// naming one is not by itself a defect — spelling it in a second place is.
var survivor = regexp.MustCompile(`^o11y_(metadata|meter|audit|analytics)$`)
// TestNoDroppedDatabaseIsNamedAnywhere is the point of the package.
//
// THE DEFECT THIS CATCHES IS THE ONE THAT HAPPENED. HIP-0132 dropped o11y_logs,
// o11y_traces and o11y_metrics. A commit repointed THREE files at `event` and
// left thirty spelling the dropped names, and the tree stayed GREEN — because a
// string literal naming a database that no longer exists compiles perfectly, and
// because the golden tests asserted the same dead SQL the readers emitted. Two
// copies of a wrong answer agree with each other. The failure surfaced only in
// production: 316 `Code: 81 UNKNOWN_DATABASE` in thirty minutes, five views
// showing an error boundary or a false empty.
//
// That is why this walks TEST files too. A golden that encodes the broken SQL is
// not a test, it is a second vote for the bug.
//
// It parses rather than greps, so a dropped name inside a COMMENT — of which
// there are many, deliberately, recording what moved where — is not a finding.
// Only a string literal in code is.
func TestNoDroppedDatabaseIsNamedAnywhere(t *testing.T) {
root := repoRoot(t)
type finding struct{ file, lit string }
var findings []finding
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
switch info.Name() {
case ".git", "node_modules", "frontend", "vendor", "attic":
return filepath.SkipDir
}
return nil
}
if !strings.HasSuffix(path, ".go") {
return nil
}
rel, relErr := filepath.Rel(root, path)
if relErr != nil {
return relErr
}
// This package is the one address. Its own test is where the names are
// enumerated, so both are exempt.
if strings.HasPrefix(rel, filepath.Join("pkg", "telemetryplane")) {
return nil
}
// The datastore cutover migrations are a RECORD of the rename. They have
// to say the old names; that is what a migration is.
if strings.HasPrefix(rel, filepath.Join("deploy", "datastore")) {
return nil
}
fset := token.NewFileSet()
f, parseErr := parser.ParseFile(fset, path, nil, 0) // 0 => comments dropped
if parseErr != nil {
return nil // not our business to police unparseable files
}
ast.Inspect(f, func(n ast.Node) bool {
lit, ok := n.(*ast.BasicLit)
if !ok || lit.Kind != token.STRING {
return true
}
v, uqErr := strconv.Unquote(lit.Value)
if uqErr != nil {
return true
}
if m := dropped.FindString(v); m != "" {
findings = append(findings, finding{
file: rel + ":" + strconv.Itoa(fset.Position(lit.Pos()).Line),
lit: m,
})
}
return true
})
return nil
})
require.NoError(t, err)
sort.Slice(findings, func(i, j int) bool { return findings[i].file < findings[j].file })
if len(findings) > 0 {
var b strings.Builder
b.WriteString("a database HIP-0132 DROPPED is still named in code:\n")
for _, f := range findings {
b.WriteString(" " + f.file + " " + strconv.Quote(f.lit) + "\n")
}
b.WriteString("\nEvery read through one of these answers Code: 81 UNKNOWN_DATABASE.\n")
b.WriteString("Use telemetryplane.DBName, and the table constants in\n")
b.WriteString("pkg/telemetry{logs,traces,metrics}/tables.go for the tables.\n")
t.Fatal(b.String())
}
}
// repoRoot walks up from this test file to the directory holding go.mod. The
// walk above has to start from the repository, not from this package.
func repoRoot(t *testing.T) string {
t.Helper()
dir, err := os.Getwd()
require.NoError(t, err)
for {
if _, statErr := os.Stat(filepath.Join(dir, "go.mod")); statErr == nil {
return dir
}
parent := filepath.Dir(dir)
require.NotEqual(t, dir, parent, "walked to / without finding go.mod")
dir = parent
}
}
// TestSurvivorsAreSpelledOnce covers the other half. o11y_metadata, o11y_meter,
// o11y_audit and o11y_analytics still exist and still have writers, so naming one
// is correct — naming one TWICE is the defect, and it had already happened:
// o11y_metadata was spelled in three files and o11y_analytics in two, which is
// how a rename lands in one of them and not the others.
//
// Golden SQL fixtures are exempt: a fixture asserting
// `FROM o11y_audit.distributed_logs` is asserting the SQL a live reader emits,
// and it goes red on its own the moment that reader moves. What is checked here
// is the BARE literal — a constant declaration — which is the form that drifts
// silently.
func TestSurvivorsAreSpelledOnce(t *testing.T) {
root := repoRoot(t)
type finding struct{ file, lit string }
var findings []finding
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
switch info.Name() {
case ".git", "node_modules", "frontend", "vendor", "attic":
return filepath.SkipDir
}
return nil
}
if !strings.HasSuffix(path, ".go") {
return nil
}
rel, relErr := filepath.Rel(root, path)
if relErr != nil {
return relErr
}
if strings.HasPrefix(rel, filepath.Join("pkg", "telemetryplane")) {
return nil
}
if strings.HasPrefix(rel, filepath.Join("deploy", "datastore")) {
return nil
}
fset := token.NewFileSet()
f, parseErr := parser.ParseFile(fset, path, nil, 0)
if parseErr != nil {
return nil
}
ast.Inspect(f, func(n ast.Node) bool {
lit, ok := n.(*ast.BasicLit)
if !ok || lit.Kind != token.STRING {
return true
}
v, uqErr := strconv.Unquote(lit.Value)
if uqErr != nil {
return true
}
if survivor.MatchString(v) {
findings = append(findings, finding{
file: rel + ":" + strconv.Itoa(fset.Position(lit.Pos()).Line),
lit: v,
})
}
return true
})
return nil
})
require.NoError(t, err)
sort.Slice(findings, func(i, j int) bool { return findings[i].file < findings[j].file })
if len(findings) > 0 {
var b strings.Builder
b.WriteString("a database name is spelled outside pkg/telemetryplane:\n")
for _, f := range findings {
b.WriteString(" " + f.file + " " + strconv.Quote(f.lit) + "\n")
}
b.WriteString("\nUse the telemetryplane.<X>DBName alias. One value, one address.\n")
t.Fatal(b.String())
}
}
@@ -6,6 +6,7 @@ import (
"github.com/hanzoai/o11y/pkg/flagger/flaggertest"
"github.com/hanzoai/o11y/pkg/instrumentation/instrumentationtest"
"github.com/hanzoai/o11y/pkg/telemetryplane"
qbtypes "github.com/hanzoai/o11y/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/hanzoai/o11y/pkg/types/telemetrytypes"
"github.com/hanzoai/o11y/pkg/types/telemetrytypes/telemetrytypestest"
@@ -119,7 +120,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
start: testStartNs,
end: testEndNs,
expected: &qbtypes.Statement{
Query: "SELECT fingerprint FROM o11y_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Query: "SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", expectedBucketStart, expectedBucketEnd},
},
},
@@ -134,7 +135,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
start: testStartNs,
end: testEndNs,
expected: &qbtypes.Statement{
Query: "SELECT fingerprint FROM o11y_traces.distributed_traces_v3_resource WHERE ((simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND (simpleJSONExtractString(labels, 'k8s.namespace.name') = ? AND labels LIKE ? AND labels LIKE ?)) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Query: "SELECT fingerprint FROM event.span_resource WHERE ((simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND (simpleJSONExtractString(labels, 'k8s.namespace.name') = ? AND labels LIKE ? AND labels LIKE ?)) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", "production", "%k8s.namespace.name%", "%k8s.namespace.name\":\"production%", expectedBucketStart, expectedBucketEnd},
},
},
@@ -183,7 +184,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
start: testStartNs,
end: testEndNs,
expected: &qbtypes.Statement{
Query: "SELECT fingerprint FROM o11y_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') ILIKE ? AND labels LIKE ? AND labels ILIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Query: "SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') ILIKE ? AND labels LIKE ? AND labels ILIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Args: []any{"redis%", "%service.name%", "%service.name%redis%%", expectedBucketStart, expectedBucketEnd},
},
},
@@ -198,7 +199,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
start: testStartNs,
end: testEndNs,
expected: &qbtypes.Statement{
Query: "SELECT fingerprint FROM o11y_traces.distributed_traces_v3_resource WHERE (simpleJSONHas(labels, 'service.name') = ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Query: "SELECT fingerprint FROM event.span_resource WHERE (simpleJSONHas(labels, 'service.name') = ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Args: []any{true, "%service.name%", expectedBucketStart, expectedBucketEnd},
},
},
@@ -213,7 +214,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
start: testStartNs,
end: testEndNs,
expected: &qbtypes.Statement{
Query: "SELECT fingerprint FROM o11y_traces.distributed_traces_v3_resource WHERE (simpleJSONHas(labels, 'service.name') <> ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Query: "SELECT fingerprint FROM event.span_resource WHERE (simpleJSONHas(labels, 'service.name') <> ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Args: []any{true, expectedBucketStart, expectedBucketEnd},
},
},
@@ -228,7 +229,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
start: testStartNs,
end: testEndNs,
expected: &qbtypes.Statement{
Query: "SELECT fingerprint FROM o11y_traces.distributed_traces_v3_resource WHERE ((simpleJSONExtractString(labels, 'service.name') = ? OR simpleJSONExtractString(labels, 'service.name') = ?) AND labels LIKE ? AND (labels LIKE ? OR labels LIKE ?)) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Query: "SELECT fingerprint FROM event.span_resource WHERE ((simpleJSONExtractString(labels, 'service.name') = ? OR simpleJSONExtractString(labels, 'service.name') = ?) AND labels LIKE ? AND (labels LIKE ? OR labels LIKE ?)) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Args: []any{"redis", "postgres", "%service.name%", "%service.name\":\"redis%", "%service.name\":\"postgres%", expectedBucketStart, expectedBucketEnd},
},
},
@@ -243,7 +244,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
start: testStartNs,
end: testEndNs,
expected: &qbtypes.Statement{
Query: "SELECT fingerprint FROM o11y_traces.distributed_traces_v3_resource WHERE ((simpleJSONExtractString(labels, 'service.name') <> ? AND simpleJSONExtractString(labels, 'service.name') <> ?) AND (labels NOT LIKE ? AND labels NOT LIKE ?)) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Query: "SELECT fingerprint FROM event.span_resource WHERE ((simpleJSONExtractString(labels, 'service.name') <> ? AND simpleJSONExtractString(labels, 'service.name') <> ?) AND (labels NOT LIKE ? AND labels NOT LIKE ?)) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Args: []any{"redis", "postgres", "%service.name\":\"redis%", "%service.name\":\"postgres%", expectedBucketStart, expectedBucketEnd},
},
},
@@ -258,7 +259,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
start: testStartNs,
end: testEndNs,
expected: &qbtypes.Statement{
Query: "SELECT fingerprint FROM o11y_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') ILIKE ? AND labels LIKE ? AND labels ILIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Query: "SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') ILIKE ? AND labels LIKE ? AND labels ILIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Args: []any{"%redis%", "%service.name%", "%service.name%redis%", expectedBucketStart, expectedBucketEnd},
},
},
@@ -273,7 +274,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
start: testStartNs,
end: testEndNs,
expected: &qbtypes.Statement{
Query: "SELECT fingerprint FROM o11y_traces.distributed_traces_v3_resource WHERE (match(simpleJSONExtractString(labels, 'service.name'), ?) AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Query: "SELECT fingerprint FROM event.span_resource WHERE (match(simpleJSONExtractString(labels, 'service.name'), ?) AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Args: []any{"redis.*", "%service.name%", expectedBucketStart, expectedBucketEnd},
},
},
@@ -288,7 +289,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
start: testStartNs,
end: testEndNs,
expected: &qbtypes.Statement{
Query: "SELECT fingerprint FROM o11y_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') <> ? AND labels NOT LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Query: "SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') <> ? AND labels NOT LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Args: []any{"redis", "%service.name\":\"redis%", expectedBucketStart, expectedBucketEnd},
},
},
@@ -315,7 +316,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
start: testStartNs,
end: 0,
expected: &qbtypes.Statement{
Query: "SELECT fingerprint FROM o11y_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? GROUP BY fingerprint",
Query: "SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? GROUP BY fingerprint",
Args: []any{"redis", "%service.name%", "%service.name\":\"redis%", expectedBucketStart},
},
},
@@ -330,7 +331,7 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
start: testStartNs,
end: testEndNs,
expected: &qbtypes.Statement{
Query: "SELECT fingerprint FROM o11y_traces.distributed_traces_v3_resource WHERE NOT (((simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?))) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Query: "SELECT fingerprint FROM event.span_resource WHERE NOT (((simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?))) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Args: []any{"redis", "%service.name%", "%service.name\":\"redis%", expectedBucketStart, expectedBucketEnd},
},
},
@@ -355,8 +356,8 @@ func TestResourceFilterStatementBuilder_Traces(t *testing.T) {
builder := New[qbtypes.TraceAggregation](
instrumentationtest.New().ToProviderSettings(),
"o11y_traces",
"distributed_traces_v3_resource",
telemetryplane.DBName,
"span_resource",
telemetrytypes.SignalTraces,
telemetrytypes.SourceUnspecified,
mockMetadataStore,
@@ -406,7 +407,7 @@ func TestResourceFilterStatementBuilder_Logs(t *testing.T) {
start: testStartNs,
end: testEndNs,
expected: &qbtypes.Statement{
Query: "SELECT fingerprint FROM o11y_logs.distributed_logs_v2_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Query: "SELECT fingerprint FROM event.log_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", expectedBucketStart, expectedBucketEnd},
},
},
@@ -443,7 +444,7 @@ func TestResourceFilterStatementBuilder_Logs(t *testing.T) {
start: testStartNs,
end: testEndNs,
expected: &qbtypes.Statement{
Query: "SELECT fingerprint FROM o11y_logs.distributed_logs_v2_resource WHERE ((simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND (simpleJSONExtractString(labels, 'k8s.namespace.name') = ? AND labels LIKE ? AND labels LIKE ?)) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Query: "SELECT fingerprint FROM event.log_resource WHERE ((simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND (simpleJSONExtractString(labels, 'k8s.namespace.name') = ? AND labels LIKE ? AND labels LIKE ?)) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Args: []any{"redis", "%service.name%", "%service.name\":\"redis%", "default", "%k8s.namespace.name%", "%k8s.namespace.name\":\"default%", expectedBucketStart, expectedBucketEnd},
},
},
@@ -461,7 +462,7 @@ func TestResourceFilterStatementBuilder_Logs(t *testing.T) {
start: uint64(1769976178000000000), // These will give bucket start 1769974378 and end 1770062578
end: uint64(1770062578000000000),
expected: &qbtypes.Statement{
Query: "SELECT fingerprint FROM o11y_logs.distributed_logs_v2_resource WHERE ((simpleJSONExtractString(labels, 'env') = ? AND labels LIKE ? AND labels LIKE ?) AND (simpleJSONExtractString(labels, 'k8s.deployment.name') = ? AND labels LIKE ? AND labels LIKE ?)) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Query: "SELECT fingerprint FROM event.log_resource WHERE ((simpleJSONExtractString(labels, 'env') = ? AND labels LIKE ? AND labels LIKE ?) AND (simpleJSONExtractString(labels, 'k8s.deployment.name') = ? AND labels LIKE ? AND labels LIKE ?)) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Args: []any{"prod", "%env%", "%env\":\"prod%", "prod-deployment", "%k8s.deployment.name%", "%k8s.deployment.name\":\"prod-deployment%", uint64(1769974378), uint64(1770062578)},
},
},
@@ -549,8 +550,8 @@ func TestResourceFilterStatementBuilder_Logs(t *testing.T) {
builder := New[qbtypes.LogAggregation](
instrumentationtest.New().ToProviderSettings(),
"o11y_logs",
"distributed_logs_v2_resource",
telemetryplane.DBName,
"log_resource",
telemetrytypes.SignalLogs,
telemetrytypes.SourceUnspecified,
mockMetadataStore,
@@ -606,7 +607,7 @@ func TestResourceFilterStatementBuilder_Variables(t *testing.T) {
start: testStartNs,
end: testEndNs,
expected: &qbtypes.Statement{
Query: "SELECT fingerprint FROM o11y_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Query: "SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint",
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", expectedBucketStart, expectedBucketEnd},
},
},
@@ -617,8 +618,8 @@ func TestResourceFilterStatementBuilder_Variables(t *testing.T) {
builder := New[qbtypes.TraceAggregation](
instrumentationtest.New().ToProviderSettings(),
"o11y_traces",
"distributed_traces_v3_resource",
telemetryplane.DBName,
"span_resource",
telemetrytypes.SignalTraces,
telemetrytypes.SourceUnspecified,
mockMetadataStore,
+3 -1
View File
@@ -1,5 +1,7 @@
package telemetrytraces
import "github.com/hanzoai/o11y/pkg/telemetryplane"
// Spans live in the ONE event database beside the other signals, as event.span:
// singular, because a row is one span. It carries the same 15-column envelope as
// event.event / event.error / event.log, so correlating a span with an error is a
@@ -10,7 +12,7 @@ package telemetrytraces
// name of a table are the same string. The pair is kept because the query builders
// distinguish the two roles.
const (
DBName = "event"
DBName = telemetryplane.DBName
SpanTableName = "span"
SpanLocalTableName = "span"
+2 -2
View File
@@ -139,7 +139,7 @@ func TestTraceTimeRangeFinderQuery(t *testing.T) {
SELECT
toUnixTimestamp64Nano(min(timestamp)) as start_time,
toUnixTimestamp64Nano(max(timestamp)) as end_time
FROM o11y_traces.distributed_o11y_spans
FROM event.span
WHERE traceID = ?
AND timestamp >= now() - INTERVAL 30 DAY
`
@@ -150,7 +150,7 @@ func TestTraceTimeRangeFinderQuery(t *testing.T) {
SELECT
toUnixTimestamp64Nano(min(timestamp)) as start_time,
toUnixTimestamp64Nano(max(timestamp)) as end_time
FROM o11y_traces.distributed_o11y_spans
FROM event.span
WHERE traceID = ?
AND timestamp >= now() - INTERVAL 30 DAY
`
+2 -2
View File
@@ -39,8 +39,8 @@ func (s IssueStatus) Valid() bool {
const DefaultLevel = "error"
// Issue is the grouped error — a fingerprint bucket. It is the ONE net-new table
// backing error tracking. Occurrences live in the telemetry store (o11y_traces /
// o11y_logs); only the lifecycle state that CANNOT be derived from telemetry —
// backing error tracking. Occurrences live in the telemetry store (event.span /
// event.log); only the lifecycle state that CANNOT be derived from telemetry —
// status, assignee, first/last-seen, running count, regression — lives here.
// Grouping is done at INGEST (the shim computes the fingerprint), so the Issues
// list is a plain org-scoped SELECT, never an unscoped scan over an org-less
+1 -1
View File
@@ -5,7 +5,7 @@ import "time"
// Occurrence is a single normalized error event (one exception instance). It is
// derived from a Sentry event (envelope / legacy store item) or, later, from an
// OTel exception span-event. It is the OTel-shaped occurrence the shim persists
// to o11y_logs (the reused occurrence store) and the "latest event" sample kept
// to event.log (the reused occurrence store) and the "latest event" sample kept
// on the issue for the detail view. Purely a value — no store, no tags of its own.
type Occurrence struct {
EventID string `json:"eventId"`
+1 -1
View File
@@ -53,7 +53,7 @@ type EventStore interface {
ListForFingerprint(ctx context.Context, orgID, projectID valuer.UUID, fingerprint string, limit int) ([]*Event, error)
// ListForTrace returns the (org, project)-scoped error events referencing a trace
// id — the tenant-safe "errors in this trace" detail (the o11y_traces span plane is
// id — the tenant-safe "errors in this trace" detail (the event.span plane is
// NOT read: it has no general org column and cannot be tenant-scoped).
ListForTrace(ctx context.Context, orgID, projectID valuer.UUID, traceID string, limit int) ([]*Event, error)
+12 -12
View File
@@ -15,12 +15,12 @@ func TestTransform(t *testing.T) {
{
name: "Example 1: Only service_name is __all__",
sql: `SELECT trace_id, name, kind, status
FROM o11y_traces.o11y_index_v3
FROM event.span
WHERE service_name = $service_name
AND operation_name IN ($operation_names)
AND duration >= $min_duration
AND attributes['http.method'] = $http_method
AND op IN (SELECT op FROM o11y_traces.operations WHERE service_name = $service_name AND kind = 'server')
AND op IN (SELECT op FROM event.operation WHERE service_name = $service_name AND kind = 'server')
AND timestamp BETWEEN $start_time AND $end_time`,
// Define our variables and their values/metadata
@@ -62,17 +62,17 @@ WHERE service_name = $service_name
FieldType: "scalar",
},
},
expected: `SELECT trace_id, name, kind, status FROM o11y_traces.o11y_index_v3 WHERE operation_name IN ($operation_names) AND duration >= $min_duration AND attributes['http.method'] = $http_method AND op IN (SELECT op FROM o11y_traces.operations WHERE kind = 'server') AND timestamp BETWEEN $start_time AND $end_time;`,
expected: `SELECT trace_id, name, kind, status FROM event.span WHERE operation_name IN ($operation_names) AND duration >= $min_duration AND attributes['http.method'] = $http_method AND op IN (SELECT op FROM event.operation WHERE kind = 'server') AND timestamp BETWEEN $start_time AND $end_time;`,
},
{
name: "Example 2: Multiple __all__ selections and map lookups",
sql: `SELECT trace_id, name, kind, status
FROM o11y_traces.o11y_index_v3
FROM event.span
WHERE service_name = $service_name
AND operation_name IN ($operation_names)
AND duration >= $min_duration
AND attributes['http.method'] = $http_method
AND op IN (SELECT op FROM o11y_traces.operations WHERE service_name = $service_name AND kind = 'server')
AND op IN (SELECT op FROM event.operation WHERE service_name = $service_name AND kind = 'server')
AND timestamp BETWEEN $start_time AND $end_time`,
variables: []VariableValue{
@@ -113,17 +113,17 @@ WHERE service_name = $service_name
FieldType: "scalar",
},
},
expected: `SELECT trace_id, name, kind, status FROM o11y_traces.o11y_index_v3 WHERE attributes['http.method'] = $http_method AND op IN (SELECT op FROM o11y_traces.operations WHERE kind = 'server') AND timestamp BETWEEN $start_time AND $end_time;`,
expected: `SELECT trace_id, name, kind, status FROM event.span WHERE attributes['http.method'] = $http_method AND op IN (SELECT op FROM event.operation WHERE kind = 'server') AND timestamp BETWEEN $start_time AND $end_time;`,
},
{
name: "Example 3: Multiple __all__ selections and map lookups",
sql: `SELECT trace_id, name, kind, status
FROM o11y_traces.o11y_index_v3
FROM event.span
WHERE service_name = $service_name
AND operation_name IN ($operation_names)
AND duration >= $min_duration
AND attributes['http.method'] = $http_method
AND op IN (SELECT op FROM o11y_traces.operations WHERE service_name = $service_name AND kind = 'server')
AND op IN (SELECT op FROM event.operation WHERE service_name = $service_name AND kind = 'server')
AND timestamp BETWEEN $start_time AND $end_time`,
variables: []VariableValue{
@@ -152,17 +152,17 @@ WHERE service_name = $service_name
FieldType: "map",
},
},
expected: `SELECT trace_id, name, kind, status FROM o11y_traces.o11y_index_v3 WHERE op IN (SELECT op FROM o11y_traces.operations WHERE kind = 'server') AND timestamp BETWEEN $start_time AND $end_time;`,
expected: `SELECT trace_id, name, kind, status FROM event.span WHERE op IN (SELECT op FROM event.operation WHERE kind = 'server') AND timestamp BETWEEN $start_time AND $end_time;`,
},
{
name: "Example 3: Multiple __all__ selections and map lookups",
sql: `SELECT trace_id, name, kind, status
FROM o11y_traces.o11y_index_v3
FROM event.span
WHERE service_name = {{service_name}}
AND operation_name IN {{.operation_names}}
AND duration >= {{min_duration}}
AND attributes['http.method'] = $http_method
AND op IN (SELECT op FROM o11y_traces.operations WHERE service_name = {{service_name}} AND kind = 'server')`,
AND op IN (SELECT op FROM event.operation WHERE service_name = {{service_name}} AND kind = 'server')`,
variables: []VariableValue{
{
@@ -190,7 +190,7 @@ WHERE service_name = {{service_name}}
FieldType: "map",
},
},
expected: `SELECT trace_id, name, kind, status FROM o11y_traces.o11y_index_v3 WHERE op IN (SELECT op FROM o11y_traces.operations WHERE kind = 'server');`,
expected: `SELECT trace_id, name, kind, status FROM event.span WHERE op IN (SELECT op FROM event.operation WHERE kind = 'server');`,
},
}
+2 -2
View File
@@ -22,8 +22,8 @@
"@zap-proto/zap": "^1.6.0"
},
"devDependencies": {
"@types/node": "^22.0.0",
"typescript": "^5.6.0"
"@types/node": "26.4.0",
"typescript": "7.0.2"
},
"engines": {
"node": ">=18.12.0"
+8 -8
View File
@@ -32,14 +32,14 @@
"author": "",
"license": "MIT",
"devDependencies": {
"@playwright/test": "^1.57.0-alpha-2025-10-09",
"@types/node": "^20.0.0",
"dotenv": "^16.0.0",
"eslint-plugin-playwright": "^2.10.2",
"oxfmt": "^0.41.0",
"oxlint": "^1.59.0",
"oxlint-tsgolint": "^0.20.0",
"typescript": "^5.0.0"
"@playwright/test": "^1.62.1",
"@types/node": "26.4.0",
"dotenv": "^17.4.2",
"eslint-plugin-playwright": "^2.11.0",
"oxfmt": "^0.65.0",
"oxlint": "^1.80.0",
"oxlint-tsgolint": "^7.0.2001",
"typescript": "7.0.2"
},
"engines": {
"node": ">=18.0.0",