diff --git a/.github/workflows/node-clickhouse-cluster.js.yml b/.github/workflows/node-clickhouse-cluster.js.yml index fbfddb72..50a65a72 100644 --- a/.github/workflows/node-clickhouse-cluster.js.yml +++ b/.github/workflows/node-clickhouse-cluster.js.yml @@ -39,6 +39,11 @@ jobs: - run: npm run postinstall - run: git submodule init - run: git submodule update + - name: Install Compose + uses: ndeloof/install-compose-action@v0.0.1 + with: + version: v2.1.0 # defaults to 'latest' + legacy: true # will also install in PATH as `docker-compose` - run: docker-compose -f docker/e2e/docker-compose-cluster.yaml up -d - run: sleep 5 - name: Workflow Telemetry diff --git a/patterns/patterns_bin/src/pattern_reg.rs b/patterns/patterns_bin/src/pattern_reg.rs new file mode 100644 index 00000000..f8657961 --- /dev/null +++ b/patterns/patterns_bin/src/pattern_reg.rs @@ -0,0 +1,45 @@ +use crate::pattern::Pattern; +use uuid::Uuid; + +pub struct PatternRegistry { + patterns: Vec, +} + +impl PatternRegistry { + pub const fn new() -> PatternRegistry { + PatternRegistry { patterns: Vec::new() } + } + + pub fn find_pattern(&mut self, str_text: &Vec, i_text: &Vec, sample: String) -> &Pattern { + let mut idx: i32 = -1; + let mut mtc = 0; + for i in 0..self.patterns.len() { + mtc = self.patterns[i].match_text(&i_text); + if mtc == -1 || mtc > self.patterns[i].fluct { + continue; + } + idx = i as i32; + break; + } + + if idx == -1 { + let pattern = Pattern::new(Uuid::new_v4().to_string(), &i_text, &str_text, sample); + self.patterns.push(pattern); + idx = (self.patterns.len() - 1) as i32; + } else if mtc != 0 { + self.patterns[idx as usize].adjust_pattern(&i_text); + } + return &self.patterns[idx as usize]; + } + + pub fn to_string(&self) -> String { + let mut s = String::new(); + for i in 0..self.patterns.len() { + s += self.patterns[i].to_string().as_str(); + s += "\n"; + } + return s + } +} + +pub static mut REGISTRY: PatternRegistry = PatternRegistry::new(); \ No newline at end of file diff --git a/patterns/patterns_bin/src/tokens.rs b/patterns/patterns_bin/src/tokens.rs new file mode 100644 index 00000000..5d3f4449 --- /dev/null +++ b/patterns/patterns_bin/src/tokens.rs @@ -0,0 +1,45 @@ +use regex::{Regex, CaptureMatches, Match}; + +/*pub fn tokenize(re: &Regex, text: &str) -> CaptureMatches { + return re.captures_iter(text); +}*/ + +pub struct Tokenizer<'a> { + text: String, + pos: usize, + re: Regex, + iter: Option> +} + +impl Tokenizer<'_> { + pub fn new<'a>(text: &'a str) -> Tokenizer<'a> { + let mut res = Tokenizer { + text: text.to_string(), + pos: 0, + re: Regex::new(r"([\p{L}_]+|[\d.]+|[^\p{L}_\d.]+)\s*").unwrap(), + iter: None + }; + res + } +} + +impl Iterator for Tokenizer<'_> { + type Item = String; + + fn next(&mut self) -> Option { + None + /*let cap: Option = None; + if let Some(c) = cap { + self.pos += c.get(0).unwrap().end(); + Some(c.get(0).unwrap().as_str().to_string()) + } else { + None + }*/ + } +} + +#[test] +fn test_tokenizer() { + let text = "Hello, world! 123"; + let mut tokenizer = Tokenizer::new(text); +} \ No newline at end of file diff --git a/pyroscope/flamebearer.d.ts b/pyroscope/flamebearer.d.ts new file mode 100644 index 00000000..7ec2f880 --- /dev/null +++ b/pyroscope/flamebearer.d.ts @@ -0,0 +1,67 @@ + +type int64 = string; +type uint64 = string; +type units = string; + +export interface Flamebearer { + version: number, + flamebearerProfileV1: flamebearerProfileV1 + telemetry?: {[key: string]: any} +} + +export interface flamebearerProfileV1 { + flamebearer: flamebearerV1, + metadata: flamebearerMetadataV1, + timeline: flamebearerTimelineV1, + groups: {[key: string]: flamebearerTimelineV1} + heatmap: heatmap, + leftTicks: string, + rightTicks: string, +} + +export interface flamebearerV1 { + names: string, + levels: [[number]], + numTicks: number, + maxSelf: number +} + +export interface flamebearerMetadataV1 { + format: string, + spyName: string, + sampleRate: number, + units: units, + name: string +} + +export interface flamebearerTimelineV1 { + startTime: int64, + samples: [uint64] + durationDelta: int64, + watermarks: {[key: number]: int64} +} + +export interface heatmap { + values: [[uint64]], + timeBuckets: int64, + valueBuckets: int64, + startTime: int64, + endTime: int64, + minValue: uint64, + maxValue: uint64, + minDepth: uint64, + maxDepth: uint64 +} + +export interface level { + values: number[] +} + +export interface flamegraphDiff { + name: string[], + levels: level[], + total: int64, + maxSelf: int64, + leftTicks: int64, + rightTicks: int64 +} diff --git a/pyroscope/json_parsers.js b/pyroscope/json_parsers.js new file mode 100644 index 00000000..48d8e27a --- /dev/null +++ b/pyroscope/json_parsers.js @@ -0,0 +1,57 @@ +const { bufferize } = require('./shared') + +/** + * + * @param req + */ +const series = async (req, payload) => { + let body = await bufferize(payload) + body = JSON.parse(body.toString()) + req.type = 'json' + return { + getStart: () => body.start, + getEnd: () => body.end, + getMatchersList: () => body.matchers, + getLabelNamesList: () => body.labelNames + } +} + +const getProfileStats = async (req, payload) => { + req.type = 'json' + return null +} + +const settingsGet = async (req, payload) => { + req.type = 'json' + return {} +} + +const labelNames = async (req, payload) => { + req.type = 'json' + let body = await bufferize(payload) + body = JSON.parse(body.toString()) + return { + getStart: () => body.start, + getEnd: () => body.end, + getName: () => body.name + } +} + +const analyzeQuery = async (req, payload) => { + req.type = 'json' + let body = await bufferize(payload) + body = JSON.parse(body.toString()) + return { + getStart: () => body.start, + getEnd: () => body.end, + getQuery: () => body.query + } +} + +module.exports = { + series, + getProfileStats, + labelNames, + settingsGet, + analyzeQuery +} diff --git a/pyroscope/merge_stack_traces.js b/pyroscope/merge_stack_traces.js new file mode 100644 index 00000000..f8664712 --- /dev/null +++ b/pyroscope/merge_stack_traces.js @@ -0,0 +1,173 @@ +const { checkVersion, DATABASE_NAME } = require('../lib/utils') +const Sql = require('@cloki/clickhouse-sql') +const { clusterName } = require('../common') +const clickhouse = require('../lib/db/clickhouse') +const { readULeb32 } = require('./pprof') +const pprofBin = require('./pprof-bin/pkg') +const { + serviceNameSelectorQuery, + labelSelectorQuery +} = require('./shared') + +const sqlWithReference = (ref) => { + const res = new Sql.WithReference(ref) + res.toString = function () { + if (this.ref.inline) { + return `(${this.ref.query.toString()}) as ${this.ref.alias}` + } + return this.ref.alias + } + return res +} + +let ctxIdx = 0 + +const newCtxIdx = () => ++ctxIdx + +const importStackTraces = async (typeRegex, sel, fromTimeSec, toTimeSec, log, _ctxIdx, save) => { + const dist = clusterName ? '_dist' : '' + const v2 = checkVersion('profiles_v2', (fromTimeSec - 3600) * 1000) + const serviceNameSelector = serviceNameSelectorQuery(sel) + const typeIdSelector = Sql.Eq( + 'type_id', + Sql.val(`${typeRegex.type}:${typeRegex.periodType}:${typeRegex.periodUnit}`) + ) + const idxSelect = (new Sql.Select()) + .select('fingerprint') + .from(`${DATABASE_NAME()}.profiles_series_gin`) + .where( + Sql.And( + Sql.Eq(new Sql.Raw(`has(sample_types_units, (${Sql.quoteVal(typeRegex.sampleType)},${Sql.quoteVal(typeRegex.sampleUnit)}))`), 1), + typeIdSelector, + Sql.Gte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(fromTimeSec)}))`)), + Sql.Lte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(toTimeSec)}))`)), + serviceNameSelector + ) + ).groupBy('fingerprint') + labelSelectorQuery(idxSelect, sel) + const withIdxSelect = new Sql.With('idx', idxSelect, !!clusterName) + const rawReq = (new Sql.Select()).with(withIdxSelect) + .select([ + new Sql.Raw(`arrayMap(x -> (x.1, x.2, x.3, (arrayFirst(y -> y.1 == ${Sql.quoteVal(`${typeRegex.sampleType}:${typeRegex.sampleUnit}`)}, x.4) as af).2, af.3), tree)`), + 'tree' + ], 'functions') + .from(`${DATABASE_NAME()}.profiles${dist}`) + .where( + Sql.And( + Sql.Gte('timestamp_ns', new Sql.Raw(Math.floor(fromTimeSec) + '000000000')), + Sql.Lte('timestamp_ns', new Sql.Raw(Math.floor(toTimeSec) + '000000000')), + new Sql.In('fingerprint', 'IN', sqlWithReference(withIdxSelect)), + typeIdSelector, + serviceNameSelector + )) + if (process.env.ADVANCED_PROFILES_MERGE_LIMIT) { + rawReq.orderBy(['timestamp_ns', 'desc']).limit(parseInt(process.env.ADVANCED_PROFILES_MERGE_LIMIT)) + } + const withRawReq = new Sql.With('raw', rawReq, !!clusterName) + const joinedReq = (new Sql.Select()).with(withRawReq).select([ + new Sql.Raw('(raw.tree.1, raw.tree.2, raw.tree.3, sum(raw.tree.4), sum(raw.tree.5))'), + 'tree2' + ]).from(sqlWithReference(withRawReq)) + .join('raw.tree', 'array') + .groupBy(new Sql.Raw('raw.tree.1'), new Sql.Raw('raw.tree.2'), new Sql.Raw('raw.tree.3')) + .orderBy(new Sql.Raw('raw.tree.1')).limit(2000000) + const withJoinedReq = new Sql.With('joined', joinedReq, !!clusterName) + const joinedAggregatedReq = (new Sql.Select()).select( + [new Sql.Raw('groupArray(tree2)'), 'tree']).from(sqlWithReference(withJoinedReq)) + const functionsReq = (new Sql.Select()).select( + [new Sql.Raw('groupUniqArray(raw.functions)'), 'functions2'] + ).from(sqlWithReference(withRawReq)).join('raw.functions', 'array') + + let brackLegacy = (new Sql.Select()).select( + [new Sql.Raw('[]::Array(String)'), 'legacy'] + ) + let withLegacy = null + if (!v2) { + const legacy = (new Sql.Select()).with(withIdxSelect) + .select('payload') + .from(`${DATABASE_NAME()}.profiles${dist}`) + .where( + Sql.And( + Sql.Gte('timestamp_ns', new Sql.Raw(Math.floor(fromTimeSec) + '000000000')), + Sql.Lte('timestamp_ns', new Sql.Raw(Math.floor(toTimeSec) + '000000000')), + new Sql.In('fingerprint', 'IN', sqlWithReference(withIdxSelect)), + Sql.Eq(new Sql.Raw('empty(tree)'), 1), + typeIdSelector, + serviceNameSelector + )) + if (process.env.ADVANCED_PROFILES_MERGE_LIMIT) { + legacy.orderBy(['timestamp_ns', 'desc']).limit(parseInt(process.env.ADVANCED_PROFILES_MERGE_LIMIT)) + } + withLegacy = new Sql.With('legacy', legacy, !!clusterName) + brackLegacy = (new Sql.Select()) + .select([new Sql.Raw('groupArray(payload)'), 'payloads']) + .from(sqlWithReference(withLegacy)) + } + brackLegacy = new Sql.Raw(`(${brackLegacy.toString()})`) + const brack1 = new Sql.Raw(`(${joinedAggregatedReq.toString()})`) + const brack2 = new Sql.Raw(`(${functionsReq.toString()})`) + + const sqlReq = (new Sql.Select()) + .select( + [brackLegacy, 'legacy'], + [brack2, 'functions'], + [brack1, 'tree'] + ) + if (v2) { + sqlReq.with(withJoinedReq, withRawReq) + } else { + sqlReq.with(withJoinedReq, withRawReq, withLegacy) + } + + let start = Date.now() + const profiles = await clickhouse.rawRequest(sqlReq.toString() + ' FORMAT RowBinary', + null, + DATABASE_NAME(), + { + responseType: 'arraybuffer' + }) + const binData = Uint8Array.from(profiles.data) + log.debug(`selectMergeStacktraces: profiles downloaded: ${binData.length / 1025}kB in ${Date.now() - start}ms`) + require('./pprof-bin/pkg/pprof_bin').init_panic_hook() + const [legacyLen, shift] = readULeb32(binData, 0) + let ofs = shift + let mergePprofLat = BigInt(0) + for (let i = 0; i < legacyLen; i++) { + const [profLen, shift] = readULeb32(binData, ofs) + ofs += shift + start = process.hrtime?.bigint ? process.hrtime.bigint() : BigInt(0) + pprofBin.merge_prof(_ctxIdx, + Uint8Array.from(profiles.data.slice(ofs, ofs + profLen)), + `${typeRegex.sampleType}:${typeRegex.sampleUnit}`) + mergePprofLat += (process.hrtime?.bigint ? process.hrtime.bigint() : BigInt(0)) - start + ofs += profLen + } + start = process.hrtime?.bigint ? process.hrtime.bigint() : BigInt(0) + save && require('fs').writeFileSync(`/home/hromozeka/QXIP/qryn/data.${Date.now()}.bin`, + Buffer.from(Uint8Array.from(profiles.data.slice(ofs)))) + pprofBin.merge_tree(_ctxIdx, Uint8Array.from(profiles.data.slice(ofs)), + typeRegex.sampleType + ':' + typeRegex.sampleUnit) + const mergeTreeLat = (process.hrtime?.bigint ? process.hrtime.bigint() : BigInt(0)) - start + log.debug(`merge_pprof: ${mergePprofLat / BigInt(1000000)}ms`) + log.debug(`merge_tree: ${mergeTreeLat / BigInt(1000000)}ms`) +} + +const mergeStackTraces = async (typeRegex, sel, fromTimeSec, toTimeSec, log) => { + const _ctxIdx = newCtxIdx() + try { + await importStackTraces(typeRegex, sel, fromTimeSec, toTimeSec, log, _ctxIdx) + const start = process.hrtime?.bigint ? process.hrtime.bigint() : BigInt(0) + const resp = pprofBin.export_tree(_ctxIdx, typeRegex.sampleType + ':' + typeRegex.sampleUnit) + const exportTreeLat = (process.hrtime?.bigint ? process.hrtime.bigint() : BigInt(0)) - start + log.debug(`export_tree: ${exportTreeLat / BigInt(1000000)}ms`) + return Buffer.from(resp) + } finally { + try { pprofBin.drop_tree(_ctxIdx) } catch (e) {} + } +} + +module.exports = { + mergeStackTraces, + importStackTraces, + newCtxIdx +} diff --git a/pyroscope/pprof-bin/pkg/pprof_bin.d.ts b/pyroscope/pprof-bin/pkg/pprof_bin.d.ts index f4204d23..ccbddd41 100644 --- a/pyroscope/pprof-bin/pkg/pprof_bin.d.ts +++ b/pyroscope/pprof-bin/pkg/pprof_bin.d.ts @@ -13,6 +13,13 @@ export function merge_prof(id: number, bytes: Uint8Array, sample_type: string): */ export function merge_tree(id: number, bytes: Uint8Array, sample_type: string): void; /** +* @param {number} id1 +* @param {number} id2 +* @param {string} sample_type +* @returns {Uint8Array} +*/ +export function diff_tree(id1: number, id2: number, sample_type: string): Uint8Array; +/** * @param {number} id * @param {string} sample_type * @returns {Uint8Array} diff --git a/pyroscope/pprof-bin/pkg/pprof_bin.js b/pyroscope/pprof-bin/pkg/pprof_bin.js index 913b1d40..25da605f 100644 --- a/pyroscope/pprof-bin/pkg/pprof_bin.js +++ b/pyroscope/pprof-bin/pkg/pprof_bin.js @@ -133,6 +133,28 @@ function getArrayU8FromWasm0(ptr, len) { ptr = ptr >>> 0; return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len); } +/** +* @param {number} id1 +* @param {number} id2 +* @param {string} sample_type +* @returns {Uint8Array} +*/ +module.exports.diff_tree = function(id1, id2, sample_type) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0(sample_type, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + wasm.diff_tree(retptr, id1, id2, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var v2 = getArrayU8FromWasm0(r0, r1).slice(); + wasm.__wbindgen_free(r0, r1 * 1, 1); + return v2; + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +}; + /** * @param {number} id * @param {string} sample_type diff --git a/pyroscope/pprof-bin/pkg/pprof_bin_bg.wasm b/pyroscope/pprof-bin/pkg/pprof_bin_bg.wasm index fcb06ee5..a110fa20 100644 Binary files a/pyroscope/pprof-bin/pkg/pprof_bin_bg.wasm and b/pyroscope/pprof-bin/pkg/pprof_bin_bg.wasm differ diff --git a/pyroscope/pprof-bin/pkg/pprof_bin_bg.wasm.d.ts b/pyroscope/pprof-bin/pkg/pprof_bin_bg.wasm.d.ts index c1259bcb..6dc10bc2 100644 --- a/pyroscope/pprof-bin/pkg/pprof_bin_bg.wasm.d.ts +++ b/pyroscope/pprof-bin/pkg/pprof_bin_bg.wasm.d.ts @@ -3,6 +3,7 @@ export const memory: WebAssembly.Memory; export function merge_prof(a: number, b: number, c: number, d: number, e: number): void; export function merge_tree(a: number, b: number, c: number, d: number, e: number): void; +export function diff_tree(a: number, b: number, c: number, d: number, e: number): void; export function export_tree(a: number, b: number, c: number, d: number): void; export function export_trees_pprof(a: number, b: number, c: number): void; export function drop_tree(a: number): void; diff --git a/pyroscope/pprof-bin/src/lib.rs b/pyroscope/pprof-bin/src/lib.rs index 493d3992..e3985831 100644 --- a/pyroscope/pprof-bin/src/lib.rs +++ b/pyroscope/pprof-bin/src/lib.rs @@ -2,6 +2,7 @@ mod ch64; mod merge; +use std::cmp::Ordering; use ch64::city_hash_64; use ch64::read_uint64_le; use lazy_static::lazy_static; @@ -12,6 +13,7 @@ use pprof_pb::google::v1::Sample; use pprof_pb::querier::v1::FlameGraph; use pprof_pb::querier::v1::Level; use pprof_pb::querier::v1::SelectMergeStacktracesResponse; +use pprof_pb::querier::v1::FlameGraphDiff; use prost::Message; use std::collections::{HashMap, HashSet}; use std::io::Read; @@ -19,6 +21,8 @@ use std::panic; use std::sync::Mutex; use std::vec::Vec; use wasm_bindgen::prelude::*; +use std::sync::Arc; + pub mod pprof_pb { @@ -47,16 +51,51 @@ struct TreeNodeV2 { total: Vec, } +impl TreeNodeV2 { + pub fn clone(&self) -> TreeNodeV2 { + TreeNodeV2 { + fn_id: self.fn_id, + node_id: self.node_id, + slf: self.slf.clone(), + total: self.total.clone(), + } + } + pub fn set_total_and_self(&self, slf: Vec, total: Vec) -> TreeNodeV2 { + let mut res = self.clone(); + res.slf = slf; + res.total = total; + return res; + } +} + struct Tree { names: Vec, names_map: HashMap, - nodes: HashMap>, + nodes: HashMap>>, sample_types: Vec, max_self: Vec, nodes_num: i32, } -fn find_node(id: u64, nodes: &Vec) -> i32 { +impl Tree { + pub fn total(&self) -> i64 { + let mut total: i64 = 0; + for c in 0..self.nodes.get(&0).unwrap().len() { + let _c = &self.nodes.get(&0).unwrap()[c]; + total += _c.total[0]; + } + total + } + pub fn add_name(&mut self, name: String, name_hash: u64) { + if self.names_map.contains_key(&name_hash) { + return; + } + self.names.push(name); + self.names_map.insert(name_hash, self.names.len() - 1); + } +} + +fn find_node(id: u64, nodes: &Vec>) -> i32 { let mut n: i32 = -1; for c in 0..nodes.len() { let _c = &nodes[c]; @@ -109,19 +148,25 @@ impl MergeTotalsProcessor { fn merge_totals( &self, - node: &mut TreeNodeV2, + node: Arc, _max_self: &Vec, sample: &Sample, merge_self: bool, - ) -> Vec { + ) -> (TreeNodeV2, Vec) { let mut max_self = _max_self.clone(); + let mut res: TreeNodeV2 = TreeNodeV2 { + fn_id: node.fn_id, + node_id: node.node_id, + slf: vec![0; node.slf.len()], + total: vec![0; node.slf.len()], + }; for i in 0..self.from_idx.len() { if self.from_idx[i] == -1 { continue; } - node.total[i] += sample.value[self.from_idx[i] as usize]; + res.total[i] += sample.value[self.from_idx[i] as usize]; if merge_self { - node.slf[i] += sample.value[self.from_idx[i] as usize]; + res.slf[i] += sample.value[self.from_idx[i] as usize]; for i in 0..max_self.len() { if max_self[i] < node.slf[i] { max_self[i] = node.slf[i]; @@ -129,7 +174,7 @@ impl MergeTotalsProcessor { } } } - max_self + (res, max_self) } } @@ -164,29 +209,30 @@ fn merge(tree: &mut Tree, p: &Profile) { if !tree.nodes.contains_key(&parent_id) && tree.nodes_num < 2000000 { tree.nodes.insert(parent_id, Vec::new()); } - let mut fake_children: Vec = Vec::new(); + let mut fake_children: Vec> = Vec::new(); let children = tree.nodes.get_mut(&parent_id).unwrap_or(&mut fake_children); let mut n = find_node(node_id, children); if n == -1 { - children.push(TreeNodeV2 { + children.push(Arc::new(TreeNodeV2 { //parent_id, fn_id: name_hash, node_id, slf: vec![0; tree.sample_types.len()], total: vec![0; tree.sample_types.len()], - }); + })); let idx = children.len().clone() - 1; - let max_self = m.merge_totals( - children.get_mut(idx).unwrap(), + let new_node_and_max_self = m.merge_totals( + children.get(idx).unwrap().clone(), tree.max_self.as_ref(), s, i == 0, ); - tree.max_self = max_self; + children[idx] = Arc::new(new_node_and_max_self.0); + tree.max_self = new_node_and_max_self.1; n = idx as i32; } else if tree.nodes_num < 2000000 { m.merge_totals( - children.get_mut(n as usize).unwrap(), + children.get_mut(n as usize).unwrap().clone(), &tree.max_self, s, i == 0, @@ -214,7 +260,7 @@ fn read_uleb128(bytes: &[u8]) -> (usize, usize) { fn bfs(t: &Tree, res: &mut Vec, sample_type: String) { let mut total: i64 = 0; - let mut root_children: &Vec = &Vec::new(); + let mut root_children: &Vec> = &Vec::new(); if t.nodes.contains_key(&(0u64)) { root_children = t.nodes.get(&(0u64)).unwrap(); } @@ -291,22 +337,22 @@ fn bfs(t: &Tree, res: &mut Vec, sample_type: String) { } lazy_static! { - static ref CTX: Mutex> = Mutex::new(HashMap::new()); + static ref CTX: Mutex>> = Mutex::new(HashMap::new()); } -fn upsert_tree(ctx: &mut HashMap, id: u32, sample_types: Vec) { +fn upsert_tree(ctx: &mut HashMap>, id: u32, sample_types: Vec) { if !ctx.contains_key(&id) { let _len = sample_types.len().clone(); ctx.insert( id, - Tree { + Mutex::new(Tree { names: vec!["total".to_string(), "n/a".to_string()], names_map: HashMap::new(), nodes: HashMap::new(), sample_types, max_self: vec![0; _len], nodes_num: 1, - }, + }), ); } } @@ -413,18 +459,11 @@ fn merge_trie(tree: &mut Tree, bytes: &[u8], samples_type: &String) { n = find_node(node_id, tree.nodes.get(&parent_id).unwrap()); } if n != -1 { - tree.nodes - .get_mut(&parent_id) - .unwrap() - .get_mut(n as usize) - .unwrap() - .total[sample_type_index] += total[sample_type_index]; - tree.nodes - .get_mut(&parent_id) - .unwrap() - .get_mut(n as usize) - .unwrap() - .slf[sample_type_index] += slf[sample_type_index]; + let mut __node = tree.nodes.get_mut(&parent_id).unwrap().get_mut(n as usize).unwrap().clone(); + let mut _node = __node.as_ref().clone(); + _node.total[sample_type_index] += total[sample_type_index]; + _node.slf[sample_type_index] += slf[sample_type_index]; + tree.nodes.get_mut(&parent_id).unwrap()[n as usize] = Arc::new(_node); } if tree.nodes_num >= 2000000 { return; @@ -432,13 +471,13 @@ fn merge_trie(tree: &mut Tree, bytes: &[u8], samples_type: &String) { if !tree.nodes.contains_key(&parent_id) { tree.nodes.insert(parent_id, Vec::new()); } - tree.nodes.get_mut(&parent_id).unwrap().push(TreeNodeV2 { + tree.nodes.get_mut(&parent_id).unwrap().push(Arc::new(TreeNodeV2 { fn_id, //parent_id, node_id, slf, total, - }); + })); tree.nodes_num += 1; } } @@ -551,12 +590,25 @@ fn merge_trie(tree: &mut Tree, bytes: &[u8], samples_type: &String) { inject_functions(prof, tree, 0, vec![], type_idx); }*/ +fn assert_positive(t: &Tree) -> bool{ + for n in t.nodes.keys() { + for _n in 0..t.nodes.get(&n).unwrap().len() { + for __n in 0..t.nodes.get(&n).unwrap()[_n].slf.len() { + if t.nodes.get(&n).unwrap()[_n].slf[__n] < 0 { + return false; + } + } + } + } + true +} + #[wasm_bindgen] pub fn merge_prof(id: u32, bytes: &[u8], sample_type: String) { let p = panic::catch_unwind(|| { let mut ctx = CTX.lock().unwrap(); upsert_tree(&mut ctx, id, vec![sample_type]); - let mut tree = ctx.get_mut(&id).unwrap(); + let mut tree = ctx.get_mut(&id).unwrap().lock().unwrap(); let prof = Profile::decode(bytes).unwrap(); merge(&mut tree, &prof); }); @@ -571,7 +623,7 @@ pub fn merge_tree(id: u32, bytes: &[u8], sample_type: String) { let result = panic::catch_unwind(|| { let mut ctx = CTX.lock().unwrap(); upsert_tree(&mut ctx, id, vec![sample_type.clone()]); - let mut tree = ctx.get_mut(&id).unwrap(); + let mut tree = ctx.get_mut(&id).unwrap().lock().unwrap(); merge_trie(&mut tree, bytes, &sample_type); 0 }); @@ -581,25 +633,275 @@ pub fn merge_tree(id: u32, bytes: &[u8], sample_type: String) { } } +#[wasm_bindgen] +pub fn diff_tree(id1: u32, id2: u32, sample_type: String) -> Vec { + let mut ctx = CTX.lock().unwrap(); + let _ctx = &mut ctx; + upsert_tree(_ctx, id1, vec![sample_type.clone()]); + upsert_tree(_ctx, id2, vec![sample_type.clone()]); + let mut t1 = _ctx.get(&id1).unwrap().lock().unwrap(); + let mut t2 = _ctx.get(&id2).unwrap().lock().unwrap(); + let mut is_positive = assert_positive(&t1); + if !is_positive { + panic!("Tree 1 is not positive"); + } + is_positive = assert_positive(&t2); + if!is_positive { + panic!("Tree 2 is not positive"); + } + + + for n in t1.names_map.keys() { + if !t2.names_map.contains_key(&n) { + t2.names.push(t1.names[*t1.names_map.get(&n).unwrap()].clone()); + let idx = t2.names.len() - 1; + t2.names_map.insert(*n, idx); + } + } + for n in t2.names_map.keys() { + if !t1.names_map.contains_key(&n) { + let idx = t2.names_map.get(&n).unwrap().clone(); + t1.names.push(t2.names[idx].clone()); + let idx2 = t1.names.len() - 1; + t1.names_map.insert(*n, idx2); + } + } + + let keys = t1.nodes.keys().map(|x| (*x).clone()).collect::>(); + for n in keys { + if !t2.nodes.contains_key(&n) { + t2.nodes.insert(n, vec![]); + } + let lnodes = t1.nodes.get_mut(&n).unwrap(); + let rnodes = t2.nodes.get_mut(&n).unwrap(); + lnodes.sort_by(|x, y| + if x.node_id < y.node_id { Ordering::Less } else { Ordering::Greater }); + rnodes.sort_by(|x, y| + if x.node_id < y.node_id { Ordering::Less } else { Ordering::Greater }); + let mut i = 0; + let mut j = 0; + let mut new_t1_nodes: Vec> = vec![]; + let mut new_t2_nodes: Vec> = vec![]; + let t1_nodes = t1.nodes.get(&n).unwrap(); + let t2_nodes = t2.nodes.get(&n).unwrap(); + while i < t1_nodes.len() && j < t2_nodes.len() { + if n == 0 { + println!("{:?}:{:?} - {:?}:{:?}", + t1_nodes[i].node_id, + t1.names[*t1.names_map.get(&t1_nodes[i].fn_id).unwrap() as usize], + t2_nodes[j].node_id, + t2.names[*t2.names_map.get(&t2_nodes[j].fn_id).unwrap() as usize] + ) + } + + if t1_nodes[i].node_id == t2_nodes[j].node_id { + new_t1_nodes.push(t1_nodes[i].clone()); + new_t2_nodes.push(t2_nodes[j].clone()); + i += 1; + j += 1; + continue; + } + if t1_nodes[i].node_id < t2_nodes[j].node_id { + new_t1_nodes.push(t1_nodes[i].clone()); + new_t2_nodes.push(Arc::new(TreeNodeV2{ + node_id: t1_nodes[i].node_id, + fn_id: t1_nodes[i].fn_id, + slf: vec![0], + total: vec![0], + })); + i += 1; + } else { + new_t2_nodes.push(t2_nodes[j].clone()); + new_t1_nodes.push(Arc::new(TreeNodeV2{ + node_id: t2_nodes[j].node_id, + fn_id: t2_nodes[j].fn_id, + slf: vec![0], + total: vec![0], + })); + j += 1; + } + } + while i < t1_nodes.len() { + new_t1_nodes.push(t1_nodes[i].clone()); + new_t2_nodes.push(Arc::new(TreeNodeV2{ + node_id: t1_nodes[i].node_id, + fn_id: t1_nodes[i].fn_id, + slf: vec![0], + total: vec![0], + })); + i += 1; + } + while j < t2_nodes.len() { + new_t2_nodes.push(t2_nodes[j].clone()); + new_t1_nodes.push(Arc::new(TreeNodeV2{ + node_id: t2_nodes[j].node_id, + fn_id: t2_nodes[j].fn_id, + slf: vec![0], + total: vec![0], + })); + j+=1; + } + t1.nodes.insert(n, new_t1_nodes); + t2.nodes.insert(n, new_t2_nodes); + } + + for n in t2.nodes.keys().clone() { + if!t1.nodes.contains_key(&n) { + let mut new_t1_nodes: Vec> = vec![]; + for _n in t2.nodes.get(&n).unwrap() { + new_t1_nodes.push(Arc::new(TreeNodeV2{ + node_id: _n.node_id, + fn_id: _n.fn_id, + slf: vec![0], + total: vec![0], + })) + } + t1.nodes.insert(*n, new_t1_nodes); + } + } + + let total_left = t1.total(); + let total_right = t2.total(); + let mut min_val = 0 as i64; + let tn = Arc::new(TreeNodeV2{ + fn_id: 0, + node_id: 0, + slf: vec![0], + total: vec![total_left], + }); + let mut left_nodes = vec![tn]; + let tn2 = Arc::new(TreeNodeV2{ + fn_id: 0, + node_id: 0, + slf: vec![0], + total: vec![total_right], + }); + let mut right_nodes = vec![tn2]; + + let mut x_left_offsets = vec![0 as i64]; + let mut x_right_offsets = vec![0 as i64]; + let mut levels = vec![0 as i64]; + let mut name_location_cache: HashMap = HashMap::new(); + let mut res = FlameGraphDiff::default(); + res.left_ticks = total_left; + res.right_ticks = total_right; + res.total = total_left + total_right; + while left_nodes.len() > 0 { + let left = left_nodes.pop().unwrap(); + let right = right_nodes.pop().unwrap(); + let mut x_left_offset = x_left_offsets.pop().unwrap(); + let mut x_right_offset = x_right_offsets.pop().unwrap(); + let level = levels.pop().unwrap(); + let mut name: String = "total".to_string(); + if left.fn_id != 0 { + name = t1.names[t1.names_map.get(&left.fn_id).unwrap().clone() as usize].clone(); + } + if left.total[0] >= min_val || right.total[0] >= min_val || name == "other" { + let mut i = 0 as i64; + if !name_location_cache.contains_key(&name) { + res.names.push(name.clone().to_string()); + name_location_cache.insert(name, (res.names.len() - 1) as i64); + i = res.names.len() as i64 - 1; + } else { + i = *name_location_cache.get(name.as_str()).unwrap(); + } + if level == res.levels.len() as i64 { + res.levels.push(Level::default()) + } + if res.max_self < left.slf[0] { + res.max_self = left.slf[0]; + } + if res.max_self < right.slf[0] { + res.max_self = right.slf[0]; + } + let mut values = vec![x_left_offset, left.total[0], left.slf[0], + x_right_offset, right.total[0], right.slf[0], i]; + res.levels[level as usize].values.extend(values); + let mut other_left_total = 0 as i64; + let mut other_right_total = 0 as i64; + let mut nodes_len = 0; + if t1.nodes.contains_key(&left.node_id) { + nodes_len = t1.nodes.get(&left.node_id).unwrap().len().clone(); + } + for j in 0..nodes_len { + let _left = t1.nodes.get(&left.node_id).unwrap()[j].clone(); + let _right = t2.nodes.get(&left.node_id).unwrap()[j].clone(); + if _left.total[0] >= min_val || _right.total[0] >= min_val { + levels.insert(0, level + 1); + x_left_offsets.insert(0, x_left_offset); + x_right_offsets.insert(0, x_right_offset); + x_left_offset += _left.total[0].clone() as i64; + x_right_offset += _right.total[0].clone() as i64; + left_nodes.insert(0, _left.clone()); + right_nodes.insert(0, _right.clone()); + } else { + other_left_total += _left.total[0] as i64; + other_right_total += _right.total[0] as i64; + } + if other_left_total > 0 || other_right_total > 0 { + levels.insert(0, level + 1); + t1.add_name("other".to_string(), 1); + x_left_offsets.insert(0, x_left_offset); + left_nodes.insert(0, Arc::new(TreeNodeV2{ + fn_id: 1, + node_id: 1, + slf: vec![other_left_total as i64], + total: vec![other_left_total as i64], + })); + t2.add_name("other".to_string(), 1); + x_right_offsets.insert(0, x_right_offset); + right_nodes.insert(0, Arc::new(TreeNodeV2{ + fn_id: 1, + node_id: 1, + slf: vec![other_right_total as i64], + total: vec![other_right_total as i64], + })); + } + } + } + + } + for i in 0..res.levels.len() { + let mut j = 0; + let mut prev = 0 as i64; + while j < res.levels[i].values.len() { + res.levels[i].values[j] -= prev; + prev += res.levels[i].values[j] + res.levels[i].values[j+1]; + j += 7; + } + prev = 0; + j = 3; + while j < res.levels[i].values.len() { + res.levels[i].values[j] -= prev; + prev += res.levels[i].values[j] + res.levels[i].values[j+1]; + j += 7; + } + } + + res.encode_to_vec() +} + + + #[wasm_bindgen] pub fn export_tree(id: u32, sample_type: String) -> Vec { let p = panic::catch_unwind(|| { let mut ctx = CTX.lock().unwrap(); let mut res = SelectMergeStacktracesResponse::default(); upsert_tree(&mut ctx, id, vec![sample_type.clone()]); - let tree = ctx.get_mut(&id).unwrap(); + let tree = ctx.get_mut(&id).unwrap().lock().unwrap(); let mut fg = FlameGraph::default(); fg.names = tree.names.clone(); fg.max_self = tree.max_self[0 /* TODO */]; fg.total = 0; - let mut root_children: &Vec = &vec![]; + let mut root_children: &Vec> = &vec![]; if tree.nodes.contains_key(&(0u64)) { root_children = tree.nodes.get(&(0u64)).unwrap(); } for n in root_children.iter() { fg.total += n.total[0 /*TODO*/] as i64; } - bfs(tree, &mut fg.levels, sample_type.clone()); + bfs(&tree, &mut fg.levels, sample_type.clone()); res.flamegraph = Some(fg); return res.encode_to_vec(); }); diff --git a/pyroscope/proto/querier.proto b/pyroscope/proto/querier.proto index f5fc7fc5..78ae0da0 100644 --- a/pyroscope/proto/querier.proto +++ b/pyroscope/proto/querier.proto @@ -16,14 +16,19 @@ service QuerierService { rpc Series(SeriesRequest) returns (SeriesResponse) {} // SelectMergeStacktraces returns matching profiles aggregated in a flamegraph format. It will combine samples from within the same callstack, with each element being grouped by its function name. rpc SelectMergeStacktraces(SelectMergeStacktracesRequest) returns (SelectMergeStacktracesResponse) {} - // SelectMergeSpans returns matching profiles aggregated in a flamegraph format. It will combine samples from within the same callstack, with each element being grouped by its function name. + // SelectMergeSpanProfile returns matching profiles aggregated in a flamegraph format. It will combine samples from within the same callstack, with each element being grouped by its function name. rpc SelectMergeSpanProfile(SelectMergeSpanProfileRequest) returns (SelectMergeSpanProfileResponse) {} // SelectMergeProfile returns matching profiles aggregated in pprof format. It will contain all information stored (so including filenames and line number, if ingested). rpc SelectMergeProfile(SelectMergeProfileRequest) returns (google.v1.Profile) {} // SelectSeries returns a time series for the total sum of the requested profiles. rpc SelectSeries(SelectSeriesRequest) returns (SelectSeriesResponse) {} + // Diff returns a diff of two profiles rpc Diff(DiffRequest) returns (DiffResponse) {} + + // GetProfileStats returns profile stats for the current tenant. + rpc GetProfileStats(types.v1.GetProfileStatsRequest) returns (types.v1.GetProfileStatsResponse) {} + rpc AnalyzeQuery(AnalyzeQueryRequest) returns (AnalyzeQueryResponse) {} } message ProfileTypesRequest { @@ -57,26 +62,48 @@ message SeriesResponse { message SelectMergeStacktracesRequest { string profile_typeID = 1; string label_selector = 2; - int64 start = 3; // milliseconds since epoch - int64 end = 4; // milliseconds since epoch - optional int64 max_nodes = 5; // Limit the nodes returned to only show the node with the max_node's biggest total + // Milliseconds since epoch. + int64 start = 3; + // Milliseconds since epoch. + int64 end = 4; + // Limit the nodes returned to only show the node with the max_node's biggest total + optional int64 max_nodes = 5; + // Profile format specifies the format of profile to be returned. + // If not specified, the profile will be returned in flame graph format. + ProfileFormat format = 6; +} + +enum ProfileFormat { + PROFILE_FORMAT_UNSPECIFIED = 0; + PROFILE_FORMAT_FLAMEGRAPH = 1; + PROFILE_FORMAT_TREE = 2; } message SelectMergeStacktracesResponse { FlameGraph flamegraph = 1; + // Pyroscope tree bytes. + bytes tree = 2; } message SelectMergeSpanProfileRequest { string profile_typeID = 1; string label_selector = 2; repeated string span_selector = 3; - int64 start = 4; // milliseconds since epoch - int64 end = 5; // milliseconds since epoch - optional int64 max_nodes = 6; // Limit the nodes returned to only show the node with the max_node's biggest total + // Milliseconds since epoch. + int64 start = 4; + // Milliseconds since epoch. + int64 end = 5; + // Limit the nodes returned to only show the node with the max_node's biggest total + optional int64 max_nodes = 6; + // Profile format specifies the format of profile to be returned. + // If not specified, the profile will be returned in flame graph format. + ProfileFormat format = 7; } message SelectMergeSpanProfileResponse { FlameGraph flamegraph = 1; + // Pyroscope tree bytes. + bytes tree = 2; } message DiffRequest { @@ -112,22 +139,61 @@ message Level { message SelectMergeProfileRequest { string profile_typeID = 1; string label_selector = 2; - int64 start = 3; // milliseconds since epoch - int64 end = 4; // milliseconds since epoch - optional int64 max_nodes = 5; // Limit the nodes returned to only show the node with the max_node's biggest total - optional types.v1.StackTraceSelector stack_trace_selector = 6; // Only return stack traces matching the selector + // Milliseconds since epoch. + int64 start = 3; + // Milliseconds since epoch. + int64 end = 4; + // Limit the nodes returned to only show the node with the max_node's biggest total + optional int64 max_nodes = 5; + // Select stack traces that match the provided selector. + optional types.v1.StackTraceSelector stack_trace_selector = 6; } message SelectSeriesRequest { string profile_typeID = 1; string label_selector = 2; - int64 start = 3; // milliseconds since epoch - int64 end = 4; // milliseconds since epoch + // Milliseconds since epoch. + int64 start = 3; + // Milliseconds since epoch. + int64 end = 4; repeated string group_by = 5; - double step = 6; // Query resolution step width in seconds + double step = 6; + // Query resolution step width in seconds optional types.v1.TimeSeriesAggregationType aggregation = 7; + // Select stack traces that match the provided selector. + optional types.v1.StackTraceSelector stack_trace_selector = 8; } message SelectSeriesResponse { repeated types.v1.Series series = 1; } + +message AnalyzeQueryRequest { + int64 start = 2; + int64 end = 3; + string query = 4; +} + +message AnalyzeQueryResponse { + repeated QueryScope query_scopes = 1; // detailed view of what the query will require + QueryImpact query_impact = 2; // summary of the query impact / performance +} + +message QueryScope { + string component_type = 1; // a descriptive high level name of the component processing one part of the query (e.g., "short term storage") + uint64 component_count = 2; // how many components of this type will process the query (indicator of read-path replication) + + uint64 block_count = 3; + uint64 series_count = 4; + uint64 profile_count = 5; + uint64 sample_count = 6; + uint64 index_bytes = 7; + uint64 profile_bytes = 8; + uint64 symbol_bytes = 9; +} + +message QueryImpact { + uint64 total_bytes_in_time_range = 2; + uint64 total_queried_series = 3; + bool deduplication_needed = 4; +} diff --git a/pyroscope/proto/settings.proto b/pyroscope/proto/settings.proto new file mode 100644 index 00000000..fb7375bf --- /dev/null +++ b/pyroscope/proto/settings.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; + +package settings.v1; + +service SettingsService { + rpc Get(GetSettingsRequest) returns (GetSettingsResponse) {} + rpc Set(SetSettingsRequest) returns (SetSettingsResponse) {} +} + +message GetSettingsRequest {} + +message GetSettingsResponse { + repeated Setting settings = 1; +} + +message SetSettingsRequest { + Setting setting = 1; +} + +message SetSettingsResponse { + Setting setting = 1; +} + +message Setting { + string name = 1; + string value = 2; + int64 modifiedAt = 3; +} diff --git a/pyroscope/proto/types/v1/types.proto b/pyroscope/proto/types/v1/types.proto index 62f8ec94..ced94bf2 100644 --- a/pyroscope/proto/types/v1/types.proto +++ b/pyroscope/proto/types/v1/types.proto @@ -74,11 +74,34 @@ enum TimeSeriesAggregationType { // StackTraceSelector is used for filtering stack traces by locations. message StackTraceSelector { - // Only stack traces that have this stack trace as a prefix will be selected. - // If empty, no stack traces will be selected. Leaf is at stack_trace[0]. - repeated Location stack_trace = 1; + // Stack trace of the call site. Root at call_site[0]. + // Only stack traces having the prefix provided will be selected. + // If empty, the filter is ignored. + repeated Location call_site = 1; + // Stack trace selector for profiles purposed for Go PGO. + // If set, call_site is ignored. + GoPGO go_pgo = 2; } message Location { string name = 1; } + +message GoPGO { + // Specifies the number of leaf locations to keep. + uint32 keep_locations = 1; + // Aggregate callees causes the leaf location line number to be ignored, + // thus aggregating all callee samples (but not callers). + bool aggregate_callees = 2; +} + +message GetProfileStatsRequest {} + +message GetProfileStatsResponse { + // Whether we received any data at any time in the past. + bool data_ingested = 1; + // Milliseconds since epoch. + int64 oldest_profile_time = 2; + // Milliseconds since epoch. + int64 newest_profile_time = 3; +} diff --git a/pyroscope/pyroscope.js b/pyroscope/pyroscope.js index 041382a3..f979e10a 100644 --- a/pyroscope/pyroscope.js +++ b/pyroscope/pyroscope.js @@ -2,34 +2,26 @@ const messages = require('./querier_pb') const types = require('./types/v1/types_pb') const services = require('./querier_grpc_pb') const clickhouse = require('../lib/db/clickhouse') -const { DATABASE_NAME, checkVersion } = require('../lib/utils') +const { DATABASE_NAME } = require('../lib/utils') const Sql = require('@cloki/clickhouse-sql') -const compiler = require('../parser/bnf') -const { readULeb32 } = require('./pprof') const pprofBin = require('./pprof-bin/pkg/pprof_bin') const { QrynBadRequest } = require('../lib/handlers/errors') const { clusterName } = require('../common') const logger = require('../lib/logger') - -const HISTORY_TIMESPAN = 1000 * 60 * 60 * 24 * 7 - -/** - * - * @param typeId {string} - */ -const parseTypeId = (typeId) => { - const typeParts = typeId.match(/^([^:]+):([^:]+):([^:]+):([^:]+):([^:]+)$/) - if (!typeParts) { - throw new QrynBadRequest('invalid type id') - } - return { - type: typeParts[1], - sampleType: typeParts[2], - sampleUnit: typeParts[3], - periodType: typeParts[4], - periodUnit: typeParts[5] - } -} +const jsonParsers = require('./json_parsers') +const renderDiff = require('./render_diff') +const { + parser, + wrapResponse, + parseTypeId, + serviceNameSelectorQuery, + labelSelectorQuery, + HISTORY_TIMESPAN +} = require('./shared') +const settings = require('./settings') +const { mergeStackTraces } = require('./merge_stack_traces') +const { selectSeriesImpl } = require('./select_series') +const render = require('./render') const profileTypesHandler = async (req, res) => { const dist = clusterName ? '_dist' : '' @@ -57,7 +49,7 @@ WHERE date >= toDate(FROM_UNIXTIME(${Math.floor(fromTimeSec)})) AND date <= toDa pt.setPeriodUnit(periodUnit) return pt })) - return res.code(200).send(Buffer.from(_res.serializeBinary())) + return _res } const labelNames = async (req, res) => { @@ -74,7 +66,7 @@ WHERE date >= toDate(FROM_UNIXTIME(${Math.floor(fromTimeSec)})) AND date <= toDa null, DATABASE_NAME()) const resp = new types.LabelNamesResponse() resp.setNamesList(labelNames.data.data.map(label => label.key)) - return res.code(200).send(Buffer.from(resp.serializeBinary())) + return resp } const labelValues = async (req, res) => { @@ -98,125 +90,14 @@ date >= toDate(FROM_UNIXTIME(${Math.floor(fromTimeSec)})) AND date <= toDate(FROM_UNIXTIME(${Math.floor(toTimeSec)})) FORMAT JSON`, null, DATABASE_NAME()) const resp = new types.LabelValuesResponse() resp.setNamesList(labelValues.data.data.map(label => label.val)) - return res.code(200).send(Buffer.from(resp.serializeBinary())) -} - -const parser = (MsgClass) => { - return async (req, payload) => { - const _body = [] - payload.on('data', data => { - _body.push(data)// += data.toString() - }) - if (payload.isPaused && payload.isPaused()) { - payload.resume() - } - await new Promise(resolve => { - payload.on('end', resolve) - payload.on('close', resolve) - }) - const body = Buffer.concat(_body) - if (body.length === 0) { - return null - } - req._rawBody = body - return MsgClass.deserializeBinary(body) - } -} - -let ctxIdx = 0 - -/** - * - * @param {Sql.Select} query - * @param {string} labelSelector - */ -const labelSelectorQuery = (query, labelSelector) => { - if (!labelSelector || !labelSelector.length || labelSelector === '{}') { - return query - } - const labelSelectorScript = compiler.ParseScript(labelSelector).rootToken - const labelsConds = [] - for (const rule of labelSelectorScript.Children('log_stream_selector_rule')) { - const val = JSON.parse(rule.Child('quoted_str').value) - let valRul = null - switch (rule.Child('operator').value) { - case '=': - valRul = Sql.Eq(new Sql.Raw('val'), Sql.val(val)) - break - case '!=': - valRul = Sql.Ne(new Sql.Raw('val'), Sql.val(val)) - break - case '=~': - valRul = Sql.Eq(new Sql.Raw(`match(val, ${Sql.quoteVal(val)})`), 1) - break - case '!~': - valRul = Sql.Ne(new Sql.Raw(`match(val, ${Sql.quoteVal(val)})`), 1) - } - const labelSubCond = Sql.And( - Sql.Eq('key', Sql.val(rule.Child('label').value)), - valRul - ) - labelsConds.push(labelSubCond) - } - query.where(Sql.Or(...labelsConds)) - query.groupBy(new Sql.Raw('fingerprint')) - query.having(Sql.Eq( - new Sql.Raw(`groupBitOr(${labelsConds.map((cond, i) => { - return `bitShiftLeft(toUInt64(${cond}), ${i})` - }).join('+')})`), - new Sql.Raw(`bitShiftLeft(toUInt64(1), ${labelsConds.length})-1`) - )) -} - -const serviceNameSelectorQuery = (labelSelector) => { - const empty = Sql.Eq(new Sql.Raw('1'), new Sql.Raw('1')) - if (!labelSelector || !labelSelector.length || labelSelector === '{}') { - return empty - } - const labelSelectorScript = compiler.ParseScript(labelSelector).rootToken - let conds = null - for (const rule of labelSelectorScript.Children('log_stream_selector_rule')) { - const label = rule.Child('label').value - if (label !== 'service_name') { - continue - } - const val = JSON.parse(rule.Child('quoted_str').value) - let valRul = null - switch (rule.Child('operator').value) { - case '=': - valRul = Sql.Eq(new Sql.Raw('service_name'), Sql.val(val)) - break - case '!=': - valRul = Sql.Ne(new Sql.Raw('service_name'), Sql.val(val)) - break - case '=~': - valRul = Sql.Eq(new Sql.Raw(`match(service_name, ${Sql.quoteVal(val)})`), 1) - break - case '!~': - valRul = Sql.Ne(new Sql.Raw(`match(service_name, ${Sql.quoteVal(val)})`), 1) - } - conds = valRul - } - return conds || empty + return resp } const selectMergeStacktraces = async (req, res) => { return await selectMergeStacktracesV2(req, res) } -const sqlWithReference = (ref) => { - const res = new Sql.WithReference(ref) - res.toString = function () { - if (this.ref.inline) { - return `(${this.ref.query.toString()}) as ${this.ref.alias}` - } - return this.ref.alias - } - return res -} - const selectMergeStacktracesV2 = async (req, res) => { - const dist = clusterName ? '_dist' : '' const typeRegex = parseTypeId(req.body.getProfileTypeid()) const sel = req.body.getLabelSelector() const fromTimeSec = req.body && req.body.getStart() @@ -225,273 +106,18 @@ const selectMergeStacktracesV2 = async (req, res) => { const toTimeSec = req.body && req.body.getEnd() ? Math.floor(parseInt(req.body.getEnd()) / 1000) : Math.floor(Date.now() / 1000) - const v2 = checkVersion('profiles_v2', (fromTimeSec - 3600) * 1000) - const serviceNameSelector = serviceNameSelectorQuery(sel) - const typeIdSelector = Sql.Eq( - 'type_id', - Sql.val(`${typeRegex.type}:${typeRegex.periodType}:${typeRegex.periodUnit}`) - ) - const idxSelect = (new Sql.Select()) - .select('fingerprint') - .from(`${DATABASE_NAME()}.profiles_series_gin`) - .where( - Sql.And( - Sql.Eq(new Sql.Raw(`has(sample_types_units, (${Sql.quoteVal(typeRegex.sampleType)},${Sql.quoteVal(typeRegex.sampleUnit)}))`), 1), - typeIdSelector, - Sql.Gte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(fromTimeSec)}))`)), - Sql.Lte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(toTimeSec)}))`)), - serviceNameSelector - ) - ).groupBy('fingerprint') - labelSelectorQuery(idxSelect, sel) - const withIdxSelect = new Sql.With('idx', idxSelect, !!clusterName) - const rawReq = (new Sql.Select()).with(withIdxSelect) - .select([ - new Sql.Raw(`arrayMap(x -> (x.1, x.2, x.3, (arrayFirst(y -> y.1 == ${Sql.quoteVal(`${typeRegex.sampleType}:${typeRegex.sampleUnit}`)}, x.4) as af).2, af.3), tree)`), - 'tree' - ], 'functions') - .from(`${DATABASE_NAME()}.profiles${dist}`) - .where( - Sql.And( - Sql.Gte('timestamp_ns', new Sql.Raw(Math.floor(fromTimeSec) + '000000000')), - Sql.Lte('timestamp_ns', new Sql.Raw(Math.floor(toTimeSec) + '000000000')), - new Sql.In('fingerprint', 'IN', sqlWithReference(withIdxSelect)), - typeIdSelector, - serviceNameSelector - )) - if (process.env.ADVANCED_PROFILES_MERGE_LIMIT) { - rawReq.orderBy(['timestamp_ns', 'desc']).limit(parseInt(process.env.ADVANCED_PROFILES_MERGE_LIMIT)) - } - const withRawReq = new Sql.With('raw', rawReq, !!clusterName) - const joinedReq = (new Sql.Select()).with(withRawReq).select([ - new Sql.Raw('(raw.tree.1, raw.tree.2, raw.tree.3, sum(raw.tree.4), sum(raw.tree.5))'), - 'tree2' - ]).from(sqlWithReference(withRawReq)) - .join('raw.tree', 'array') - .groupBy(new Sql.Raw('raw.tree.1'), new Sql.Raw('raw.tree.2'), new Sql.Raw('raw.tree.3')) - .orderBy(new Sql.Raw('raw.tree.1')).limit(2000000) - const withJoinedReq = new Sql.With('joined', joinedReq, !!clusterName) - const joinedAggregatedReq = (new Sql.Select()).select( - [new Sql.Raw('groupArray(tree2)'), 'tree']).from(sqlWithReference(withJoinedReq)) - const functionsReq = (new Sql.Select()).select( - [new Sql.Raw('groupUniqArray(raw.functions)'), 'functions2'] - ).from(sqlWithReference(withRawReq)).join('raw.functions', 'array') - - let brackLegacy = (new Sql.Select()).select( - [new Sql.Raw('[]::Array(String)'), 'legacy'] - ) - let withLegacy = null - if (!v2) { - const legacy = (new Sql.Select()).with(withIdxSelect) - .select('payload') - .from(`${DATABASE_NAME()}.profiles${dist}`) - .where( - Sql.And( - Sql.Gte('timestamp_ns', new Sql.Raw(Math.floor(fromTimeSec) + '000000000')), - Sql.Lte('timestamp_ns', new Sql.Raw(Math.floor(toTimeSec) + '000000000')), - new Sql.In('fingerprint', 'IN', sqlWithReference(withIdxSelect)), - Sql.Eq(new Sql.Raw('empty(tree)'), 1), - typeIdSelector, - serviceNameSelector - )) - if (process.env.ADVANCED_PROFILES_MERGE_LIMIT) { - legacy.orderBy(['timestamp_ns', 'desc']).limit(parseInt(process.env.ADVANCED_PROFILES_MERGE_LIMIT)) - } - withLegacy = new Sql.With('legacy', legacy, !!clusterName) - brackLegacy = (new Sql.Select()) - .select([new Sql.Raw('groupArray(payload)'), 'payloads']) - .from(sqlWithReference(withLegacy)) - } - brackLegacy = new Sql.Raw(`(${brackLegacy.toString()})`) - const brack1 = new Sql.Raw(`(${joinedAggregatedReq.toString()})`) - const brack2 = new Sql.Raw(`(${functionsReq.toString()})`) - - const sqlReq = (new Sql.Select()) - .select( - [brackLegacy, 'legacy'], - [brack2, 'functions'], - [brack1, 'tree'] - ) - if (v2) { - sqlReq.with(withJoinedReq, withRawReq) - } else { - sqlReq.with(withJoinedReq, withRawReq, withLegacy) - } - - let start = Date.now() - const profiles = await clickhouse.rawRequest(sqlReq.toString() + ' FORMAT RowBinary', - null, - DATABASE_NAME(), - { - responseType: 'arraybuffer' - }) - const binData = Uint8Array.from(profiles.data) - req.log.debug(`selectMergeStacktraces: profiles downloaded: ${binData.length / 1025}kB in ${Date.now() - start}ms`) - require('./pprof-bin/pkg/pprof_bin').init_panic_hook() - const _ctxIdx = ++ctxIdx - const [legacyLen, shift] = readULeb32(binData, 0) - let ofs = shift - try { - let mergePprofLat = BigInt(0) - for (let i = 0; i < legacyLen; i++) { - const [profLen, shift] = readULeb32(binData, ofs) - ofs += shift - start = process.hrtime?.bigint ? process.hrtime.bigint() : BigInt(0) - pprofBin.merge_prof(_ctxIdx, - Uint8Array.from(profiles.data.slice(ofs, ofs + profLen)), - `${typeRegex.sampleType}:${typeRegex.sampleUnit}`) - mergePprofLat += (process.hrtime?.bigint ? process.hrtime.bigint() : BigInt(0)) - start - ofs += profLen - } - start = process.hrtime?.bigint ? process.hrtime.bigint() : BigInt(0) - pprofBin.merge_tree(_ctxIdx, Uint8Array.from(profiles.data.slice(ofs)), - typeRegex.sampleType + ':' + typeRegex.sampleUnit) - const mergeTreeLat = (process.hrtime?.bigint ? process.hrtime.bigint() : BigInt(0)) - start - start = process.hrtime?.bigint ? process.hrtime.bigint() : BigInt(0) - const resp = pprofBin.export_tree(_ctxIdx, typeRegex.sampleType + ':' + typeRegex.sampleUnit) - const exportTreeLat = (process.hrtime?.bigint ? process.hrtime.bigint() : BigInt(0)) - start - req.log.debug(`merge_pprof: ${mergePprofLat / BigInt(1000000)}ms`) - req.log.debug(`merge_tree: ${mergeTreeLat / BigInt(1000000)}ms`) - req.log.debug(`export_tree: ${exportTreeLat / BigInt(1000000)}ms`) - return res.code(200).send(Buffer.from(resp)) - } finally { - try { pprofBin.drop_tree(_ctxIdx) } catch (e) {} - } + const resBuffer = await mergeStackTraces(typeRegex, sel, fromTimeSec, toTimeSec, req.log) + return res.code(200).send(resBuffer) } const selectSeries = async (req, res) => { - const _req = req.body const fromTimeSec = Math.floor(req.getStart && req.getStart() ? parseInt(req.getStart()) / 1000 : Date.now() / 1000 - HISTORY_TIMESPAN) const toTimeSec = Math.floor(req.getEnd && req.getEnd() ? parseInt(req.getEnd()) / 1000 : Date.now() / 1000) - let typeID = _req.getProfileTypeid && _req.getProfileTypeid() - if (!typeID) { - throw new QrynBadRequest('No type provided') - } - typeID = parseTypeId(typeID) - if (!typeID) { - throw new QrynBadRequest('Invalid type provided') - } - const dist = clusterName ? '_dist' : '' - const sampleTypeId = typeID.sampleType + ':' + typeID.sampleUnit - const labelSelector = _req.getLabelSelector && _req.getLabelSelector() - let groupBy = _req.getGroupByList && _req.getGroupByList() - groupBy = groupBy && groupBy.length ? groupBy : null - const step = _req.getStep && parseInt(_req.getStep()) - if (!step || isNaN(step)) { - throw new QrynBadRequest('No step provided') - } - const aggregation = _req.getAggregation && _req.getAggregation() - - const typeIdSelector = Sql.Eq( - 'type_id', - Sql.val(`${typeID.type}:${typeID.periodType}:${typeID.periodUnit}`)) - const serviceNameSelector = serviceNameSelectorQuery(labelSelector) - - const idxReq = (new Sql.Select()) - .select(new Sql.Raw('fingerprint')) - .from(`${DATABASE_NAME()}.profiles_series_gin`) - .where( - Sql.And( - typeIdSelector, - serviceNameSelector, - Sql.Gte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(fromTimeSec)}))`)), - Sql.Lte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(toTimeSec)}))`)), - Sql.Eq(new Sql.Raw( - `has(sample_types_units, (${Sql.quoteVal(typeID.sampleType)}, ${Sql.quoteVal(typeID.sampleUnit)}))`), - 1) - ) - ) - labelSelectorQuery(idxReq, labelSelector) - - const withIdxReq = (new Sql.With('idx', idxReq, !!clusterName)) - - let tagsReq = 'arraySort(p.tags)' - if (groupBy) { - tagsReq = `arraySort(arrayFilter(x -> x.1 in (${groupBy.map(g => Sql.quoteVal(g)).join(',')}), p.tags))` - } - - const labelsReq = (new Sql.Select()).with(withIdxReq).select( - 'fingerprint', - [new Sql.Raw(tagsReq), 'tags'], - [groupBy ? new Sql.Raw('cityHash64(tags)') : 'fingerprint', 'new_fingerprint'] - ).distinct(true).from([`${DATABASE_NAME()}.profiles_series`, 'p']) - .where(Sql.And( - new Sql.In('fingerprint', 'IN', new Sql.WithReference(withIdxReq)), - Sql.Gte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(fromTimeSec)}))`)), - Sql.Lte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(toTimeSec)}))`)), - typeIdSelector, - serviceNameSelector - )) - - const withLabelsReq = new Sql.With('labels', labelsReq, !!clusterName) - - let valueCol = new Sql.Raw( - `sum(toFloat64(arrayFirst(x -> x.1 == ${Sql.quoteVal(sampleTypeId)}, p.values_agg).2))`) - if (aggregation === types.TimeSeriesAggregationType.TIME_SERIES_AGGREGATION_TYPE_AVERAGE) { - valueCol = new Sql.Raw( - `sum(toFloat64(arrayFirst(x -> x.1 == ${Sql.quoteVal(sampleTypeId)}).2, p.values_agg)) / ` + - `sum(toFloat64(arrayFirst(x -> x.1 == ${Sql.quoteVal(sampleTypeId)}).3, p.values_agg))` - ) - } - - const mainReq = (new Sql.Select()).with(withIdxReq, withLabelsReq).select( - [new Sql.Raw(`intDiv(p.timestamp_ns, 1000000000 * ${step}) * ${step} * 1000`), 'timestamp_ms'], - [new Sql.Raw('labels.new_fingerprint'), 'fingerprint'], - [new Sql.Raw('min(labels.tags)'), 'labels'], - [valueCol, 'value'] - ).from([`${DATABASE_NAME()}.profiles${dist}`, 'p']).join( - [new Sql.WithReference(withLabelsReq), 'labels'], - 'ANY LEFT', - Sql.Eq(new Sql.Raw('p.fingerprint'), new Sql.Raw('labels.fingerprint')) - ).where( - Sql.And( - new Sql.In('p.fingerprint', 'IN', new Sql.WithReference(withIdxReq)), - Sql.Gte('p.timestamp_ns', new Sql.Raw(`${fromTimeSec}000000000`)), - Sql.Lt('p.timestamp_ns', new Sql.Raw(`${toTimeSec}000000000`)), - typeIdSelector, - serviceNameSelector - ) - ).groupBy('timestamp_ns', 'fingerprint') - .orderBy(['fingerprint', 'ASC'], ['timestamp_ns', 'ASC']) - const strMainReq = mainReq.toString() - const chRes = await clickhouse - .rawRequest(strMainReq + ' FORMAT JSON', null, DATABASE_NAME()) - - let lastFingerprint = null - const seriesList = [] - let lastSeries = null - let lastPoints = [] - for (let i = 0; i < chRes.data.data.length; i++) { - const e = chRes.data.data[i] - if (lastFingerprint !== e.fingerprint) { - lastFingerprint = e.fingerprint - lastSeries && lastSeries.setPointsList(lastPoints) - lastSeries && seriesList.push(lastSeries) - lastPoints = [] - lastSeries = new types.Series() - lastSeries.setLabelsList(e.labels.map(l => { - const lp = new types.LabelPair() - lp.setName(l[0]) - lp.setValue(l[1]) - return lp - })) - } - - const p = new types.Point() - p.setValue(e.value) - p.setTimestamp(e.timestamp_ms) - lastPoints.push(p) - } - lastSeries && lastSeries.setPointsList(lastPoints) - lastSeries && seriesList.push(lastSeries) - - const resp = new messages.SelectSeriesResponse() - resp.setSeriesList(seriesList) - return res.code(200).send(Buffer.from(resp.serializeBinary())) + return selectSeriesImpl(fromTimeSec, toTimeSec, req.body) } const selectMergeProfile = async (req, res) => { @@ -607,13 +233,30 @@ const series = async (req, res) => { ) promises.push(clickhouse.rawRequest(labelsReq.toString() + ' FORMAT JSON', null, DATABASE_NAME())) } + if ((_req.getMatchersList() || []).length === 0) { + const labelsReq = (new Sql.Select()) + .select( + ['tags', 'tags'], + ['type_id', 'type_id'], + ['sample_types_units', '_sample_types_units']) + .from([`${DATABASE_NAME()}.profiles_series${dist}`, 'p']) + .join('p.sample_types_units', 'array') + .where( + Sql.And( + Sql.Gte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(fromTimeSec)}))`)), + Sql.Lte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(toTimeSec)}))`)) + ) + ) + promises.push(clickhouse.rawRequest(labelsReq.toString() + ' FORMAT JSON', null, DATABASE_NAME())) + } const resp = await Promise.all(promises) const response = new messages.SeriesResponse() const labelsSet = [] + const filterLabelNames = _req.getLabelNamesList() || null resp.forEach(_res => { for (const row of _res.data.data) { const labels = new types.Labels() - const _labels = [] + let _labels = [] for (const tag of row.tags) { const pair = new types.LabelPair() pair.setName(tag[0]) @@ -636,12 +279,17 @@ const series = async (req, res) => { _pair('__profile_type__', `${typeId[0]}:${row._sample_types_units[0]}:${row._sample_types_units[1]}:${typeId[1]}:${typeId[2]}`) ) - labels.setLabelsList(_labels) - labelsSet.push(labels) + if (filterLabelNames && filterLabelNames.length) { + _labels = _labels.filter((l) => filterLabelNames.includes(l.getName())) + } + if (_labels.length > 0) { + labels.setLabelsList(_labels) + labelsSet.push(labels) + } } }) response.setLabelsSetList(labelsSet) - return res.code(200).send(Buffer.from(response.serializeBinary())) + return response } /** @@ -722,6 +370,53 @@ const specialMatchersQuery = (matchers) => { return new Sql.And(...clauses) } +const getProfileStats = async (req, res) => { + const sql = ` +with non_empty as (select any(1) as non_empty from profiles limit 1), + min_date as (select min(date) as min_date, max(date) as max_date from profiles_series), + min_time as ( + select intDiv(min(timestamp_ns), 1000000) as min_time, + intDiv(max(timestamp_ns), 1000000) as max_time + from profiles + where timestamp_ns < toUnixTimestamp((select any (min_date) from min_date) + INTERVAL '1 day') * 1000000000 OR + timestamp_ns >= toUnixTimestamp((select any(max_date) from min_date)) * 1000000000 + ) +select + (select any(non_empty) from non_empty) as non_empty, + (select any(min_time) from min_time) as min_time, + (select any(max_time) from min_time) as max_time +` + const sqlRes = await clickhouse.rawRequest(sql + ' FORMAT JSON', null, DATABASE_NAME()) + const response = new types.GetProfileStatsResponse() + response.setDataIngested(!!sqlRes.data.data[0].non_empty) + response.setOldestProfileTime(sqlRes.data.data[0].min_time) + response.setNewestProfileTime(sqlRes.data.data[0].max_time) + return response +} + +const analyzeQuery = async (req, res) => { + const query = req.body.getQuery() + const fromTimeSec = Math.floor(req.getStart && req.getStart() + ? parseInt(req.getStart()) / 1000 + : Date.now() / 1000 - HISTORY_TIMESPAN) + const toTimeSec = Math.floor(req.getEnd && req.getEnd() + ? parseInt(req.getEnd()) / 1000 + : Date.now() / 1000) + console.log(query) + + const scope = new messages.QueryScope() + scope.setComponentType('store') + scope.setComponentCount(1) + const impact = new messages.QueryImpact() + impact.setTotalBytesInTimeRange(10 * 1024 * 1024) + impact.setTotalQueriedSeries(15) + impact.setDeduplicationNeeded(false) + const response = new messages.AnalyzeQueryResponse() + response.setQueryScopesList([scope]) + response.setQueryImpact(impact) + return response +} + module.exports.init = (fastify) => { const fns = { profileTypes: profileTypesHandler, @@ -730,13 +425,25 @@ module.exports.init = (fastify) => { selectMergeStacktraces: selectMergeStacktraces, selectSeries: selectSeries, selectMergeProfile: selectMergeProfile, - series: series + series: series, + getProfileStats: getProfileStats, + analyzeQuery: analyzeQuery + } + const parsers = { + series: jsonParsers.series, + getProfileStats: jsonParsers.getProfileStats, + labelNames: jsonParsers.labelNames, + analyzeQuery: jsonParsers.analyzeQuery } for (const name of Object.keys(fns)) { fastify.post(services.QuerierServiceService[name].path, (req, res) => { - return fns[name](req, res) + return wrapResponse(fns[name])(req, res) }, { + 'application/json': parsers[name], '*': parser(services.QuerierServiceService[name].requestType) }) } + settings.init(fastify) + render.init(fastify) + renderDiff.init(fastify) } diff --git a/pyroscope/querier_grpc_pb.js b/pyroscope/querier_grpc_pb.js index f4660485..4f473c34 100644 --- a/pyroscope/querier_grpc_pb.js +++ b/pyroscope/querier_grpc_pb.js @@ -17,6 +17,28 @@ function deserialize_google_v1_Profile(buffer_arg) { return google_v1_profile_pb.Profile.deserializeBinary(new Uint8Array(buffer_arg)); } +function serialize_querier_v1_AnalyzeQueryRequest(arg) { + if (!(arg instanceof querier_pb.AnalyzeQueryRequest)) { + throw new Error('Expected argument of type querier.v1.AnalyzeQueryRequest'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_querier_v1_AnalyzeQueryRequest(buffer_arg) { + return querier_pb.AnalyzeQueryRequest.deserializeBinary(new Uint8Array(buffer_arg)); +} + +function serialize_querier_v1_AnalyzeQueryResponse(arg) { + if (!(arg instanceof querier_pb.AnalyzeQueryResponse)) { + throw new Error('Expected argument of type querier.v1.AnalyzeQueryResponse'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_querier_v1_AnalyzeQueryResponse(buffer_arg) { + return querier_pb.AnalyzeQueryResponse.deserializeBinary(new Uint8Array(buffer_arg)); +} + function serialize_querier_v1_DiffRequest(arg) { if (!(arg instanceof querier_pb.DiffRequest)) { throw new Error('Expected argument of type querier.v1.DiffRequest'); @@ -160,6 +182,28 @@ function deserialize_querier_v1_SeriesResponse(buffer_arg) { return querier_pb.SeriesResponse.deserializeBinary(new Uint8Array(buffer_arg)); } +function serialize_types_v1_GetProfileStatsRequest(arg) { + if (!(arg instanceof types_v1_types_pb.GetProfileStatsRequest)) { + throw new Error('Expected argument of type types.v1.GetProfileStatsRequest'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_types_v1_GetProfileStatsRequest(buffer_arg) { + return types_v1_types_pb.GetProfileStatsRequest.deserializeBinary(new Uint8Array(buffer_arg)); +} + +function serialize_types_v1_GetProfileStatsResponse(arg) { + if (!(arg instanceof types_v1_types_pb.GetProfileStatsResponse)) { + throw new Error('Expected argument of type types.v1.GetProfileStatsResponse'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_types_v1_GetProfileStatsResponse(buffer_arg) { + return types_v1_types_pb.GetProfileStatsResponse.deserializeBinary(new Uint8Array(buffer_arg)); +} + function serialize_types_v1_LabelNamesRequest(arg) { if (!(arg instanceof types_v1_types_pb.LabelNamesRequest)) { throw new Error('Expected argument of type types.v1.LabelNamesRequest'); @@ -266,7 +310,7 @@ selectMergeStacktraces: { responseSerialize: serialize_querier_v1_SelectMergeStacktracesResponse, responseDeserialize: deserialize_querier_v1_SelectMergeStacktracesResponse, }, - // SelectMergeSpans returns matching profiles aggregated in a flamegraph format. It will combine samples from within the same callstack, with each element being grouped by its function name. + // SelectMergeSpanProfile returns matching profiles aggregated in a flamegraph format. It will combine samples from within the same callstack, with each element being grouped by its function name. selectMergeSpanProfile: { path: '/querier.v1.QuerierService/SelectMergeSpanProfile', requestStream: false, @@ -302,7 +346,8 @@ selectSeries: { responseSerialize: serialize_querier_v1_SelectSeriesResponse, responseDeserialize: deserialize_querier_v1_SelectSeriesResponse, }, - diff: { + // Diff returns a diff of two profiles +diff: { path: '/querier.v1.QuerierService/Diff', requestStream: false, responseStream: false, @@ -313,6 +358,29 @@ selectSeries: { responseSerialize: serialize_querier_v1_DiffResponse, responseDeserialize: deserialize_querier_v1_DiffResponse, }, + // GetProfileStats returns profile stats for the current tenant. +getProfileStats: { + path: '/querier.v1.QuerierService/GetProfileStats', + requestStream: false, + responseStream: false, + requestType: types_v1_types_pb.GetProfileStatsRequest, + responseType: types_v1_types_pb.GetProfileStatsResponse, + requestSerialize: serialize_types_v1_GetProfileStatsRequest, + requestDeserialize: deserialize_types_v1_GetProfileStatsRequest, + responseSerialize: serialize_types_v1_GetProfileStatsResponse, + responseDeserialize: deserialize_types_v1_GetProfileStatsResponse, + }, + analyzeQuery: { + path: '/querier.v1.QuerierService/AnalyzeQuery', + requestStream: false, + responseStream: false, + requestType: querier_pb.AnalyzeQueryRequest, + responseType: querier_pb.AnalyzeQueryResponse, + requestSerialize: serialize_querier_v1_AnalyzeQueryRequest, + requestDeserialize: deserialize_querier_v1_AnalyzeQueryRequest, + responseSerialize: serialize_querier_v1_AnalyzeQueryResponse, + responseDeserialize: deserialize_querier_v1_AnalyzeQueryResponse, + }, }; exports.QuerierServiceClient = grpc.makeGenericClientConstructor(QuerierServiceService); diff --git a/pyroscope/querier_pb.js b/pyroscope/querier_pb.js index 8988dda9..82334400 100644 --- a/pyroscope/querier_pb.js +++ b/pyroscope/querier_pb.js @@ -25,13 +25,18 @@ var google_v1_profile_pb = require('./google/v1/profile_pb.js'); goog.object.extend(proto, google_v1_profile_pb); var types_v1_types_pb = require('./types/v1/types_pb.js'); goog.object.extend(proto, types_v1_types_pb); +goog.exportSymbol('proto.querier.v1.AnalyzeQueryRequest', null, global); +goog.exportSymbol('proto.querier.v1.AnalyzeQueryResponse', null, global); goog.exportSymbol('proto.querier.v1.DiffRequest', null, global); goog.exportSymbol('proto.querier.v1.DiffResponse', null, global); goog.exportSymbol('proto.querier.v1.FlameGraph', null, global); goog.exportSymbol('proto.querier.v1.FlameGraphDiff', null, global); goog.exportSymbol('proto.querier.v1.Level', null, global); +goog.exportSymbol('proto.querier.v1.ProfileFormat', null, global); goog.exportSymbol('proto.querier.v1.ProfileTypesRequest', null, global); goog.exportSymbol('proto.querier.v1.ProfileTypesResponse', null, global); +goog.exportSymbol('proto.querier.v1.QueryImpact', null, global); +goog.exportSymbol('proto.querier.v1.QueryScope', null, global); goog.exportSymbol('proto.querier.v1.SelectMergeProfileRequest', null, global); goog.exportSymbol('proto.querier.v1.SelectMergeSpanProfileRequest', null, global); goog.exportSymbol('proto.querier.v1.SelectMergeSpanProfileResponse', null, global); @@ -377,6 +382,90 @@ if (goog.DEBUG && !COMPILED) { */ proto.querier.v1.SelectSeriesResponse.displayName = 'proto.querier.v1.SelectSeriesResponse'; } +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.querier.v1.AnalyzeQueryRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.querier.v1.AnalyzeQueryRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.querier.v1.AnalyzeQueryRequest.displayName = 'proto.querier.v1.AnalyzeQueryRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.querier.v1.AnalyzeQueryResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.querier.v1.AnalyzeQueryResponse.repeatedFields_, null); +}; +goog.inherits(proto.querier.v1.AnalyzeQueryResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.querier.v1.AnalyzeQueryResponse.displayName = 'proto.querier.v1.AnalyzeQueryResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.querier.v1.QueryScope = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.querier.v1.QueryScope, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.querier.v1.QueryScope.displayName = 'proto.querier.v1.QueryScope'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.querier.v1.QueryImpact = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.querier.v1.QueryImpact, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.querier.v1.QueryImpact.displayName = 'proto.querier.v1.QueryImpact'; +} @@ -1158,7 +1247,8 @@ proto.querier.v1.SelectMergeStacktracesRequest.toObject = function(includeInstan labelSelector: jspb.Message.getFieldWithDefault(msg, 2, ""), start: jspb.Message.getFieldWithDefault(msg, 3, 0), end: jspb.Message.getFieldWithDefault(msg, 4, 0), - maxNodes: jspb.Message.getFieldWithDefault(msg, 5, 0) + maxNodes: jspb.Message.getFieldWithDefault(msg, 5, 0), + format: jspb.Message.getFieldWithDefault(msg, 6, 0) }; if (includeInstance) { @@ -1215,6 +1305,10 @@ proto.querier.v1.SelectMergeStacktracesRequest.deserializeBinaryFromReader = fun var value = /** @type {number} */ (reader.readInt64()); msg.setMaxNodes(value); break; + case 6: + var value = /** @type {!proto.querier.v1.ProfileFormat} */ (reader.readEnum()); + msg.setFormat(value); + break; default: reader.skipField(); break; @@ -1279,6 +1373,13 @@ proto.querier.v1.SelectMergeStacktracesRequest.serializeBinaryToWriter = functio f ); } + f = message.getFormat(); + if (f !== 0.0) { + writer.writeEnum( + 6, + f + ); + } }; @@ -1390,6 +1491,24 @@ proto.querier.v1.SelectMergeStacktracesRequest.prototype.hasMaxNodes = function( }; +/** + * optional ProfileFormat format = 6; + * @return {!proto.querier.v1.ProfileFormat} + */ +proto.querier.v1.SelectMergeStacktracesRequest.prototype.getFormat = function() { + return /** @type {!proto.querier.v1.ProfileFormat} */ (jspb.Message.getFieldWithDefault(this, 6, 0)); +}; + + +/** + * @param {!proto.querier.v1.ProfileFormat} value + * @return {!proto.querier.v1.SelectMergeStacktracesRequest} returns this + */ +proto.querier.v1.SelectMergeStacktracesRequest.prototype.setFormat = function(value) { + return jspb.Message.setProto3EnumField(this, 6, value); +}; + + @@ -1422,7 +1541,8 @@ proto.querier.v1.SelectMergeStacktracesResponse.prototype.toObject = function(op */ proto.querier.v1.SelectMergeStacktracesResponse.toObject = function(includeInstance, msg) { var f, obj = { - flamegraph: (f = msg.getFlamegraph()) && proto.querier.v1.FlameGraph.toObject(includeInstance, f) + flamegraph: (f = msg.getFlamegraph()) && proto.querier.v1.FlameGraph.toObject(includeInstance, f), + tree: msg.getTree_asB64() }; if (includeInstance) { @@ -1464,6 +1584,10 @@ proto.querier.v1.SelectMergeStacktracesResponse.deserializeBinaryFromReader = fu reader.readMessage(value,proto.querier.v1.FlameGraph.deserializeBinaryFromReader); msg.setFlamegraph(value); break; + case 2: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setTree(value); + break; default: reader.skipField(); break; @@ -1501,6 +1625,13 @@ proto.querier.v1.SelectMergeStacktracesResponse.serializeBinaryToWriter = functi proto.querier.v1.FlameGraph.serializeBinaryToWriter ); } + f = message.getTree_asU8(); + if (f.length > 0) { + writer.writeBytes( + 2, + f + ); + } }; @@ -1541,6 +1672,48 @@ proto.querier.v1.SelectMergeStacktracesResponse.prototype.hasFlamegraph = functi }; +/** + * optional bytes tree = 2; + * @return {!(string|Uint8Array)} + */ +proto.querier.v1.SelectMergeStacktracesResponse.prototype.getTree = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * optional bytes tree = 2; + * This is a type-conversion wrapper around `getTree()` + * @return {string} + */ +proto.querier.v1.SelectMergeStacktracesResponse.prototype.getTree_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getTree())); +}; + + +/** + * optional bytes tree = 2; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getTree()` + * @return {!Uint8Array} + */ +proto.querier.v1.SelectMergeStacktracesResponse.prototype.getTree_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getTree())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.querier.v1.SelectMergeStacktracesResponse} returns this + */ +proto.querier.v1.SelectMergeStacktracesResponse.prototype.setTree = function(value) { + return jspb.Message.setProto3BytesField(this, 2, value); +}; + + /** * List of repeated fields within this message type. @@ -1585,7 +1758,8 @@ proto.querier.v1.SelectMergeSpanProfileRequest.toObject = function(includeInstan spanSelectorList: (f = jspb.Message.getRepeatedField(msg, 3)) == null ? undefined : f, start: jspb.Message.getFieldWithDefault(msg, 4, 0), end: jspb.Message.getFieldWithDefault(msg, 5, 0), - maxNodes: jspb.Message.getFieldWithDefault(msg, 6, 0) + maxNodes: jspb.Message.getFieldWithDefault(msg, 6, 0), + format: jspb.Message.getFieldWithDefault(msg, 7, 0) }; if (includeInstance) { @@ -1646,6 +1820,10 @@ proto.querier.v1.SelectMergeSpanProfileRequest.deserializeBinaryFromReader = fun var value = /** @type {number} */ (reader.readInt64()); msg.setMaxNodes(value); break; + case 7: + var value = /** @type {!proto.querier.v1.ProfileFormat} */ (reader.readEnum()); + msg.setFormat(value); + break; default: reader.skipField(); break; @@ -1717,6 +1895,13 @@ proto.querier.v1.SelectMergeSpanProfileRequest.serializeBinaryToWriter = functio f ); } + f = message.getFormat(); + if (f !== 0.0) { + writer.writeEnum( + 7, + f + ); + } }; @@ -1865,6 +2050,24 @@ proto.querier.v1.SelectMergeSpanProfileRequest.prototype.hasMaxNodes = function( }; +/** + * optional ProfileFormat format = 7; + * @return {!proto.querier.v1.ProfileFormat} + */ +proto.querier.v1.SelectMergeSpanProfileRequest.prototype.getFormat = function() { + return /** @type {!proto.querier.v1.ProfileFormat} */ (jspb.Message.getFieldWithDefault(this, 7, 0)); +}; + + +/** + * @param {!proto.querier.v1.ProfileFormat} value + * @return {!proto.querier.v1.SelectMergeSpanProfileRequest} returns this + */ +proto.querier.v1.SelectMergeSpanProfileRequest.prototype.setFormat = function(value) { + return jspb.Message.setProto3EnumField(this, 7, value); +}; + + @@ -1897,7 +2100,8 @@ proto.querier.v1.SelectMergeSpanProfileResponse.prototype.toObject = function(op */ proto.querier.v1.SelectMergeSpanProfileResponse.toObject = function(includeInstance, msg) { var f, obj = { - flamegraph: (f = msg.getFlamegraph()) && proto.querier.v1.FlameGraph.toObject(includeInstance, f) + flamegraph: (f = msg.getFlamegraph()) && proto.querier.v1.FlameGraph.toObject(includeInstance, f), + tree: msg.getTree_asB64() }; if (includeInstance) { @@ -1939,6 +2143,10 @@ proto.querier.v1.SelectMergeSpanProfileResponse.deserializeBinaryFromReader = fu reader.readMessage(value,proto.querier.v1.FlameGraph.deserializeBinaryFromReader); msg.setFlamegraph(value); break; + case 2: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setTree(value); + break; default: reader.skipField(); break; @@ -1976,6 +2184,13 @@ proto.querier.v1.SelectMergeSpanProfileResponse.serializeBinaryToWriter = functi proto.querier.v1.FlameGraph.serializeBinaryToWriter ); } + f = message.getTree_asU8(); + if (f.length > 0) { + writer.writeBytes( + 2, + f + ); + } }; @@ -2016,6 +2231,48 @@ proto.querier.v1.SelectMergeSpanProfileResponse.prototype.hasFlamegraph = functi }; +/** + * optional bytes tree = 2; + * @return {!(string|Uint8Array)} + */ +proto.querier.v1.SelectMergeSpanProfileResponse.prototype.getTree = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * optional bytes tree = 2; + * This is a type-conversion wrapper around `getTree()` + * @return {string} + */ +proto.querier.v1.SelectMergeSpanProfileResponse.prototype.getTree_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getTree())); +}; + + +/** + * optional bytes tree = 2; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getTree()` + * @return {!Uint8Array} + */ +proto.querier.v1.SelectMergeSpanProfileResponse.prototype.getTree_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getTree())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.querier.v1.SelectMergeSpanProfileResponse} returns this + */ +proto.querier.v1.SelectMergeSpanProfileResponse.prototype.setTree = function(value) { + return jspb.Message.setProto3BytesField(this, 2, value); +}; + + @@ -3489,7 +3746,8 @@ proto.querier.v1.SelectSeriesRequest.toObject = function(includeInstance, msg) { end: jspb.Message.getFieldWithDefault(msg, 4, 0), groupByList: (f = jspb.Message.getRepeatedField(msg, 5)) == null ? undefined : f, step: jspb.Message.getFloatingPointFieldWithDefault(msg, 6, 0.0), - aggregation: jspb.Message.getFieldWithDefault(msg, 7, 0) + aggregation: jspb.Message.getFieldWithDefault(msg, 7, 0), + stackTraceSelector: (f = msg.getStackTraceSelector()) && types_v1_types_pb.StackTraceSelector.toObject(includeInstance, f) }; if (includeInstance) { @@ -3554,6 +3812,11 @@ proto.querier.v1.SelectSeriesRequest.deserializeBinaryFromReader = function(msg, var value = /** @type {!proto.types.v1.TimeSeriesAggregationType} */ (reader.readEnum()); msg.setAggregation(value); break; + case 8: + var value = new types_v1_types_pb.StackTraceSelector; + reader.readMessage(value,types_v1_types_pb.StackTraceSelector.deserializeBinaryFromReader); + msg.setStackTraceSelector(value); + break; default: reader.skipField(); break; @@ -3632,6 +3895,14 @@ proto.querier.v1.SelectSeriesRequest.serializeBinaryToWriter = function(message, f ); } + f = message.getStackTraceSelector(); + if (f != null) { + writer.writeMessage( + 8, + f, + types_v1_types_pb.StackTraceSelector.serializeBinaryToWriter + ); + } }; @@ -3798,6 +4069,43 @@ proto.querier.v1.SelectSeriesRequest.prototype.hasAggregation = function() { }; +/** + * optional types.v1.StackTraceSelector stack_trace_selector = 8; + * @return {?proto.types.v1.StackTraceSelector} + */ +proto.querier.v1.SelectSeriesRequest.prototype.getStackTraceSelector = function() { + return /** @type{?proto.types.v1.StackTraceSelector} */ ( + jspb.Message.getWrapperField(this, types_v1_types_pb.StackTraceSelector, 8)); +}; + + +/** + * @param {?proto.types.v1.StackTraceSelector|undefined} value + * @return {!proto.querier.v1.SelectSeriesRequest} returns this +*/ +proto.querier.v1.SelectSeriesRequest.prototype.setStackTraceSelector = function(value) { + return jspb.Message.setWrapperField(this, 8, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.querier.v1.SelectSeriesRequest} returns this + */ +proto.querier.v1.SelectSeriesRequest.prototype.clearStackTraceSelector = function() { + return this.setStackTraceSelector(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.querier.v1.SelectSeriesRequest.prototype.hasStackTraceSelector = function() { + return jspb.Message.getField(this, 8) != null; +}; + + /** * List of repeated fields within this message type. @@ -3958,4 +4266,974 @@ proto.querier.v1.SelectSeriesResponse.prototype.clearSeriesList = function() { }; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.querier.v1.AnalyzeQueryRequest.prototype.toObject = function(opt_includeInstance) { + return proto.querier.v1.AnalyzeQueryRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.querier.v1.AnalyzeQueryRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.querier.v1.AnalyzeQueryRequest.toObject = function(includeInstance, msg) { + var f, obj = { + start: jspb.Message.getFieldWithDefault(msg, 2, 0), + end: jspb.Message.getFieldWithDefault(msg, 3, 0), + query: jspb.Message.getFieldWithDefault(msg, 4, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.querier.v1.AnalyzeQueryRequest} + */ +proto.querier.v1.AnalyzeQueryRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.querier.v1.AnalyzeQueryRequest; + return proto.querier.v1.AnalyzeQueryRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.querier.v1.AnalyzeQueryRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.querier.v1.AnalyzeQueryRequest} + */ +proto.querier.v1.AnalyzeQueryRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 2: + var value = /** @type {number} */ (reader.readInt64()); + msg.setStart(value); + break; + case 3: + var value = /** @type {number} */ (reader.readInt64()); + msg.setEnd(value); + break; + case 4: + var value = /** @type {string} */ (reader.readString()); + msg.setQuery(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.querier.v1.AnalyzeQueryRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.querier.v1.AnalyzeQueryRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.querier.v1.AnalyzeQueryRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.querier.v1.AnalyzeQueryRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getStart(); + if (f !== 0) { + writer.writeInt64( + 2, + f + ); + } + f = message.getEnd(); + if (f !== 0) { + writer.writeInt64( + 3, + f + ); + } + f = message.getQuery(); + if (f.length > 0) { + writer.writeString( + 4, + f + ); + } +}; + + +/** + * optional int64 start = 2; + * @return {number} + */ +proto.querier.v1.AnalyzeQueryRequest.prototype.getStart = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.querier.v1.AnalyzeQueryRequest} returns this + */ +proto.querier.v1.AnalyzeQueryRequest.prototype.setStart = function(value) { + return jspb.Message.setProto3IntField(this, 2, value); +}; + + +/** + * optional int64 end = 3; + * @return {number} + */ +proto.querier.v1.AnalyzeQueryRequest.prototype.getEnd = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.querier.v1.AnalyzeQueryRequest} returns this + */ +proto.querier.v1.AnalyzeQueryRequest.prototype.setEnd = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + +/** + * optional string query = 4; + * @return {string} + */ +proto.querier.v1.AnalyzeQueryRequest.prototype.getQuery = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, "")); +}; + + +/** + * @param {string} value + * @return {!proto.querier.v1.AnalyzeQueryRequest} returns this + */ +proto.querier.v1.AnalyzeQueryRequest.prototype.setQuery = function(value) { + return jspb.Message.setProto3StringField(this, 4, value); +}; + + + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.querier.v1.AnalyzeQueryResponse.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.querier.v1.AnalyzeQueryResponse.prototype.toObject = function(opt_includeInstance) { + return proto.querier.v1.AnalyzeQueryResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.querier.v1.AnalyzeQueryResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.querier.v1.AnalyzeQueryResponse.toObject = function(includeInstance, msg) { + var f, obj = { + queryScopesList: jspb.Message.toObjectList(msg.getQueryScopesList(), + proto.querier.v1.QueryScope.toObject, includeInstance), + queryImpact: (f = msg.getQueryImpact()) && proto.querier.v1.QueryImpact.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.querier.v1.AnalyzeQueryResponse} + */ +proto.querier.v1.AnalyzeQueryResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.querier.v1.AnalyzeQueryResponse; + return proto.querier.v1.AnalyzeQueryResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.querier.v1.AnalyzeQueryResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.querier.v1.AnalyzeQueryResponse} + */ +proto.querier.v1.AnalyzeQueryResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new proto.querier.v1.QueryScope; + reader.readMessage(value,proto.querier.v1.QueryScope.deserializeBinaryFromReader); + msg.addQueryScopes(value); + break; + case 2: + var value = new proto.querier.v1.QueryImpact; + reader.readMessage(value,proto.querier.v1.QueryImpact.deserializeBinaryFromReader); + msg.setQueryImpact(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.querier.v1.AnalyzeQueryResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.querier.v1.AnalyzeQueryResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.querier.v1.AnalyzeQueryResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.querier.v1.AnalyzeQueryResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getQueryScopesList(); + if (f.length > 0) { + writer.writeRepeatedMessage( + 1, + f, + proto.querier.v1.QueryScope.serializeBinaryToWriter + ); + } + f = message.getQueryImpact(); + if (f != null) { + writer.writeMessage( + 2, + f, + proto.querier.v1.QueryImpact.serializeBinaryToWriter + ); + } +}; + + +/** + * repeated QueryScope query_scopes = 1; + * @return {!Array} + */ +proto.querier.v1.AnalyzeQueryResponse.prototype.getQueryScopesList = function() { + return /** @type{!Array} */ ( + jspb.Message.getRepeatedWrapperField(this, proto.querier.v1.QueryScope, 1)); +}; + + +/** + * @param {!Array} value + * @return {!proto.querier.v1.AnalyzeQueryResponse} returns this +*/ +proto.querier.v1.AnalyzeQueryResponse.prototype.setQueryScopesList = function(value) { + return jspb.Message.setRepeatedWrapperField(this, 1, value); +}; + + +/** + * @param {!proto.querier.v1.QueryScope=} opt_value + * @param {number=} opt_index + * @return {!proto.querier.v1.QueryScope} + */ +proto.querier.v1.AnalyzeQueryResponse.prototype.addQueryScopes = function(opt_value, opt_index) { + return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.querier.v1.QueryScope, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.querier.v1.AnalyzeQueryResponse} returns this + */ +proto.querier.v1.AnalyzeQueryResponse.prototype.clearQueryScopesList = function() { + return this.setQueryScopesList([]); +}; + + +/** + * optional QueryImpact query_impact = 2; + * @return {?proto.querier.v1.QueryImpact} + */ +proto.querier.v1.AnalyzeQueryResponse.prototype.getQueryImpact = function() { + return /** @type{?proto.querier.v1.QueryImpact} */ ( + jspb.Message.getWrapperField(this, proto.querier.v1.QueryImpact, 2)); +}; + + +/** + * @param {?proto.querier.v1.QueryImpact|undefined} value + * @return {!proto.querier.v1.AnalyzeQueryResponse} returns this +*/ +proto.querier.v1.AnalyzeQueryResponse.prototype.setQueryImpact = function(value) { + return jspb.Message.setWrapperField(this, 2, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.querier.v1.AnalyzeQueryResponse} returns this + */ +proto.querier.v1.AnalyzeQueryResponse.prototype.clearQueryImpact = function() { + return this.setQueryImpact(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.querier.v1.AnalyzeQueryResponse.prototype.hasQueryImpact = function() { + return jspb.Message.getField(this, 2) != null; +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.querier.v1.QueryScope.prototype.toObject = function(opt_includeInstance) { + return proto.querier.v1.QueryScope.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.querier.v1.QueryScope} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.querier.v1.QueryScope.toObject = function(includeInstance, msg) { + var f, obj = { + componentType: jspb.Message.getFieldWithDefault(msg, 1, ""), + componentCount: jspb.Message.getFieldWithDefault(msg, 2, 0), + blockCount: jspb.Message.getFieldWithDefault(msg, 3, 0), + seriesCount: jspb.Message.getFieldWithDefault(msg, 4, 0), + profileCount: jspb.Message.getFieldWithDefault(msg, 5, 0), + sampleCount: jspb.Message.getFieldWithDefault(msg, 6, 0), + indexBytes: jspb.Message.getFieldWithDefault(msg, 7, 0), + profileBytes: jspb.Message.getFieldWithDefault(msg, 8, 0), + symbolBytes: jspb.Message.getFieldWithDefault(msg, 9, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.querier.v1.QueryScope} + */ +proto.querier.v1.QueryScope.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.querier.v1.QueryScope; + return proto.querier.v1.QueryScope.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.querier.v1.QueryScope} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.querier.v1.QueryScope} + */ +proto.querier.v1.QueryScope.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setComponentType(value); + break; + case 2: + var value = /** @type {number} */ (reader.readUint64()); + msg.setComponentCount(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint64()); + msg.setBlockCount(value); + break; + case 4: + var value = /** @type {number} */ (reader.readUint64()); + msg.setSeriesCount(value); + break; + case 5: + var value = /** @type {number} */ (reader.readUint64()); + msg.setProfileCount(value); + break; + case 6: + var value = /** @type {number} */ (reader.readUint64()); + msg.setSampleCount(value); + break; + case 7: + var value = /** @type {number} */ (reader.readUint64()); + msg.setIndexBytes(value); + break; + case 8: + var value = /** @type {number} */ (reader.readUint64()); + msg.setProfileBytes(value); + break; + case 9: + var value = /** @type {number} */ (reader.readUint64()); + msg.setSymbolBytes(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.querier.v1.QueryScope.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.querier.v1.QueryScope.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.querier.v1.QueryScope} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.querier.v1.QueryScope.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getComponentType(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getComponentCount(); + if (f !== 0) { + writer.writeUint64( + 2, + f + ); + } + f = message.getBlockCount(); + if (f !== 0) { + writer.writeUint64( + 3, + f + ); + } + f = message.getSeriesCount(); + if (f !== 0) { + writer.writeUint64( + 4, + f + ); + } + f = message.getProfileCount(); + if (f !== 0) { + writer.writeUint64( + 5, + f + ); + } + f = message.getSampleCount(); + if (f !== 0) { + writer.writeUint64( + 6, + f + ); + } + f = message.getIndexBytes(); + if (f !== 0) { + writer.writeUint64( + 7, + f + ); + } + f = message.getProfileBytes(); + if (f !== 0) { + writer.writeUint64( + 8, + f + ); + } + f = message.getSymbolBytes(); + if (f !== 0) { + writer.writeUint64( + 9, + f + ); + } +}; + + +/** + * optional string component_type = 1; + * @return {string} + */ +proto.querier.v1.QueryScope.prototype.getComponentType = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.querier.v1.QueryScope} returns this + */ +proto.querier.v1.QueryScope.prototype.setComponentType = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional uint64 component_count = 2; + * @return {number} + */ +proto.querier.v1.QueryScope.prototype.getComponentCount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.querier.v1.QueryScope} returns this + */ +proto.querier.v1.QueryScope.prototype.setComponentCount = function(value) { + return jspb.Message.setProto3IntField(this, 2, value); +}; + + +/** + * optional uint64 block_count = 3; + * @return {number} + */ +proto.querier.v1.QueryScope.prototype.getBlockCount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.querier.v1.QueryScope} returns this + */ +proto.querier.v1.QueryScope.prototype.setBlockCount = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + +/** + * optional uint64 series_count = 4; + * @return {number} + */ +proto.querier.v1.QueryScope.prototype.getSeriesCount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.querier.v1.QueryScope} returns this + */ +proto.querier.v1.QueryScope.prototype.setSeriesCount = function(value) { + return jspb.Message.setProto3IntField(this, 4, value); +}; + + +/** + * optional uint64 profile_count = 5; + * @return {number} + */ +proto.querier.v1.QueryScope.prototype.getProfileCount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.querier.v1.QueryScope} returns this + */ +proto.querier.v1.QueryScope.prototype.setProfileCount = function(value) { + return jspb.Message.setProto3IntField(this, 5, value); +}; + + +/** + * optional uint64 sample_count = 6; + * @return {number} + */ +proto.querier.v1.QueryScope.prototype.getSampleCount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 6, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.querier.v1.QueryScope} returns this + */ +proto.querier.v1.QueryScope.prototype.setSampleCount = function(value) { + return jspb.Message.setProto3IntField(this, 6, value); +}; + + +/** + * optional uint64 index_bytes = 7; + * @return {number} + */ +proto.querier.v1.QueryScope.prototype.getIndexBytes = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 7, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.querier.v1.QueryScope} returns this + */ +proto.querier.v1.QueryScope.prototype.setIndexBytes = function(value) { + return jspb.Message.setProto3IntField(this, 7, value); +}; + + +/** + * optional uint64 profile_bytes = 8; + * @return {number} + */ +proto.querier.v1.QueryScope.prototype.getProfileBytes = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 8, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.querier.v1.QueryScope} returns this + */ +proto.querier.v1.QueryScope.prototype.setProfileBytes = function(value) { + return jspb.Message.setProto3IntField(this, 8, value); +}; + + +/** + * optional uint64 symbol_bytes = 9; + * @return {number} + */ +proto.querier.v1.QueryScope.prototype.getSymbolBytes = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 9, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.querier.v1.QueryScope} returns this + */ +proto.querier.v1.QueryScope.prototype.setSymbolBytes = function(value) { + return jspb.Message.setProto3IntField(this, 9, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.querier.v1.QueryImpact.prototype.toObject = function(opt_includeInstance) { + return proto.querier.v1.QueryImpact.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.querier.v1.QueryImpact} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.querier.v1.QueryImpact.toObject = function(includeInstance, msg) { + var f, obj = { + totalBytesInTimeRange: jspb.Message.getFieldWithDefault(msg, 2, 0), + totalQueriedSeries: jspb.Message.getFieldWithDefault(msg, 3, 0), + deduplicationNeeded: jspb.Message.getBooleanFieldWithDefault(msg, 4, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.querier.v1.QueryImpact} + */ +proto.querier.v1.QueryImpact.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.querier.v1.QueryImpact; + return proto.querier.v1.QueryImpact.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.querier.v1.QueryImpact} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.querier.v1.QueryImpact} + */ +proto.querier.v1.QueryImpact.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 2: + var value = /** @type {number} */ (reader.readUint64()); + msg.setTotalBytesInTimeRange(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint64()); + msg.setTotalQueriedSeries(value); + break; + case 4: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setDeduplicationNeeded(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.querier.v1.QueryImpact.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.querier.v1.QueryImpact.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.querier.v1.QueryImpact} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.querier.v1.QueryImpact.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getTotalBytesInTimeRange(); + if (f !== 0) { + writer.writeUint64( + 2, + f + ); + } + f = message.getTotalQueriedSeries(); + if (f !== 0) { + writer.writeUint64( + 3, + f + ); + } + f = message.getDeduplicationNeeded(); + if (f) { + writer.writeBool( + 4, + f + ); + } +}; + + +/** + * optional uint64 total_bytes_in_time_range = 2; + * @return {number} + */ +proto.querier.v1.QueryImpact.prototype.getTotalBytesInTimeRange = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.querier.v1.QueryImpact} returns this + */ +proto.querier.v1.QueryImpact.prototype.setTotalBytesInTimeRange = function(value) { + return jspb.Message.setProto3IntField(this, 2, value); +}; + + +/** + * optional uint64 total_queried_series = 3; + * @return {number} + */ +proto.querier.v1.QueryImpact.prototype.getTotalQueriedSeries = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.querier.v1.QueryImpact} returns this + */ +proto.querier.v1.QueryImpact.prototype.setTotalQueriedSeries = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + +/** + * optional bool deduplication_needed = 4; + * @return {boolean} + */ +proto.querier.v1.QueryImpact.prototype.getDeduplicationNeeded = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 4, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.querier.v1.QueryImpact} returns this + */ +proto.querier.v1.QueryImpact.prototype.setDeduplicationNeeded = function(value) { + return jspb.Message.setProto3BooleanField(this, 4, value); +}; + + +/** + * @enum {number} + */ +proto.querier.v1.ProfileFormat = { + PROFILE_FORMAT_UNSPECIFIED: 0, + PROFILE_FORMAT_FLAMEGRAPH: 1, + PROFILE_FORMAT_TREE: 2 +}; + goog.object.extend(exports, proto.querier.v1); diff --git a/pyroscope/render.js b/pyroscope/render.js new file mode 100644 index 00000000..b6fc29ca --- /dev/null +++ b/pyroscope/render.js @@ -0,0 +1,257 @@ +const { parseTypeId } = require('./shared') +const { mergeStackTraces } = require('./merge_stack_traces') +const querierMessages = require('./querier_pb') +const { selectSeriesImpl } = require('./select_series') +const types = require('./types/v1/types_pb') + +const render = async (req, res) => { + const query = req.query.query + const parsedQuery = parseQuery(query) + const fromTimeSec = req.query.from + ? Math.floor(parseInt(req.query.from) / 1000) + : Math.floor((Date.now() - 1000 * 60 * 60 * 48) / 1000) + const toTimeSec = req.query.until + ? Math.floor(parseInt(req.query.until) / 1000) + : Math.floor((Date.now() - 1000 * 60 * 60 * 48) / 1000) + if (!parsedQuery) { + return res.sendStatus(400).send('Invalid query') + } + const groupBy = req.query.groupBy || [] + let agg = '' + switch (req.query.aggregation) { + case 'sum': + agg = 'sum' + break + case 'avg': + agg = 'avg' + break + } + if (req.query.format === 'dot') { + return res.sendStatus(400).send('Dot format is not supported') + } + const promises = [] + promises.push(mergeStackTraces( + parsedQuery.typeDesc, + '{' + parsedQuery.labelSelector + '}', + fromTimeSec, + toTimeSec, + req.log)) + + const timelineStep = calcIntervalSec(fromTimeSec, toTimeSec) + promises.push(selectSeriesImpl( + fromTimeSec, + toTimeSec, + { + getProfileTypeid: () => parsedQuery.typeId, + getLabelSelector: () => `{${parsedQuery.labelSelector}}`, + getGroupByList: () => groupBy, + getStep: () => timelineStep, + getAggregation: () => agg + } + )) + const [bMergeStackTrace, selectSeries] = + await Promise.all(promises) + const mergeStackTrace = querierMessages.SelectMergeStacktracesResponse.deserializeBinary(bMergeStackTrace) + let series = new types.Series() + if (selectSeries.getSeriesList().length === 1) { + series = selectSeries.getSeriesList()[0] + } + const fb = toFlamebearer(mergeStackTrace.getFlamegraph(), parsedQuery.profileType) + fb.flamebearerProfileV1.timeline = timeline(series, + fromTimeSec * 1000, + toTimeSec * 1000, + timelineStep) + + if (groupBy.length > 0) { + fb.flamebearerProfileV1.groups = {} + let key = '*' + series.getSeriesList().forEach((_series) => { + _series.getLabelsList().forEach((label) => { + key = label.getName() === groupBy[0] ? label.getValue() : key + }) + }) + fb.flamebearerProfileV1.groups[key] = timeline(series, + fromTimeSec * 1000, + toTimeSec * 1000, + timelineStep) + } + res.code(200) + res.headers({ 'Content-Type': 'application/json' }) + return res.send(Buffer.from(JSON.stringify(fb.flamebearerProfileV1))) +} + +/** + * + * @param fg + * @param profileType + * @returns {Flamebearer} + */ +function toFlamebearer (fg, profileType) { + if (!fg) { + fg = new querierMessages.FlameGraph() + } + let unit = profileType.getSampleUnit() + let sampleRate = 100 + switch (profileType.getSampleType()) { + case 'inuse_objects': + case 'alloc_objects': + case 'goroutine': + case 'samples': + unit = 'objects' + break + case 'cpu': + unit = 'samples' + sampleRate = 1000000000 + } + /** @type {flamebearerV1} */ + const flameBearer = { + levels: fg.getLevelsList().map(l => l.getValuesList().map(v => v)), + maxSelf: fg.getMaxSelf(), + names: fg.getNamesList(), + numTicks: fg.getTotal() + } + /** @type {flamebearerMetadataV1} */ + const metadata = { + format: 'single', + units: unit, + name: profileType.getSampleType(), + sampleRate: sampleRate + } + + return { + version: 1, + flamebearerProfileV1: { + metadata: metadata, + flamebearer: flameBearer + } + } +} + +/** + * + * @param fromSec {number} + * @param toSec {number} + * @returns {number} + */ +function calcIntervalSec (fromSec, toSec) { + return Math.max(Math.ceil((toSec - fromSec) / 1500), 15) +} + +/** + * + * @param series + * @param startMs + * @param endMs + * @param durationDeltaSec + * @returns {flamebearerTimelineV1} + */ +function timeline (series, startMs, endMs, durationDeltaSec) { + const durationDeltaMs = durationDeltaSec * 1000 + startMs = Math.floor(startMs / durationDeltaMs) * durationDeltaMs + endMs = Math.floor(endMs / durationDeltaMs) * durationDeltaMs + const startS = Math.floor(startMs / 1000) + /** @type {flamebearerTimelineV1} */ + const timeline = { + durationDelta: durationDeltaSec, + startTime: startS, + samples: [] + } + if (startMs >= endMs) { + return timeline + } + const points = boundPointsToWindow(series.getPointsList(), startMs, endMs) + if (points.length < 1) { + const n = sizeToBackfill(startMs, endMs, durationDeltaSec) + if (n > 0) { + timeline.samples = new Array(n).fill(0) + } + return timeline + } + + let n = sizeToBackfill(startMs, parseInt(points[0].getTimestamp()), durationDeltaSec) + const samples = n > 0 ? Array(n).fill(0) : [] + let prev = points[0] + for (const p of points) { + n = sizeToBackfill(parseInt(prev.getTimestamp()), parseInt(p.getTimestamp()), durationDeltaSec) + Array.prototype.push.apply(samples, new Array(Math.max(0, n - 1)).fill(0)) + samples.push(p.getValue()) + prev = p + } + Array.prototype.push.apply(samples, + new Array(Math.max(0, sizeToBackfill(startMs, endMs, durationDeltaSec) - samples.length)) + .fill(0) + ) + timeline.samples = samples + return timeline +} + +/** + * + * @param points {[]} + * @param startMs {number} + * @param endMs {number} + */ +function boundPointsToWindow (points, startMs, endMs) { + const startIdx = points.findIndex((v) => v.getTimestamp() >= startMs) + const endIdx = points.findLastIndex((v) => v.getTimestamp() < endMs) + return points.slice(startIdx, endIdx + 1) +} + +/** + * + * @param startMs {number} + * @param endMs {number} + * @param stepSec {number} + * @returns {number} + */ +function sizeToBackfill (startMs, endMs, stepSec) { + return Math.floor((endMs - startMs) / (stepSec * 1000)) +} + +/** + * + * @param query {string} + */ +const parseQuery = (query) => { + query = query.trim() + const match = query.match(/^([^{\s]+)\s*(\{(.*)})?$/) + if (!match) { + return null + } + const typeId = match[1] + const typeDesc = parseTypeId(typeId) + let strLabels = (match[3] || '').trim() + const labels = [] + while (strLabels && strLabels !== '' && strLabels !== '}') { + const m = strLabels.match(/^(,)?\s*([A-Za-z0-9_]+)\s*(!=|!~|=~|=)\s*("([^"\\]|\\.)*")/) + if (!m) { + throw new Error('Invalid label selector') + } + labels.push([m[2], m[3], m[4]]) + strLabels = strLabels.substring(m[0].length).trim() + } + const profileType = new types.ProfileType() + profileType.setId(typeId) + profileType.setName(typeDesc.type) + profileType.setSampleType(typeDesc.sampleType) + profileType.setSampleUnit(typeDesc.sampleUnit) + profileType.setPeriodType(typeDesc.periodType) + profileType.setPeriodUnit(typeDesc.periodUnit) + return { + typeId, + typeDesc, + labels, + labelSelector: strLabels, + profileType + } +} + +const init = (fastify) => { + fastify.get('/pyroscope/render', render) +} + +module.exports = { + init, + parseQuery, + toFlamebearer +} diff --git a/pyroscope/render_diff.js b/pyroscope/render_diff.js new file mode 100644 index 00000000..c3c46c06 --- /dev/null +++ b/pyroscope/render_diff.js @@ -0,0 +1,75 @@ +const { parseQuery, toFlamebearer } = require('./render') +const { importStackTraces, newCtxIdx } = require('./merge_stack_traces') +const pprofBin = require('./pprof-bin/pkg') +const querierMessages = require('./querier_pb') +const types = require('./types/v1/types_pb') + +const renderDiff = async (req, res) => { + const [leftQuery, leftFromTimeSec, leftToTimeSec] = + parseParams(req.query.leftQuery, req.query.leftFrom, req.query.leftUntil); + const [rightQuery, rightFromTimeSec, rightToTimeSec] = + parseParams(req.query.rightQuery, req.query.rightFrom, req.query.rightUntil); + if (leftQuery.typeId != rightQuery.typeId) { + res.code(400).send('Different type IDs') + return + } + const leftCtxIdx = newCtxIdx() + await importStackTraces(leftQuery.typeDesc, '{' + leftQuery.labelSelector + '}', leftFromTimeSec, leftToTimeSec, req.log, leftCtxIdx, true) + const rightCtxIdx = newCtxIdx() + await importStackTraces(rightQuery.typeDesc, '{' + rightQuery.labelSelector + '}', rightFromTimeSec, rightToTimeSec, req.log, rightCtxIdx, true) + const flamegraphDiffBin = pprofBin.diff_tree(leftCtxIdx, rightCtxIdx, + `${leftQuery.typeDesc.sampleType}:${leftQuery.typeDesc.sampleUnit}`) + const profileType = new types.ProfileType() + profileType.setId(leftQuery.typeId) + profileType.setName(leftQuery.typeDesc.type) + profileType.setSampleType(leftQuery.typeDesc.sampleType) + profileType.setSampleUnit(leftQuery.typeDesc.sampleUnit) + profileType.setPeriodType(leftQuery.typeDesc.periodType) + profileType.setPeriodUnit(leftQuery.typeDesc.periodUnit) + const diff = querierMessages.FlameGraphDiff.deserializeBinary(flamegraphDiffBin) + return res.code(200).send(diffToFlamegraph(diff, profileType).flamebearerProfileV1) +} + +/** + * + * @param diff + * @param type + */ +const diffToFlamegraph = (diff, type) => { + const fg = new querierMessages.FlameGraph() + fg.setNamesList(diff.getNamesList()) + fg.setLevelsList(diff.getLevelsList()) + fg.setTotal(diff.getTotal()) + fg.setMaxSelf(diff.getMaxSelf()) + const fb = toFlamebearer(fg, type) + fb.flamebearerProfileV1.leftTicks = diff.getLeftticks() + fb.flamebearerProfileV1.rightTicks = diff.getRightticks() + fb.flamebearerProfileV1.metadata = { + ...(fb.flamebearerProfileV1.metadata || {}), + format: 'double' + } + return fb +} + +const parseParams = (query, from, until) => { + const parsedQuery = parseQuery(query) + const fromTimeSec = from + ? Math.floor(parseInt(from) / 1000) + : Math.floor((Date.now() - 1000 * 60 * 60 * 48) / 1000) + const toTimeSec = until + ? Math.floor(parseInt(until) / 1000) + : Math.floor((Date.now() - 1000 * 60 * 60 * 48) / 1000) + if (!parsedQuery) { + throw new Error('Invalid query') + } + return [parsedQuery, fromTimeSec, toTimeSec] +} + +const init = (fastify) => { + fastify.get('/pyroscope/render-diff', renderDiff) +} + +module.exports = { + renderDiff, + init +} diff --git a/pyroscope/select_series.js b/pyroscope/select_series.js new file mode 100644 index 00000000..0f406654 --- /dev/null +++ b/pyroscope/select_series.js @@ -0,0 +1,142 @@ +const { QrynBadRequest } = require('../lib/handlers/errors') +const { parseTypeId, serviceNameSelectorQuery, labelSelectorQuery } = require('./shared') +const { clusterName } = require('../common') +const Sql = require('@cloki/clickhouse-sql') +const { DATABASE_NAME } = require('../lib/utils') +const types = require('./types/v1/types_pb') +const clickhouse = require('../lib/db/clickhouse') +const messages = require('./querier_pb') + +const selectSeriesImpl = async (fromTimeSec, toTimeSec, payload) => { + const _req = payload + let typeID = _req.getProfileTypeid && _req.getProfileTypeid() + if (!typeID) { + throw new QrynBadRequest('No type provided') + } + typeID = parseTypeId(typeID) + if (!typeID) { + throw new QrynBadRequest('Invalid type provided') + } + const dist = clusterName ? '_dist' : '' + const sampleTypeId = typeID.sampleType + ':' + typeID.sampleUnit + const labelSelector = _req.getLabelSelector && _req.getLabelSelector() + let groupBy = _req.getGroupByList && _req.getGroupByList() + groupBy = groupBy && groupBy.length ? groupBy : null + const step = _req.getStep && parseInt(_req.getStep()) + if (!step || isNaN(step)) { + throw new QrynBadRequest('No step provided') + } + const aggregation = _req.getAggregation && _req.getAggregation() + + const typeIdSelector = Sql.Eq( + 'type_id', + Sql.val(`${typeID.type}:${typeID.periodType}:${typeID.periodUnit}`)) + const serviceNameSelector = serviceNameSelectorQuery(labelSelector) + + const idxReq = (new Sql.Select()) + .select(new Sql.Raw('fingerprint')) + .from(`${DATABASE_NAME()}.profiles_series_gin`) + .where( + Sql.And( + typeIdSelector, + serviceNameSelector, + Sql.Gte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(fromTimeSec)}))`)), + Sql.Lte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(toTimeSec)}))`)), + Sql.Eq(new Sql.Raw( + `has(sample_types_units, (${Sql.quoteVal(typeID.sampleType)}, ${Sql.quoteVal(typeID.sampleUnit)}))`), + 1) + ) + ) + labelSelectorQuery(idxReq, labelSelector) + + const withIdxReq = (new Sql.With('idx', idxReq, !!clusterName)) + + let tagsReq = 'arraySort(p.tags)' + if (groupBy) { + tagsReq = `arraySort(arrayFilter(x -> x.1 in (${groupBy.map(g => Sql.quoteVal(g)).join(',')}), p.tags))` + } + + const labelsReq = (new Sql.Select()).with(withIdxReq).select( + 'fingerprint', + [new Sql.Raw(tagsReq), 'tags'], + [groupBy ? new Sql.Raw('cityHash64(tags)') : 'fingerprint', 'new_fingerprint'] + ).distinct(true).from([`${DATABASE_NAME()}.profiles_series`, 'p']) + .where(Sql.And( + new Sql.In('fingerprint', 'IN', new Sql.WithReference(withIdxReq)), + Sql.Gte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(fromTimeSec)}))`)), + Sql.Lte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(toTimeSec)}))`)), + typeIdSelector, + serviceNameSelector + )) + + const withLabelsReq = new Sql.With('labels', labelsReq, !!clusterName) + + let valueCol = new Sql.Raw( + `sum(toFloat64(arrayFirst(x -> x.1 == ${Sql.quoteVal(sampleTypeId)}, p.values_agg).2))`) + if (aggregation === types.TimeSeriesAggregationType.TIME_SERIES_AGGREGATION_TYPE_AVERAGE) { + valueCol = new Sql.Raw( + `sum(toFloat64(arrayFirst(x -> x.1 == ${Sql.quoteVal(sampleTypeId)}).2, p.values_agg)) / ` + + `sum(toFloat64(arrayFirst(x -> x.1 == ${Sql.quoteVal(sampleTypeId)}).3, p.values_agg))` + ) + } + + const mainReq = (new Sql.Select()).with(withIdxReq, withLabelsReq).select( + [new Sql.Raw(`intDiv(p.timestamp_ns, 1000000000 * ${step}) * ${step} * 1000`), 'timestamp_ms'], + [new Sql.Raw('labels.new_fingerprint'), 'fingerprint'], + [new Sql.Raw('min(labels.tags)'), 'labels'], + [valueCol, 'value'] + ).from([`${DATABASE_NAME()}.profiles${dist}`, 'p']).join( + [new Sql.WithReference(withLabelsReq), 'labels'], + 'ANY LEFT', + Sql.Eq(new Sql.Raw('p.fingerprint'), new Sql.Raw('labels.fingerprint')) + ).where( + Sql.And( + new Sql.In('p.fingerprint', 'IN', new Sql.WithReference(withIdxReq)), + Sql.Gte('p.timestamp_ns', new Sql.Raw(`${fromTimeSec}000000000`)), + Sql.Lt('p.timestamp_ns', new Sql.Raw(`${toTimeSec}000000000`)), + typeIdSelector, + serviceNameSelector + ) + ).groupBy('timestamp_ms', 'fingerprint') + .orderBy(['fingerprint', 'ASC'], ['timestamp_ms', 'ASC']) + const strMainReq = mainReq.toString() + const chRes = await clickhouse + .rawRequest(strMainReq + ' FORMAT JSON', null, DATABASE_NAME()) + + let lastFingerprint = null + const seriesList = [] + let lastSeries = null + let lastPoints = [] + for (let i = 0; i < chRes.data.data.length; i++) { + const e = chRes.data.data[i] + if (lastFingerprint !== e.fingerprint) { + lastFingerprint = e.fingerprint + lastSeries && lastSeries.setPointsList(lastPoints) + lastSeries && seriesList.push(lastSeries) + lastPoints = [] + lastSeries = new types.Series() + lastSeries.setLabelsList(e.labels.map(l => { + const lp = new types.LabelPair() + lp.setName(l[0]) + lp.setValue(l[1]) + return lp + })) + } + + const p = new types.Point() + p.setValue(e.value) + p.setTimestamp(e.timestamp_ms) + lastPoints.push(p) + } + lastSeries && lastSeries.setPointsList(lastPoints) + lastSeries && seriesList.push(lastSeries) + + const resp = new messages.SelectSeriesResponse() + resp.setSeriesList(seriesList) + console.log(`Queried ${seriesList.length} series`) + return resp +} + +module.exports = { + selectSeriesImpl +} diff --git a/pyroscope/settings.js b/pyroscope/settings.js new file mode 100644 index 00000000..98b5dc42 --- /dev/null +++ b/pyroscope/settings.js @@ -0,0 +1,31 @@ +const messages = require('./settings_pb') +const services = require('./settings_grpc_pb') +const { parser, wrapResponse } = require('./shared') +const parsers = require('./json_parsers') + +const get = (req, res) => { + const _res = new messages.GetSettingsResponse() + const s = new messages.Setting() + s.setName('pluginSettings') + s.setValue('{}') + s.setModifiedat(Date.now()) + _res.setSettingsList([s]) + return _res +} + +module.exports.init = (fastify) => { + const fns = { + get: get + } + const jsonParsers = { + get: parsers.settingsGet + } + for (const name of Object.keys(fns)) { + fastify.post(services.SettingsServiceService[name].path, (req, res) => { + return wrapResponse(fns[name])(req, res) + }, { + 'application/json': jsonParsers[name], + '*': parser(services.SettingsServiceService[name].requestType) + }) + } +} diff --git a/pyroscope/settings_grpc_pb.js b/pyroscope/settings_grpc_pb.js new file mode 100644 index 00000000..0797ea17 --- /dev/null +++ b/pyroscope/settings_grpc_pb.js @@ -0,0 +1,77 @@ +// GENERATED CODE -- DO NOT EDIT! + +'use strict'; +var grpc = require('@grpc/grpc-js'); +var settings_pb = require('./settings_pb.js'); + +function serialize_settings_v1_GetSettingsRequest(arg) { + if (!(arg instanceof settings_pb.GetSettingsRequest)) { + throw new Error('Expected argument of type settings.v1.GetSettingsRequest'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_settings_v1_GetSettingsRequest(buffer_arg) { + return settings_pb.GetSettingsRequest.deserializeBinary(new Uint8Array(buffer_arg)); +} + +function serialize_settings_v1_GetSettingsResponse(arg) { + if (!(arg instanceof settings_pb.GetSettingsResponse)) { + throw new Error('Expected argument of type settings.v1.GetSettingsResponse'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_settings_v1_GetSettingsResponse(buffer_arg) { + return settings_pb.GetSettingsResponse.deserializeBinary(new Uint8Array(buffer_arg)); +} + +function serialize_settings_v1_SetSettingsRequest(arg) { + if (!(arg instanceof settings_pb.SetSettingsRequest)) { + throw new Error('Expected argument of type settings.v1.SetSettingsRequest'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_settings_v1_SetSettingsRequest(buffer_arg) { + return settings_pb.SetSettingsRequest.deserializeBinary(new Uint8Array(buffer_arg)); +} + +function serialize_settings_v1_SetSettingsResponse(arg) { + if (!(arg instanceof settings_pb.SetSettingsResponse)) { + throw new Error('Expected argument of type settings.v1.SetSettingsResponse'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_settings_v1_SetSettingsResponse(buffer_arg) { + return settings_pb.SetSettingsResponse.deserializeBinary(new Uint8Array(buffer_arg)); +} + + +var SettingsServiceService = exports.SettingsServiceService = { + get: { + path: '/settings.v1.SettingsService/Get', + requestStream: false, + responseStream: false, + requestType: settings_pb.GetSettingsRequest, + responseType: settings_pb.GetSettingsResponse, + requestSerialize: serialize_settings_v1_GetSettingsRequest, + requestDeserialize: deserialize_settings_v1_GetSettingsRequest, + responseSerialize: serialize_settings_v1_GetSettingsResponse, + responseDeserialize: deserialize_settings_v1_GetSettingsResponse, + }, + set: { + path: '/settings.v1.SettingsService/Set', + requestStream: false, + responseStream: false, + requestType: settings_pb.SetSettingsRequest, + responseType: settings_pb.SetSettingsResponse, + requestSerialize: serialize_settings_v1_SetSettingsRequest, + requestDeserialize: deserialize_settings_v1_SetSettingsRequest, + responseSerialize: serialize_settings_v1_SetSettingsResponse, + responseDeserialize: deserialize_settings_v1_SetSettingsResponse, + }, +}; + +exports.SettingsServiceClient = grpc.makeGenericClientConstructor(SettingsServiceService); diff --git a/pyroscope/settings_pb.js b/pyroscope/settings_pb.js new file mode 100644 index 00000000..c8a073e7 --- /dev/null +++ b/pyroscope/settings_pb.js @@ -0,0 +1,887 @@ +// source: settings.proto +/** + * @fileoverview + * @enhanceable + * @suppress {missingRequire} reports error on implicit type usages. + * @suppress {messageConventions} JS Compiler reports an error if a variable or + * field starts with 'MSG_' and isn't a translatable message. + * @public + */ +// GENERATED CODE -- DO NOT EDIT! +/* eslint-disable */ +// @ts-nocheck + +var jspb = require('google-protobuf'); +var goog = jspb; +var global = (function() { + if (this) { return this; } + if (typeof window !== 'undefined') { return window; } + if (typeof global !== 'undefined') { return global; } + if (typeof self !== 'undefined') { return self; } + return Function('return this')(); +}.call(null)); + +goog.exportSymbol('proto.settings.v1.GetSettingsRequest', null, global); +goog.exportSymbol('proto.settings.v1.GetSettingsResponse', null, global); +goog.exportSymbol('proto.settings.v1.SetSettingsRequest', null, global); +goog.exportSymbol('proto.settings.v1.SetSettingsResponse', null, global); +goog.exportSymbol('proto.settings.v1.Setting', null, global); +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.settings.v1.GetSettingsRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.settings.v1.GetSettingsRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.settings.v1.GetSettingsRequest.displayName = 'proto.settings.v1.GetSettingsRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.settings.v1.GetSettingsResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.settings.v1.GetSettingsResponse.repeatedFields_, null); +}; +goog.inherits(proto.settings.v1.GetSettingsResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.settings.v1.GetSettingsResponse.displayName = 'proto.settings.v1.GetSettingsResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.settings.v1.SetSettingsRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.settings.v1.SetSettingsRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.settings.v1.SetSettingsRequest.displayName = 'proto.settings.v1.SetSettingsRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.settings.v1.SetSettingsResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.settings.v1.SetSettingsResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.settings.v1.SetSettingsResponse.displayName = 'proto.settings.v1.SetSettingsResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.settings.v1.Setting = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.settings.v1.Setting, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.settings.v1.Setting.displayName = 'proto.settings.v1.Setting'; +} + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.settings.v1.GetSettingsRequest.prototype.toObject = function(opt_includeInstance) { + return proto.settings.v1.GetSettingsRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.settings.v1.GetSettingsRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.settings.v1.GetSettingsRequest.toObject = function(includeInstance, msg) { + var f, obj = { + + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.settings.v1.GetSettingsRequest} + */ +proto.settings.v1.GetSettingsRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.settings.v1.GetSettingsRequest; + return proto.settings.v1.GetSettingsRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.settings.v1.GetSettingsRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.settings.v1.GetSettingsRequest} + */ +proto.settings.v1.GetSettingsRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.settings.v1.GetSettingsRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.settings.v1.GetSettingsRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.settings.v1.GetSettingsRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.settings.v1.GetSettingsRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; +}; + + + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.settings.v1.GetSettingsResponse.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.settings.v1.GetSettingsResponse.prototype.toObject = function(opt_includeInstance) { + return proto.settings.v1.GetSettingsResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.settings.v1.GetSettingsResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.settings.v1.GetSettingsResponse.toObject = function(includeInstance, msg) { + var f, obj = { + settingsList: jspb.Message.toObjectList(msg.getSettingsList(), + proto.settings.v1.Setting.toObject, includeInstance) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.settings.v1.GetSettingsResponse} + */ +proto.settings.v1.GetSettingsResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.settings.v1.GetSettingsResponse; + return proto.settings.v1.GetSettingsResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.settings.v1.GetSettingsResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.settings.v1.GetSettingsResponse} + */ +proto.settings.v1.GetSettingsResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new proto.settings.v1.Setting; + reader.readMessage(value,proto.settings.v1.Setting.deserializeBinaryFromReader); + msg.addSettings(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.settings.v1.GetSettingsResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.settings.v1.GetSettingsResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.settings.v1.GetSettingsResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.settings.v1.GetSettingsResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getSettingsList(); + if (f.length > 0) { + writer.writeRepeatedMessage( + 1, + f, + proto.settings.v1.Setting.serializeBinaryToWriter + ); + } +}; + + +/** + * repeated Setting settings = 1; + * @return {!Array} + */ +proto.settings.v1.GetSettingsResponse.prototype.getSettingsList = function() { + return /** @type{!Array} */ ( + jspb.Message.getRepeatedWrapperField(this, proto.settings.v1.Setting, 1)); +}; + + +/** + * @param {!Array} value + * @return {!proto.settings.v1.GetSettingsResponse} returns this +*/ +proto.settings.v1.GetSettingsResponse.prototype.setSettingsList = function(value) { + return jspb.Message.setRepeatedWrapperField(this, 1, value); +}; + + +/** + * @param {!proto.settings.v1.Setting=} opt_value + * @param {number=} opt_index + * @return {!proto.settings.v1.Setting} + */ +proto.settings.v1.GetSettingsResponse.prototype.addSettings = function(opt_value, opt_index) { + return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.settings.v1.Setting, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.settings.v1.GetSettingsResponse} returns this + */ +proto.settings.v1.GetSettingsResponse.prototype.clearSettingsList = function() { + return this.setSettingsList([]); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.settings.v1.SetSettingsRequest.prototype.toObject = function(opt_includeInstance) { + return proto.settings.v1.SetSettingsRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.settings.v1.SetSettingsRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.settings.v1.SetSettingsRequest.toObject = function(includeInstance, msg) { + var f, obj = { + setting: (f = msg.getSetting()) && proto.settings.v1.Setting.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.settings.v1.SetSettingsRequest} + */ +proto.settings.v1.SetSettingsRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.settings.v1.SetSettingsRequest; + return proto.settings.v1.SetSettingsRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.settings.v1.SetSettingsRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.settings.v1.SetSettingsRequest} + */ +proto.settings.v1.SetSettingsRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new proto.settings.v1.Setting; + reader.readMessage(value,proto.settings.v1.Setting.deserializeBinaryFromReader); + msg.setSetting(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.settings.v1.SetSettingsRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.settings.v1.SetSettingsRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.settings.v1.SetSettingsRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.settings.v1.SetSettingsRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getSetting(); + if (f != null) { + writer.writeMessage( + 1, + f, + proto.settings.v1.Setting.serializeBinaryToWriter + ); + } +}; + + +/** + * optional Setting setting = 1; + * @return {?proto.settings.v1.Setting} + */ +proto.settings.v1.SetSettingsRequest.prototype.getSetting = function() { + return /** @type{?proto.settings.v1.Setting} */ ( + jspb.Message.getWrapperField(this, proto.settings.v1.Setting, 1)); +}; + + +/** + * @param {?proto.settings.v1.Setting|undefined} value + * @return {!proto.settings.v1.SetSettingsRequest} returns this +*/ +proto.settings.v1.SetSettingsRequest.prototype.setSetting = function(value) { + return jspb.Message.setWrapperField(this, 1, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.settings.v1.SetSettingsRequest} returns this + */ +proto.settings.v1.SetSettingsRequest.prototype.clearSetting = function() { + return this.setSetting(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.settings.v1.SetSettingsRequest.prototype.hasSetting = function() { + return jspb.Message.getField(this, 1) != null; +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.settings.v1.SetSettingsResponse.prototype.toObject = function(opt_includeInstance) { + return proto.settings.v1.SetSettingsResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.settings.v1.SetSettingsResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.settings.v1.SetSettingsResponse.toObject = function(includeInstance, msg) { + var f, obj = { + setting: (f = msg.getSetting()) && proto.settings.v1.Setting.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.settings.v1.SetSettingsResponse} + */ +proto.settings.v1.SetSettingsResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.settings.v1.SetSettingsResponse; + return proto.settings.v1.SetSettingsResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.settings.v1.SetSettingsResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.settings.v1.SetSettingsResponse} + */ +proto.settings.v1.SetSettingsResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new proto.settings.v1.Setting; + reader.readMessage(value,proto.settings.v1.Setting.deserializeBinaryFromReader); + msg.setSetting(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.settings.v1.SetSettingsResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.settings.v1.SetSettingsResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.settings.v1.SetSettingsResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.settings.v1.SetSettingsResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getSetting(); + if (f != null) { + writer.writeMessage( + 1, + f, + proto.settings.v1.Setting.serializeBinaryToWriter + ); + } +}; + + +/** + * optional Setting setting = 1; + * @return {?proto.settings.v1.Setting} + */ +proto.settings.v1.SetSettingsResponse.prototype.getSetting = function() { + return /** @type{?proto.settings.v1.Setting} */ ( + jspb.Message.getWrapperField(this, proto.settings.v1.Setting, 1)); +}; + + +/** + * @param {?proto.settings.v1.Setting|undefined} value + * @return {!proto.settings.v1.SetSettingsResponse} returns this +*/ +proto.settings.v1.SetSettingsResponse.prototype.setSetting = function(value) { + return jspb.Message.setWrapperField(this, 1, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.settings.v1.SetSettingsResponse} returns this + */ +proto.settings.v1.SetSettingsResponse.prototype.clearSetting = function() { + return this.setSetting(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.settings.v1.SetSettingsResponse.prototype.hasSetting = function() { + return jspb.Message.getField(this, 1) != null; +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.settings.v1.Setting.prototype.toObject = function(opt_includeInstance) { + return proto.settings.v1.Setting.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.settings.v1.Setting} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.settings.v1.Setting.toObject = function(includeInstance, msg) { + var f, obj = { + name: jspb.Message.getFieldWithDefault(msg, 1, ""), + value: jspb.Message.getFieldWithDefault(msg, 2, ""), + modifiedat: jspb.Message.getFieldWithDefault(msg, 3, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.settings.v1.Setting} + */ +proto.settings.v1.Setting.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.settings.v1.Setting; + return proto.settings.v1.Setting.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.settings.v1.Setting} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.settings.v1.Setting} + */ +proto.settings.v1.Setting.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setName(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.setValue(value); + break; + case 3: + var value = /** @type {number} */ (reader.readInt64()); + msg.setModifiedat(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.settings.v1.Setting.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.settings.v1.Setting.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.settings.v1.Setting} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.settings.v1.Setting.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getName(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getValue(); + if (f.length > 0) { + writer.writeString( + 2, + f + ); + } + f = message.getModifiedat(); + if (f !== 0) { + writer.writeInt64( + 3, + f + ); + } +}; + + +/** + * optional string name = 1; + * @return {string} + */ +proto.settings.v1.Setting.prototype.getName = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.settings.v1.Setting} returns this + */ +proto.settings.v1.Setting.prototype.setName = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional string value = 2; + * @return {string} + */ +proto.settings.v1.Setting.prototype.getValue = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * @param {string} value + * @return {!proto.settings.v1.Setting} returns this + */ +proto.settings.v1.Setting.prototype.setValue = function(value) { + return jspb.Message.setProto3StringField(this, 2, value); +}; + + +/** + * optional int64 modifiedAt = 3; + * @return {number} + */ +proto.settings.v1.Setting.prototype.getModifiedat = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.settings.v1.Setting} returns this + */ +proto.settings.v1.Setting.prototype.setModifiedat = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + +goog.object.extend(exports, proto.settings.v1); diff --git a/pyroscope/shared.js b/pyroscope/shared.js new file mode 100644 index 00000000..fcb45966 --- /dev/null +++ b/pyroscope/shared.js @@ -0,0 +1,187 @@ +const { QrynBadRequest } = require('../lib/handlers/errors') +const Sql = require('@cloki/clickhouse-sql') +const compiler = require('../parser/bnf') +/** + * + * @param payload {ReadableStream} + * @returns {Promise} + */ +const bufferize = async (payload) => { + const _body = [] + payload.on('data', data => { + _body.push(data)// += data.toString() + }) + if (payload.isPaused && payload.isPaused()) { + payload.resume() + } + await new Promise(resolve => { + payload.on('end', resolve) + payload.on('close', resolve) + }) + const body = Buffer.concat(_body) + if (body.length === 0) { + return null + } + return body +} + +const parser = (MsgClass) => { + return async (req, payload) => { + const body = await bufferize(payload) + req._rawBody = body + return MsgClass.deserializeBinary(body) + } +} + +/** + * + * @param proto {Object} + */ +const normalizeProtoResponse = (proto) => { + if (typeof proto !== 'object') { + return proto + } + return Object.fromEntries(Object.entries(proto).map((e) => { + let name = e[0] + if (name.endsWith('List')) { + name = name.slice(0, -4) + } + if (Array.isArray(e[1])) { + return [name, e[1].map(normalizeProtoResponse)] + } + if (typeof e[1] === 'object') { + return [name, normalizeProtoResponse(e[1])] + } + return [name, e[1]] + })) +} + +const wrapResponse = (hndl) => { + return async (req, res) => { + const _res = await hndl(req, res) + if (!_res || !_res.serializeBinary) { + return _res + } + if (req.type === 'json') { + const strRes = JSON.stringify(normalizeProtoResponse(_res.toObject())) + return res.code(200).send(strRes) + } + return res.code(200).send(Buffer.from(_res.serializeBinary())) + } +} + +const serviceNameSelectorQuery = (labelSelector) => { + const empty = Sql.Eq(new Sql.Raw('1'), new Sql.Raw('1')) + if (!labelSelector || !labelSelector.length || labelSelector === '{}') { + return empty + } + const labelSelectorScript = parseLabelSelector(labelSelector) + let conds = null + for (const rule of labelSelectorScript.Children('log_stream_selector_rule')) { + const label = rule.Child('label').value + if (label !== 'service_name') { + continue + } + const val = JSON.parse(rule.Child('quoted_str').value) + let valRul = null + switch (rule.Child('operator').value) { + case '=': + valRul = Sql.Eq(new Sql.Raw('service_name'), Sql.val(val)) + break + case '!=': + valRul = Sql.Ne(new Sql.Raw('service_name'), Sql.val(val)) + break + case '=~': + valRul = Sql.Eq(new Sql.Raw(`match(service_name, ${Sql.quoteVal(val)})`), 1) + break + case '!~': + valRul = Sql.Ne(new Sql.Raw(`match(service_name, ${Sql.quoteVal(val)})`), 1) + } + conds = valRul + } + return conds || empty +} + + +const parseLabelSelector = (labelSelector) => { + if (labelSelector.endsWith(',}')) { + labelSelector = labelSelector.slice(0, -2) + '}' + } + return compiler.ParseScript(labelSelector).rootToken +} + +/** + * + * @param typeId {string} + */ +const parseTypeId = (typeId) => { + const typeParts = typeId.match(/^([^:]+):([^:]+):([^:]+):([^:]+):([^:]+)$/) + if (!typeParts) { + throw new QrynBadRequest('invalid type id') + } + return { + type: typeParts[1], + sampleType: typeParts[2], + sampleUnit: typeParts[3], + periodType: typeParts[4], + periodUnit: typeParts[5] + } +} + + +/** + * + * @param {Sql.Select} query + * @param {string} labelSelector + */ +const labelSelectorQuery = (query, labelSelector) => { + if (!labelSelector || !labelSelector.length || labelSelector === '{}') { + return query + } + const labelSelectorScript = parseLabelSelector(labelSelector) + const labelsConds = [] + for (const rule of labelSelectorScript.Children('log_stream_selector_rule')) { + const val = JSON.parse(rule.Child('quoted_str').value) + let valRul = null + switch (rule.Child('operator').value) { + case '=': + valRul = Sql.Eq(new Sql.Raw('val'), Sql.val(val)) + break + case '!=': + valRul = Sql.Ne(new Sql.Raw('val'), Sql.val(val)) + break + case '=~': + valRul = Sql.Eq(new Sql.Raw(`match(val, ${Sql.quoteVal(val)})`), 1) + break + case '!~': + valRul = Sql.Ne(new Sql.Raw(`match(val, ${Sql.quoteVal(val)})`), 1) + } + const labelSubCond = Sql.And( + Sql.Eq('key', Sql.val(rule.Child('label').value)), + valRul + ) + labelsConds.push(labelSubCond) + } + query.where(Sql.Or(...labelsConds)) + query.groupBy(new Sql.Raw('fingerprint')) + query.having(Sql.Eq( + new Sql.Raw(`groupBitOr(${labelsConds.map((cond, i) => { + return `bitShiftLeft(toUInt64(${cond}), ${i})` + }).join('+')})`), + new Sql.Raw(`bitShiftLeft(toUInt64(1), ${labelsConds.length})-1`) + )) +} + +const HISTORY_TIMESPAN = 1000 * 60 * 60 * 24 * 7 + +module.exports = { + bufferize, + parser, + normalizeProtoResponse, + wrapResponse, + parseTypeId, + serviceNameSelectorQuery, + parseLabelSelector, + labelSelectorQuery, + HISTORY_TIMESPAN +} diff --git a/pyroscope/types/v1/types_pb.js b/pyroscope/types/v1/types_pb.js index d100d698..b24be923 100644 --- a/pyroscope/types/v1/types_pb.js +++ b/pyroscope/types/v1/types_pb.js @@ -23,6 +23,9 @@ var global = (function() { goog.exportSymbol('proto.types.v1.BlockCompaction', null, global); goog.exportSymbol('proto.types.v1.BlockInfo', null, global); +goog.exportSymbol('proto.types.v1.GetProfileStatsRequest', null, global); +goog.exportSymbol('proto.types.v1.GetProfileStatsResponse', null, global); +goog.exportSymbol('proto.types.v1.GoPGO', null, global); goog.exportSymbol('proto.types.v1.LabelNamesRequest', null, global); goog.exportSymbol('proto.types.v1.LabelNamesResponse', null, global); goog.exportSymbol('proto.types.v1.LabelPair', null, global); @@ -308,6 +311,69 @@ if (goog.DEBUG && !COMPILED) { */ proto.types.v1.Location.displayName = 'proto.types.v1.Location'; } +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.types.v1.GoPGO = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.types.v1.GoPGO, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.types.v1.GoPGO.displayName = 'proto.types.v1.GoPGO'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.types.v1.GetProfileStatsRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.types.v1.GetProfileStatsRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.types.v1.GetProfileStatsRequest.displayName = 'proto.types.v1.GetProfileStatsRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.types.v1.GetProfileStatsResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.types.v1.GetProfileStatsResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.types.v1.GetProfileStatsResponse.displayName = 'proto.types.v1.GetProfileStatsResponse'; +} @@ -2630,8 +2696,9 @@ proto.types.v1.StackTraceSelector.prototype.toObject = function(opt_includeInsta */ proto.types.v1.StackTraceSelector.toObject = function(includeInstance, msg) { var f, obj = { - stackTraceList: jspb.Message.toObjectList(msg.getStackTraceList(), - proto.types.v1.Location.toObject, includeInstance) + callSiteList: jspb.Message.toObjectList(msg.getCallSiteList(), + proto.types.v1.Location.toObject, includeInstance), + goPgo: (f = msg.getGoPgo()) && proto.types.v1.GoPGO.toObject(includeInstance, f) }; if (includeInstance) { @@ -2671,7 +2738,12 @@ proto.types.v1.StackTraceSelector.deserializeBinaryFromReader = function(msg, re case 1: var value = new proto.types.v1.Location; reader.readMessage(value,proto.types.v1.Location.deserializeBinaryFromReader); - msg.addStackTrace(value); + msg.addCallSite(value); + break; + case 2: + var value = new proto.types.v1.GoPGO; + reader.readMessage(value,proto.types.v1.GoPGO.deserializeBinaryFromReader); + msg.setGoPgo(value); break; default: reader.skipField(); @@ -2702,7 +2774,7 @@ proto.types.v1.StackTraceSelector.prototype.serializeBinary = function() { */ proto.types.v1.StackTraceSelector.serializeBinaryToWriter = function(message, writer) { var f = undefined; - f = message.getStackTraceList(); + f = message.getCallSiteList(); if (f.length > 0) { writer.writeRepeatedMessage( 1, @@ -2710,14 +2782,22 @@ proto.types.v1.StackTraceSelector.serializeBinaryToWriter = function(message, wr proto.types.v1.Location.serializeBinaryToWriter ); } + f = message.getGoPgo(); + if (f != null) { + writer.writeMessage( + 2, + f, + proto.types.v1.GoPGO.serializeBinaryToWriter + ); + } }; /** - * repeated Location stack_trace = 1; + * repeated Location call_site = 1; * @return {!Array} */ -proto.types.v1.StackTraceSelector.prototype.getStackTraceList = function() { +proto.types.v1.StackTraceSelector.prototype.getCallSiteList = function() { return /** @type{!Array} */ ( jspb.Message.getRepeatedWrapperField(this, proto.types.v1.Location, 1)); }; @@ -2727,7 +2807,7 @@ proto.types.v1.StackTraceSelector.prototype.getStackTraceList = function() { * @param {!Array} value * @return {!proto.types.v1.StackTraceSelector} returns this */ -proto.types.v1.StackTraceSelector.prototype.setStackTraceList = function(value) { +proto.types.v1.StackTraceSelector.prototype.setCallSiteList = function(value) { return jspb.Message.setRepeatedWrapperField(this, 1, value); }; @@ -2737,7 +2817,7 @@ proto.types.v1.StackTraceSelector.prototype.setStackTraceList = function(value) * @param {number=} opt_index * @return {!proto.types.v1.Location} */ -proto.types.v1.StackTraceSelector.prototype.addStackTrace = function(opt_value, opt_index) { +proto.types.v1.StackTraceSelector.prototype.addCallSite = function(opt_value, opt_index) { return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.types.v1.Location, opt_index); }; @@ -2746,8 +2826,45 @@ proto.types.v1.StackTraceSelector.prototype.addStackTrace = function(opt_value, * Clears the list making it empty but non-null. * @return {!proto.types.v1.StackTraceSelector} returns this */ -proto.types.v1.StackTraceSelector.prototype.clearStackTraceList = function() { - return this.setStackTraceList([]); +proto.types.v1.StackTraceSelector.prototype.clearCallSiteList = function() { + return this.setCallSiteList([]); +}; + + +/** + * optional GoPGO go_pgo = 2; + * @return {?proto.types.v1.GoPGO} + */ +proto.types.v1.StackTraceSelector.prototype.getGoPgo = function() { + return /** @type{?proto.types.v1.GoPGO} */ ( + jspb.Message.getWrapperField(this, proto.types.v1.GoPGO, 2)); +}; + + +/** + * @param {?proto.types.v1.GoPGO|undefined} value + * @return {!proto.types.v1.StackTraceSelector} returns this +*/ +proto.types.v1.StackTraceSelector.prototype.setGoPgo = function(value) { + return jspb.Message.setWrapperField(this, 2, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.types.v1.StackTraceSelector} returns this + */ +proto.types.v1.StackTraceSelector.prototype.clearGoPgo = function() { + return this.setGoPgo(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.types.v1.StackTraceSelector.prototype.hasGoPgo = function() { + return jspb.Message.getField(this, 2) != null; }; @@ -2881,6 +2998,457 @@ proto.types.v1.Location.prototype.setName = function(value) { }; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.types.v1.GoPGO.prototype.toObject = function(opt_includeInstance) { + return proto.types.v1.GoPGO.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.types.v1.GoPGO} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.types.v1.GoPGO.toObject = function(includeInstance, msg) { + var f, obj = { + keepLocations: jspb.Message.getFieldWithDefault(msg, 1, 0), + aggregateCallees: jspb.Message.getBooleanFieldWithDefault(msg, 2, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.types.v1.GoPGO} + */ +proto.types.v1.GoPGO.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.types.v1.GoPGO; + return proto.types.v1.GoPGO.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.types.v1.GoPGO} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.types.v1.GoPGO} + */ +proto.types.v1.GoPGO.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readUint32()); + msg.setKeepLocations(value); + break; + case 2: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setAggregateCallees(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.types.v1.GoPGO.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.types.v1.GoPGO.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.types.v1.GoPGO} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.types.v1.GoPGO.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getKeepLocations(); + if (f !== 0) { + writer.writeUint32( + 1, + f + ); + } + f = message.getAggregateCallees(); + if (f) { + writer.writeBool( + 2, + f + ); + } +}; + + +/** + * optional uint32 keep_locations = 1; + * @return {number} + */ +proto.types.v1.GoPGO.prototype.getKeepLocations = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.types.v1.GoPGO} returns this + */ +proto.types.v1.GoPGO.prototype.setKeepLocations = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + +/** + * optional bool aggregate_callees = 2; + * @return {boolean} + */ +proto.types.v1.GoPGO.prototype.getAggregateCallees = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 2, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.types.v1.GoPGO} returns this + */ +proto.types.v1.GoPGO.prototype.setAggregateCallees = function(value) { + return jspb.Message.setProto3BooleanField(this, 2, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.types.v1.GetProfileStatsRequest.prototype.toObject = function(opt_includeInstance) { + return proto.types.v1.GetProfileStatsRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.types.v1.GetProfileStatsRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.types.v1.GetProfileStatsRequest.toObject = function(includeInstance, msg) { + var f, obj = { + + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.types.v1.GetProfileStatsRequest} + */ +proto.types.v1.GetProfileStatsRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.types.v1.GetProfileStatsRequest; + return proto.types.v1.GetProfileStatsRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.types.v1.GetProfileStatsRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.types.v1.GetProfileStatsRequest} + */ +proto.types.v1.GetProfileStatsRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.types.v1.GetProfileStatsRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.types.v1.GetProfileStatsRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.types.v1.GetProfileStatsRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.types.v1.GetProfileStatsRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.types.v1.GetProfileStatsResponse.prototype.toObject = function(opt_includeInstance) { + return proto.types.v1.GetProfileStatsResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.types.v1.GetProfileStatsResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.types.v1.GetProfileStatsResponse.toObject = function(includeInstance, msg) { + var f, obj = { + dataIngested: jspb.Message.getBooleanFieldWithDefault(msg, 1, false), + oldestProfileTime: jspb.Message.getFieldWithDefault(msg, 2, 0), + newestProfileTime: jspb.Message.getFieldWithDefault(msg, 3, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.types.v1.GetProfileStatsResponse} + */ +proto.types.v1.GetProfileStatsResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.types.v1.GetProfileStatsResponse; + return proto.types.v1.GetProfileStatsResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.types.v1.GetProfileStatsResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.types.v1.GetProfileStatsResponse} + */ +proto.types.v1.GetProfileStatsResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setDataIngested(value); + break; + case 2: + var value = /** @type {number} */ (reader.readInt64()); + msg.setOldestProfileTime(value); + break; + case 3: + var value = /** @type {number} */ (reader.readInt64()); + msg.setNewestProfileTime(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.types.v1.GetProfileStatsResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.types.v1.GetProfileStatsResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.types.v1.GetProfileStatsResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.types.v1.GetProfileStatsResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getDataIngested(); + if (f) { + writer.writeBool( + 1, + f + ); + } + f = message.getOldestProfileTime(); + if (f !== 0) { + writer.writeInt64( + 2, + f + ); + } + f = message.getNewestProfileTime(); + if (f !== 0) { + writer.writeInt64( + 3, + f + ); + } +}; + + +/** + * optional bool data_ingested = 1; + * @return {boolean} + */ +proto.types.v1.GetProfileStatsResponse.prototype.getDataIngested = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 1, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.types.v1.GetProfileStatsResponse} returns this + */ +proto.types.v1.GetProfileStatsResponse.prototype.setDataIngested = function(value) { + return jspb.Message.setProto3BooleanField(this, 1, value); +}; + + +/** + * optional int64 oldest_profile_time = 2; + * @return {number} + */ +proto.types.v1.GetProfileStatsResponse.prototype.getOldestProfileTime = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.types.v1.GetProfileStatsResponse} returns this + */ +proto.types.v1.GetProfileStatsResponse.prototype.setOldestProfileTime = function(value) { + return jspb.Message.setProto3IntField(this, 2, value); +}; + + +/** + * optional int64 newest_profile_time = 3; + * @return {number} + */ +proto.types.v1.GetProfileStatsResponse.prototype.getNewestProfileTime = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.types.v1.GetProfileStatsResponse} returns this + */ +proto.types.v1.GetProfileStatsResponse.prototype.setNewestProfileTime = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + /** * @enum {number} */