make error propagation modal

This commit is contained in:
Jonathan Shook 2020-08-28 12:36:20 -05:00
parent 4c940ec73b
commit 428ffe718f
31 changed files with 1020 additions and 635 deletions

View File

@ -78,7 +78,7 @@ export default {
});
},
commitWorkspace: function ({$store}) {
console.log("commit:" + JSON.stringify(this.new_workspace));
// console.log("commit:" + JSON.stringify(this.new_workspace));
this.$store.dispatch("workspaces/activateWorkspace", this.new_workspace);
this.new_workspace = "";
this.mode = "showing";

View File

@ -45,7 +45,7 @@ export default {
}
let result={namespaces: collated};
console.log("namespaces result:"+JSON.stringify(result));
// console.log("namespaces result:"+JSON.stringify(result));
return result;
}

View File

@ -5,7 +5,7 @@
"author": "Sebastian Estevez & Jonathan Shook",
"private": true,
"scripts": {
"dev": "nuxt -c nuxt.config.dev.js",
"dev": "nuxt -c nuxt.config.dev.js --port 3003",
"build": "nuxt build",
"start": "nuxt start",
"generate": "nuxt generate",

View File

@ -2,9 +2,7 @@
<v-app>
<main-app-bar>NoSQLBench - Workload Builder</main-app-bar>
<v-layout
justify-center
align-center>
<v-layout>
<v-main>
<v-container fluid>
@ -62,9 +60,9 @@ import {saveAs} from "file-saver";
import yamlDumper from "js-yaml";
import CQL3Parser from '@/antlr/CQL3Parser.js';
import CQL3Lexer from '@/antlr/CQL3Lexer.js';
import defaultYaml from '~/assets/default.yaml';
import basictypes from '@/assets/basictypes.yaml';
import WorkspaceSelector from "~/components/WorkspaceSelector";
import defaultYaml from 'assets/default.yaml';
import basictypes from 'assets/basictypes.yaml';
import WorkspaceSelector from "@/components/WorkspaceSelector";
import AppSelector from "@/components/AppSelector";
import MainAppBar from "@/components/MainAppBar";

View File

@ -0,0 +1,322 @@
<template>
<v-app>
<main-app-bar>NoSQLBench - Workload Executor</main-app-bar>
<v-container class="d-flex justify-center">
<v-main>
<v-row fluid class="d-flex">
<v-col cols="12" fluid class="d-flex justify-space-between justify-center">
<!-- <v-btn-toggle max="1" v-model="toggle_workspaces" @change="validateAndSearch()">-->
<!-- <v-btn :disabled="this.toggle_builtins===undefined && this.toggle_workspaces===0">-->
<!-- <v-container fluid class="d-flex">-->
<!-- <v-icon title="'include ' + workspace">mdi-folder-star</v-icon>-->
<!-- <div class="ma-2">workspace '{{ workspace }}'</div>-->
<!-- <v-icon v-if="this.toggle_workspaces===0">mdi-check</v-icon>-->
<!-- </v-container>-->
<!-- </v-btn>-->
<!-- <v-btn :disabled="this.toggle_builtins===undefined && this.toggle_workspaces===1">-->
<!-- <v-container fluid class="d-flex">-->
<!-- <v-icon title="search workspaces">mdi-folder-star-multiple</v-icon>-->
<!-- <div class="ma-2">all workspaces</div>-->
<!-- <v-icon v-if="this.toggle_workspaces===1">mdi-check</v-icon>-->
<!-- </v-container>-->
<!-- </v-btn>-->
<!-- </v-btn-toggle>-->
<v-btn-toggle v-model="toggle_builtins" @change="validateAndSearch()">
<v-btn :disabled="this.toggle_workspaces===undefined">
<v-container fluid class="d-flex">
<v-icon title="include built-in workloads">mdi-folder-open</v-icon>
<div class="ma-2">bundled</div>
<v-icon v-if="this.toggle_builtins===0">mdi-check</v-icon>
</v-container>
</v-btn>
</v-btn-toggle>
</v-col>
</v-row>
<v-row fluid v-if="workloads!==undefined">
<!-- :item-text="workspace"-->
<!-- :item-value="workloadName"-->
<!-- v-model="workloadName"-->
<v-select
:items="availableWorkloads"
item-text="workloadName"
item-value="workloadName"
v-model="selected"
v-on:change="loadTemplates()"
label="Workload"
></v-select>
</v-row>
<!-- TEMPLATES -->
<v-main justify-start align-start class="d-inline-block pa-4 ma-10">
<v-row no-gutters v-if="templates">
<v-card v-for="(item, j) in Object.keys(templateparams)"
:key="item"
class="ma-4 pa-4"
>
<!-- <v-card-title>{{item}}</v-card-title>-->
<v-card-title class="ma-0 pa-0">{{ item }}</v-card-title>
<v-text-field hide-details v-model="templateparams[item]" align="center"></v-text-field>
<!-- <v-card-title>{{ this.workloadName }}</v-card-title>-->
<!-- <v-row v-for="(item, j) in Object.keys(templates)" :key="item.command">-->
<!-- <v-text-field v-model="templates[item]" :label="item">{{ item.name }}></v-text-field>-->
<!-- </v-row>-->
</v-card>
</v-row>
<!-- <v-row v-if="templates">-->
<!-- <v-col>-->
<!-- <v-card>-->
<!-- <v-card-title>{{ this.workloadName }}</v-card-title>-->
<!-- <v-row v-for="(item, j) in Object.keys(templates)" :key="item.command">-->
<!-- <v-text-field v-model="templates[item]" :label="item">{{ item.name }}></v-text-field>-->
<!-- </v-row>-->
<!-- </v-card>-->
<!-- </v-col>-->
<!-- </v-row>-->
<v-row>
<v-col></v-col>
</v-row>
<v-row>
<v-col cols="12">
<v-btn :title="runtitle" v-if="this.selected" v-on:click="runWorkload()">{{ runin }}</v-btn>
</v-col>
</v-row>
</v-main>
</v-main>
</v-container>
<v-footer app>
<span>&copy; 2020</span>
</v-footer>
</v-app>
</template>
<script>
import WorkspaceSelector from "@/components/WorkspaceSelector";
import AppSelector from "@/components/AppSelector";
import MainAppBar from "@/components/MainAppBar";
export default {
name: 'app-run',
components: {
AppSelector,
WorkspaceSelector,
MainAppBar
},
data(context) {
let data = {
extraparams: {},
templateparams: {},
availableWorkloads: [],
enabled: false,
selected: null,
toggle_builtins: false,
toggle_workspaces: 0,
workspace_names: ['current', 'all'],
sample: {
"workspace": "default",
"yamlPath": "test1.yaml",
"scenarioNames": [
"default",
"main"
],
"templates": {
"keyspace": "a",
"main-cycles": "10000000",
"rampup-cycles": "10000000",
"read_cl": "LOCAL_QUORUM",
"read_partition_ratio": "1",
"read_row_ratio": "1",
"write_cl": "LOCAL_QUORUM",
"write_ratio": "1"
},
"description": "test1",
"workloadName": "test1"
},
};
return data;
},
computed: {
searchin: function () {
let searchin = Array();
if (this.toggle_workspaces === 0) {
searchin.push(this.workspace);
} else if (this.toggle_workspaces === 1) {
console.log("workspaces typeof: '" + typeof (this.workspaces) + "'");
this.workspaces.forEach(w => {
searchin.push(w.name);
})
}
if (this.toggle_builtins === 0) {
searchin.push("builtins");
}
let joined = searchin.join(",");
console.log("joined:'" + joined + "'")
return joined;
},
runin: function () {
return "Run Workload";
},
runtitle: function () {
return "Run this workload in workspace [" + this.workspace + "].\n"
},
workspace: function () {
return this.$store.getters["workspaces/getWorkspace"]
},
workspaces: function () {
return this.$store.getters["workspaces/getWorkspaces"]
},
workloads: function () {
return this.$store.getters["workloads/getWorkloads"];
},
templates: function () {
return this.$store.getters["workloads/getTemplates"];
}
// ,
// workloadNames: function () {
// for (const [key, value] of Object.entries(this.workloads)) {
// console.log("key=[" + key + "] value=[" + value + "]");
// }
// }
},
watch: {
toggle_builtins: function (val) {
this.validateAndSearch();
},
toggle_workspaces: function (val) {
this.validateAndSearch();
},
templates: function (val) {
console.log("templates property changed");
if (val === undefined) {
this.templateparams = undefined;
} else {
this.templateparams = {};
Object.keys(val).forEach(k => {
console.log("k:" + k + " = " + val[k])
if (!this.templateparams[k]) {
this.templateparams[k] = val[k];
}
})
}
}
},
created() {
this.validateAndSearch();
this.$store.subscribe((mutation, state) => {
if (mutation.type === 'workloads/setWorkloads') {
// console.log("detected update to workloads:" + JSON.stringify(this.workloads));
this.availableWorkloads = state.workloads.workloads;
} else if (mutation.type === 'workspaces/setWorkspace') {
// console.log("detected update to workspace:" + JSON.stringify(this.workspace));
this.validateAndSearch();
}
// else if (mutation.type === 'workloads/setTemplates') {
// console.log("detected update to templates:" + JSON.stringify(this.workspace));
// Object.keys(this.templates).forEach(t => {
// console.log("template:" + t)
// if (this.templateparams[t]) {
// this.templateparams[t] = this.templates[t];
// console.log("added '" + this.templates[t])
// }
// })
// }
});
},
methods: {
validateAndSearch() {
let params = {
searchin: this.searchin,
reason: "search params changed"
}
this.$store.dispatch("workloads/fetchWorkloads", params);
},
loadTemplates() {
let params = {
workload: this.selected,
searchin: this.searchin,
reason: "workload selected"
}
this.$store.dispatch("workloads/fetchTemplates", params);
let templates = this.$store.getters["workloads/getTemplates"]
console.log("templates?" + JSON.stringify(templates, null, 2))
// Object.keys(this.templates).forEach(t => {
// console.log("t:" + t)
// // if (!this.templateparams[t]) {
// // this.templateparams[t]=this.templates[t];
// // }
// })
},
runWorkload() {
console.log("running workload '" + this.selected + "'")
// let workload = this.availableWorkloads.find(w => w.workloadName === this.selected);
let commands=[];
commands.push(this.selected);
Object.keys(this.templates).forEach(k => {
if (this.templateparams && this.templateparams[k]!==this.templates[k]) {
commands.push(k+"="+this.templateparams[k])
}
})
Object.keys(this.extraparams).forEach(k => {
commands.push(k+"="+this.extraparams[k])
})
let erq = {
scenario_name: this.selected + "_DATESTAMP",
workspace: this.workspace,
commands
}
console.log("submitting:" + JSON.stringify(erq));
this.$axios.$post("/executor/cli", erq);
}
}
}
</script>
<style>
/*.container {*/
/* margin: 0 auto;*/
/* display: flex;*/
/* justify-content: center;*/
/* align-items: center;*/
/* text-align: center;*/
/*}*/
/*.title {*/
/* font-family: 'Quicksand', 'Source Sans Pro', -apple-system, BlinkMacSystemFont,*/
/* 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;*/
/* display: block;*/
/* font-weight: 300;*/
/* font-size: 100px;*/
/* color: #35495e;*/
/* letter-spacing: 1px;*/
/*}*/
/*.subtitle {*/
/* font-weight: 300;*/
/* font-size: 42px;*/
/* color: #526488;*/
/* word-spacing: 5px;*/
/* padding-bottom: 15px;*/
/*}*/
/*.links {*/
/* padding-top: 15px;*/
/*}*/
</style>

View File

@ -1,274 +0,0 @@
<template>
<v-app>
<main-app-bar>NoSQLBench - Workload Executor</main-app-bar>
<v-container class="d-flex justify-center">
<v-main>
<v-row fluid class="d-flex">
<v-col cols="12" fluid class="d-flex justify-space-between justify-center">
<v-btn-toggle max="1" v-model="toggle_workspaces" @change="validateAndSearch()">
<v-btn :disabled="this.toggle_builtins===undefined && this.toggle_workspaces===0">
<v-container fluid class="d-flex">
<v-icon title="'include ' + workspace">mdi-folder-star</v-icon>
<div class="ma-2">workspace '{{ workspace }}'</div>
<v-icon v-if="this.toggle_workspaces===0">mdi-check</v-icon>
</v-container>
</v-btn>
<v-btn :disabled="this.toggle_builtins===undefined && this.toggle_workspaces===1">
<v-container fluid class="d-flex">
<v-icon title="search workspaces">mdi-folder-star-multiple</v-icon>
<div class="ma-2">all workspaces</div>
<v-icon v-if="this.toggle_workspaces===1">mdi-check</v-icon>
</v-container>
</v-btn>
</v-btn-toggle>
<v-btn-toggle v-model="toggle_builtins" @change="validateAndSearch()">
<v-btn :disabled="this.toggle_workspaces===undefined">
<v-container fluid class="d-flex">
<v-icon title="include built-in workloads">mdi-folder-open</v-icon>
<div class="ma-2">bundled</div>
<v-icon v-if="this.toggle_builtins===0">mdi-check</v-icon>
</v-container>
</v-btn>
</v-btn-toggle>
</v-col>
</v-row>
<v-container fluid v-if="workloads!==undefined">
<v-select
:items="availableWorkloads"
item-text="description"
item-value="workloadName"
v-model="workloadName"
chips
v-on:change="loadTemplates();"
label="Workload"
></v-select>
</v-container>
<v-container fluid>
<v-row
v-if="templates"
>
<v-col
cols="12"
sm="6"
md="10"
lg="10"
>
<v-card>
<v-card-title>
{{ this.workloadName }}
</v-card-title>
<v-col
v-for="(item, j) in Object.keys(templates)"
:key="item.command"
cols="12"
sm="6"
md="10"
lg="10"
>
<v-text-field
v-model="templates[item]"
:label="item"
>{{ item.name }}
</v-text-field>
</v-col>
<v-col cols="12">
<v-btn :title="runtitle" v-if="this.workloadName" v-on:click="runWorkload()">{{ runin }}</v-btn>
</v-col>
</v-card>
</v-col>
</v-row>
</v-container>
</v-main>
</v-container>
<v-footer app>
<span>&copy; 2020</span>
</v-footer>
</v-app>
</template>
<script>
import WorkspaceSelector from "@/components/WorkspaceSelector";
import AppSelector from "@/components/AppSelector";
import MainAppBar from "@/components/MainAppBar";
export default {
name: 'app-run',
components: {
AppSelector,
WorkspaceSelector,
MainAppBar
},
data(context) {
let data = {
availableWorkloads: [],
enabled: false,
workloadName: null,
toggle_builtins: 0,
toggle_workspaces: 0,
workspace_names: ['current', 'all'],
sample: {
"workspace": "default",
"yamlPath": "test1.yaml",
"scenarioNames": [
"default",
"main"
],
"templates": {
"keyspace": "a",
"main-cycles": "10000000",
"rampup-cycles": "10000000",
"read_cl": "LOCAL_QUORUM",
"read_partition_ratio": "1",
"read_row_ratio": "1",
"write_cl": "LOCAL_QUORUM",
"write_ratio": "1"
},
"description": "test1",
"workloadName": "test1"
},
};
return data;
},
computed: {
searchin: function () {
let searchin = Array();
if (this.toggle_workspaces === 0) {
searchin.push(this.workspace);
} else if (this.toggle_workspaces === 1) {
console.log("workspaces typeof: '" + typeof (this.workspaces) + "'");
this.workspaces.forEach(w => {
searchin.push(w.name);
})
}
if (this.toggle_builtins === 0) {
searchin.push("builtins");
}
let joined = searchin.join(",");
console.log("joined:'" + joined + "'")
return joined;
},
runin: function () {
return "Run Workload in " + this.$store.getters["workspaces/getWorkspace"];
},
runtitle: function () {
return "Click to run this workload in the '" + this.workspace + "' workspace, or change the workspace in the app bar.\n"
},
workspace: function () {
return this.$store.getters["workspaces/getWorkspace"]
},
workspaces: function () {
return this.$store.getters["workspaces/getWorkspaces"]
},
workloads: function () {
return this.$store.getters["workloads/getWorkloads"];
},
templates: function () {
return this.$store.getters["workloads/getTemplates"];
},
workloadNames: function () {
for (const [key, value] of Object.entries(this.workloads)) {
console.log("key=[" + key + "] value=[" + value + "]");
}
}
},
watch: {
toggle_builtins: function (val) {
this.validateAndSearch();
},
toggle_workspaces: function (val) {
this.validateAndSearch();
}
},
created() {
this.validateAndSearch();
this.$store.subscribe((mutation, state) => {
console.log("mutation type " + mutation.type);
if (mutation.type === 'workloads/setWorkloads') {
console.log("detected update to workloads:" + JSON.stringify(this.workloads));
this.availableWorkloads = state.workloads.workloads;
}
});
},
methods: {
validateAndSearch() {
let params = {
searchin: this.searchin,
reason: "search params changed"
}
this.$store.dispatch("workloads/fetchWorkloads", params);
},
loadTemplates() {
let params = {
workload: this.workloadName,
searchin: this.searchin,
reason: "workload selected"
}
this.$store.dispatch("workloads/fetchTemplates", params);
},
runWorkload() {
console.log("running workload...")
let workload = this.availableWorkloads.filter(w => w.workloadName===this.workloadName);
let erq = {
name: "run_"+workload.workloadName,
workspace: this.workspace,
commands: [this.workloadName]
}
console.log("submitting:" + JSON.stringify(erq));
this.$axios.$post("services/executor/cli",erq);
}
}
}
</script>
<style>
/*.container {*/
/* margin: 0 auto;*/
/* display: flex;*/
/* justify-content: center;*/
/* align-items: center;*/
/* text-align: center;*/
/*}*/
/*.title {*/
/* font-family: 'Quicksand', 'Source Sans Pro', -apple-system, BlinkMacSystemFont,*/
/* 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;*/
/* display: block;*/
/* font-weight: 300;*/
/* font-size: 100px;*/
/* color: #35495e;*/
/* letter-spacing: 1px;*/
/*}*/
/*.subtitle {*/
/* font-weight: 300;*/
/* font-size: 42px;*/
/* color: #526488;*/
/* word-spacing: 5px;*/
/* padding-bottom: 15px;*/
/*}*/
/*.links {*/
/* padding-top: 15px;*/
/*}*/
</style>

View File

@ -0,0 +1,226 @@
<template>
<v-app>
<main-app-bar>NoSQLBench - Execution Status</main-app-bar>
<v-main justify-start align-start class="d-inline-block pa-4 ma-10">
<div class="row no-gutters">
<!-- SCENARIO CARD -->
<v-card min-width="300" max-width="300" max-height="400" raised elevation="3"
v-for="(scenario,w) in scenarios" :key="w"
class="pa-4 ma-4"
:title="JSON.stringify(scenario,null,2)"
:color="scenario.state==='Running' ? 'accent' : 'default'">
<v-row align-content="start" align="start">
<v-card-title>{{ scenario.scenario_name }}</v-card-title>
<v-card-subtitle>{{ scenario.state }}</v-card-subtitle>
<!-- <v-icon>mdi-magnify</v-icon>-->
</v-row>
<!-- Scenario Controls -->
<v-row>
<v-btn
v-if="scenario.state==='Running'"
@click="stop_scenario(scenario)"
:title="'Stop scenario ' + scenario.scenario_name"
><v-icon>mdi-stop</v-icon>
</v-btn>
<!-- <v-btn v-if="scenario.state!=='Running'">-->
<!-- <v-icon>mdi-play</v-icon>-->
<!-- </v-btn>-->
<v-btn v-if="scenario.state==='Finished' || scenario.state==='Errored'"
:title="'Purge scenario ' + scenario.scenario_name"
@click="purge_scenario(scenario)"
><v-icon>mdi-delete</v-icon>
</v-btn>
<v-btn v-if="scenario.state==='Scheduled'"
:title="'cancel scenario ' + scenario.scenario_name"
@click="purge_scenario(scenario)"
><v-icon>mdi-cancel</v-icon></v-btn>
</v-row>
<v-divider></v-divider>
<!-- Activities -->
<!-- <v-card-subtitle>activities:</v-card-subtitle>-->
<v-row v-for="(progress,p) in scenario.progress" :key="p">
<v-card min-width="290" :title="JSON.stringify(progress,null,2)">
<v-card-subtitle class="pa-2 ma-2">{{ progress.name }}</v-card-subtitle>
<v-progress-linear v-if="progress.completed"
:title="(progress.completed*100.0).toFixed(4)"
:value="progress_of(progress.completed)"
:color="colorof(progress.state)"
height="10"
></v-progress-linear>
<v-card-text v-if="progress.state==='Running'">
{{ (progress.eta_millis / 60000).toFixed(2) + " minutes remaining" }}
</v-card-text>
<v-card-text v-if="scenario.result && scenario.result.error">
{{ JSON.stringify(scenario.result.error, null, 2) }}
</v-card-text>
</v-card>
</v-row>
</v-card>
</div>
</v-main>
<!-- FOOTER -->
<v-footer :color="this.paused ? 'secondary' : 'unset'" app>
<v-row align="middle">
<v-progress-circular color="#AAAAAA"
width="1"
:title="'refresh in '+(this.beats_per_refresh-this.beats_in_refresh) + 's (' + this.beats_per_suffix + ')'"
:value="(this.beats_in_refresh/this.beats_per_refresh)*100"
@click="do_refresh"
>{{ beats_per_refresh - beats_in_refresh }}
</v-progress-circular>
<v-icon v-if="!this.paused" @click="pause()">mdi-pause</v-icon>
<v-icon v-if="this.paused" @click="unpause()">mdi-play</v-icon>
<v-icon @click="scale_up">mdi-plus</v-icon>
<v-icon @click="scale_down">mdi-minus</v-icon>
</v-row>
</v-footer>
</v-app>
</template>
<script>
import WorkspaceSelector from "@/components/WorkspaceSelector";
import {mapActions, mapGetters, mapMutations} from "vuex";
import AppSelector from "@/components/AppSelector";
import MainAppBar from "@/components/MainAppBar";
export default {
name: "workspaces.vue",
components: {
MainAppBar,
AppSelector,
WorkspaceSelector
},
data(context) {
let data = {
beat: false,
beats_per_refresh: 10,
beats_in_refresh: 0,
beats_per_suffix: '10s',
scales: [
[1, '1s'], [5, '5s'], [10, '10s'], [20, '20s'], [60, '60s'], [300, '5m'], [600, '10m'], [1800, '15m']
],
beat_tick: 1000,
heart_is_beating: false,
paused: false,
};
return data;
},
computed: {
scenarios: {
get() {
return this.$store.getters["scenarios/getScenarios"]
}
},
},
methods: {
colorof(state) {
if (state==='Running') {
return "success";
}
if (state==='Errored') {
return "error"
}
return "blue";
},
scale_up() {
let idx =Array.from(Array(this.scales.length), (x,i) => i)
.find(y => {return this.scales[y][0]===this.beats_per_refresh})
idx = Math.min(idx+1,this.scales.length-1);
console.log("idx:"+idx)
this.beats_per_refresh=this.scales[idx][0];
this.beats_per_suffix = this.scales[idx][1];
},
scale_down() {
let idx =Array.from(Array(this.scales.length), (x,i) => i)
.find(y => {return this.scales[y][0]===this.beats_per_refresh})
idx = Math.max(idx-1,0);
console.log("idx:"+idx)
this.beats_per_refresh=this.scales[idx][0];
this.beats_per_suffix = this.scales[idx][1];
},
stop_scenario(scenario) {
console.log("stopping scenario: " + scenario.scenario_name);
this.$store.dispatch("scenarios/stopScenario", scenario.scenario_name)
this.do_refresh();
},
purge_scenario(scenario) {
console.log("purging scenario: " + scenario.scenario_name);
this.$store.dispatch("scenarios/deleteScenario", scenario.scenario_name);
this.do_refresh();
},
pause() {
this.paused = true;
},
unpause() {
this.paused = false;
this.heartbeat();
},
heartbeat() {
this.heart_is_beating = false;
if (this.paused) {
return;
}
let seconds = new Date().getSeconds();
// console.log("seconds:" + seconds);
this.beat = (seconds % 2) === 0;
this.beats_in_refresh++;
if (this.beats_in_refresh >= this.beats_per_refresh) {
this.do_refresh();
}
if (!this.heart_is_beating) {
this.heart_is_beating = true;
setTimeout(this.heartbeat, this.beat_tick);
}
},
do_refresh() {
this.beats_in_refresh = 0;
this.$store.dispatch("scenarios/loadScenarios", "timer refresh")
},
progress_of(completion) {
if (isNaN(completion)) {
return undefined;
}
let progress = (completion * 100.0).toFixed(2);
return progress;
},
is_running(scenario) {
return scenario.progress.find(x => {
return x.state === "Running"
});
},
abbrev(name) {
return name;
}
},
created() {
console.log("created component...");
this.$store.dispatch("scenarios/loadScenarios", "watch panel load");
},
mounted() {
setTimeout(this.heartbeat, 1000);
}
}
</script>
<style>
</style>

View File

@ -1,146 +0,0 @@
<template>
<v-app>
<main-app-bar>NoSQLBench - Execution Status</main-app-bar>
<!--
{
"summary": {
"total_bytes": 24,
"total_files": 1,
"last_change": 1597293397043,
"last_file_changed": "README.md"
},
"name": "default",
"modified": 1597293397043,
"changed": "README.md (1H17M27S ago)"
}
]
-->
<v-main justify-start align-start class="d-inline-block pa-4 ma-10">
<div class="row no-gutters">
<v-card min-width="300" max-width="300" max-height="400" raised elevation="5"
v-for="(invocation,w) in invocations" :key="w"
class="pa-4 ma-4" :loading="is_loading(invocation)">
<v-row :title="JSON.stringify(invocation)">
<v-card-title title="running scenario name">{{ invocation.scenario_name }}</v-card-title>
<!-- <v-icon v-if="workspace === cardspace.name">mdi-check-bold</v-icon>-->
</v-row>
<v-card-subtitle title="title">sdf</v-card-subtitle>
<v-card-subtitle title="started at">{{invocation.started_at}}</v-card-subtitle>
<!-- <v-card-subtitle title="last change">{{ abbrev(invocation.summary.last_changed_filename) }}</v-card-subtitle>-->
<!-- <v-divider></v-divider>-->
<!-- <v-list align-start>-->
<!-- <v-simple-table>-->
<!-- <tbody>-->
<!-- <tr>-->
<!-- <td>Bytes</td>-->
<!-- <td>{{ invocation.summary.total_bytes }}</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <td>Files</td>-->
<!-- <td>{{ invocation.summary.total_files }}</td>-->
<!-- </tr>-->
<!-- </tbody>-->
<!-- </v-simple-table>-->
<!-- <v-divider></v-divider>-->
<!-- <v-list-item>-->
<!-- <v-btn title="view details of workspace">-->
<!-- <v-icon>mdi-magnify</v-icon>-->
<!-- </v-btn>-->
<!-- &lt;!&ndash; <v-btn title="use this workspace">&ndash;&gt;-->
<!-- &lt;!&ndash; <v-icon>mdi-play</v-icon>&ndash;&gt;-->
<!-- &lt;!&ndash; </v-btn>&ndash;&gt;-->
<!-- <v-spacer></v-spacer>-->
<!-- <v-btn title="download zipped workspace">-->
<!-- <v-icon>mdi-folder-download</v-icon>-->
<!-- </v-btn>-->
<!-- <v-spacer></v-spacer>-->
<!-- <v-btn title="purge workspace">-->
<!-- <v-icon @click="purgeWorkspace(cardspace.name)">mdi-trash-can</v-icon>-->
<!-- </v-btn>-->
<!-- </v-list-item>-->
<!-- </v-list>-->
</v-card>
</div>
</v-main>
</v-app>
</template>
<script>
import WorkspaceSelector from "@/components/WorkspaceSelector";
import {mapActions, mapGetters, mapMutations} from "vuex";
import AppSelector from "@/components/AppSelector";
import MainAppBar from "@/components/MainAppBar";
export default {
name: "workspaces.vue",
components: {
MainAppBar,
AppSelector,
WorkspaceSelector
},
data(context) {
let data = {
// invocations: [
// {
// "scenario_name": "mytestscenario",
// "progress": [
// {
// "name": "/tmp/nosqlbench/mytestscenario3888869514662003808file1.yaml",
// "state": "Running",
// "details": "min=0 cycle=1692 max=10000000",
// "completed": 1.692E-4
// }
// ],
// "started_at": 1597749369800,
// "ended_at": -1,
// "activity_states": [
// {
// "completion": "1.692E-4",
// "name": "/tmp/nosqlbench/mytestscenario3888869514662003808file1.yaml",
// "state": "Running"
// }
// ]
// }
// ]
};
return data;
},
computed: {
invocations: {
get() {
return this.$store.getters["invocations/getInvocations"]
}
}
},
methods: {
is_loading(invocation) {
let found = invocation.progress.find(p => {p.state==='Running'});
return !!found;
},
abbrev(name) {
return name;
}
},
created() {
console.log("created component...");
this.$store.dispatch("invocations/loadInvocations", "watch panel load");
}
}
</script>
<style>
</style>

View File

@ -72,7 +72,7 @@
<script>
import WorkspaceSelector from "~/components/WorkspaceSelector";
import WorkspaceSelector from "@/components/WorkspaceSelector";
import {mapActions, mapGetters, mapMutations} from "vuex";
import AppSelector from "@/components/AppSelector";
import MainAppBar from "@/components/MainAppBar";

View File

@ -0,0 +1,86 @@
<template>
<v-app>
<main-app-bar></main-app-bar>
<!-- <workspace-selector @changed="seenChange(workspace)"></workspace-selector>-->
<!-- <v-btn title="start a new workspace" @click="startNewWorkspace()">-->
<!-- <v-icon>mdi-folder-plus-outline</v-icon>-->
<!-- </v-btn>-->
<!-- <v-btn icon title="upload a workspace zip file">-->
<!-- <v-icon>mdi-folder-upload</v-icon>-->
<!-- </v-btn>-->
<!-- <v-btn icon title="download workspaces.zip">-->
<!-- <v-icon>mdi-briefcase-download</v-icon>-->
<!-- </v-btn>-->
<!-- <v-btn icon title="upload workspaces.zip">-->
<!-- <v-icon>mdi-briefcase-upload</v-icon>-->
<!-- </v-btn>-->
<v-content justify-start align-start class="d-inline-block pa-4 ma-10">
<div class="row no-gutters">
</div>
</v-content>
</v-app>
</template>
<script>
import WorkspaceSelector from "~/components/WorkspaceSelector";
import {mapActions, mapGetters, mapMutations} from "vuex";
import AppSelector from "@/components/AppSelector";
import MainAppBar from "@/components/MainAppBar";
export default {
name: "workspaces.vue",
components: {
MainAppBar,
AppSelector,
WorkspaceSelector
},
data(context) {
let data = {};
return data;
},
computed: {
workspace: {
get() {
return this.$store.getters["workspaces/getWorkspace"]
},
set(val) {
this.$store.dispatch("workspaces/setWorkspace", val)
}
},
workspaces: {
get() {
return this.$store.getters["workspaces/getWorkspaces"]
},
set(val) {
this.$store.dispatch("workspaces/setWorkspaces", val)
}
},
},
methods: {
abbrev(name) {
return name;
},
purgeWorkspace: function (ws) {
console.log("purging " + ws);
this.$store.dispatch('workspaces/purgeWorkspace', ws);
// this.$store.dispatch("workspaces/setWorkspace")
this.$forceUpdate();
},
},
created() {
console.log("created component...");
this.$store.dispatch('workspaces/initWorkspaces', "workspace panel load");
}
}
</script>
<style>
</style>

View File

@ -62,7 +62,7 @@ export const getters = {
throw "unable to load active markdown for undefined category";
}
if (state.active_topic===null) {
throw "uanble to load active markdown for undefined topic";
throw "unable to load active markdown for undefined topic";
}
return state.active_topic.content;
}
@ -78,13 +78,6 @@ export const mutations = {
setCategories(state, categories) {
state.categories = categories;
},
// initializeStore(state) {
// if(localStorage.getItem('store')) {
// this.replaceState(
// Object.assign(state,JSON.parse(localStorage.getItem('store')))
// );
// }
// },
toggleDrawerState(state, newDrawerState) {
if (state.isMenuLocked) {
return;

View File

@ -1,33 +0,0 @@
// https://www.mikestreety.co.uk/blog/vue-js-using-localstorage-with-the-vuex-store
import {mapGetters} from "vuex";
export const state = () => ({
invocations: []
});
export const getters = {
getInvocations: (state, getters) => {
return state.invocations;
}
}
export const mutations = {
setInvocations(state, invocations) {
state.invocations = invocations;
}
};
export const actions = {
async loadInvocations({commit, state, dispatch}, reason) {
console.log("initializing scenarios because '" + reason + "'")
this.$axios.$get("/executor/scenarios/")
.then(res => {
console.log("axios/vuex invocations async get:" + JSON.stringify(res));
console.log("committing setInvocations:" + JSON.stringify(res));
commit('setInvocations', res)
})
.catch((e) => {
console.error("axios/nuxt invocations async error:", e);
})
}
};

View File

@ -0,0 +1,49 @@
// https://www.mikestreety.co.uk/blog/vue-js-using-localstorage-with-the-vuex-store
import {mapGetters} from "vuex";
export const state = () => ({
scenarios: []
});
export const getters = {
getScenarios: (state, getters) => {
return state.scenarios;
}
}
export const mutations = {
setScenarios(state, scenarios) {
state.scenarios = scenarios;
}
}
export const actions = {
async loadScenarios({commit, state, dispatch}, reason) {
console.log("loading scenarios because '" + reason + "'")
await this.$axios.$get("/executor/scenarios/")
.then(res => {
// console.log("axios/vuex scenarios async get:" + JSON.stringify(res));
// console.log("committing setScenarios:" + JSON.stringify(res));
commit('setScenarios', res)
})
.catch((e) => {
console.error("axios/nuxt scenarios async error:", e);
})
},
async stopScenario({commit, state, dispatch}, scenario_name) {
await this.$axios.$post("/executor/stop/" + scenario_name)
.then()
.catch((e) => {
console.error("axios/nuxt scenario stop error:", e);
})
await dispatch("loadScenarios")
},
async deleteScenario({commit, state, dispatch}, scenario_name) {
await this.$axios.$delete("/executor/scenario/" + scenario_name)
.then()
.catch((e) => {
console.error("axios/nuxt scenario stop error:", e);
})
await dispatch("loadScenarios")
}
};

View File

@ -33,36 +33,36 @@ export const mutations = {
export const actions = {
async setWorkloads({commit, state, dispatch}, val) {
console.log("committing setWorkloads:" + JSON.stringify(val));
// console.log("committing setWorkloads:" + JSON.stringify(val));
commit('setWorkloads', val);
},
async setTemplates({commit, state, dispatch}, val) {
console.log("commiting setTemplates:" + JSON.stringify(val));
// console.log("commiting setTemplates:" + JSON.stringify(val));
commit("setTemplates", val);
},
async setSearchin({commit, state, dispatch}, val) {
console.log("committing setsearchin:" + JSON.stringify(val));
// console.log("committing setsearchin:" + JSON.stringify(val));
commit('setSearchin', val);
},
fetchWorkloads({commit, state, dispatch}, params) {
async fetchWorkloads({commit, state, dispatch}, params) {
let reason = params.reason;
let searchin = params.searchin;
if (reason === undefined || searchin === undefined) {
throw "Unable to fetch workloads without a reason or searchin: " + JSON.stringify(params);
}
console.log("fetching workloads because '" + reason + "'")
// console.log("fetching workloads because '" + reason + "'")
commit("setTemplates", undefined);
this.$axios.$get("/workloads/?searchin=" + searchin)
.then(res => {
console.log("axios/vuex workloads async get:" + JSON.stringify(res));
// console.log("axios/vuex workloads async get:" + JSON.stringify(res));
commit("setWorkloads", res);
})
.catch((e) => {
console.error("axios/nuxt workloads async error:", e);
})
},
async fetchTemplates({commit, state, dispatch}, params) {
fetchTemplates({commit, state, dispatch}, params) {
let reason = params.reason;
let workload = params.workload;
let searchin = params.searchin;
@ -71,13 +71,15 @@ export const actions = {
}
console.log("fetching templates for '" + workload + "' because '" + reason + "'")
this.$axios.$get("/workloads/parameters?workloadName=" + workload + "&" + searchin)
this.$axios.$get("/workloads/parameters?workloadName=" + workload + "&" + "searchin=" + searchin)
.then(res => {
console.log("axios/vuex templates async get:" + JSON.stringify(res));
// console.log("axios/vuex templates async get:" + JSON.stringify(res));
dispatch("setTemplates", res);
})
.catch((e) => {
console.error("axios/nuxt templates async error:", e);
})
}
};

View File

@ -3,7 +3,8 @@ import {mapGetters} from "vuex";
export const state = () => ({
workspace: 'default',
workspaces: []
workspaces: [],
fileview: []
});
export const getters = {
@ -12,6 +13,9 @@ export const getters = {
},
getWorkspaces: (state, getters) => {
return state.workspaces;
},
getFileview: (state, getters) => {
return state.fileview;
}
// ...mapGetters(['workspace','workspaces'])
@ -23,24 +27,27 @@ export const mutations = {
},
setWorkspaces(state, workspaces) {
state.workspaces = workspaces;
},
setFileview(state, fileview) {
state.fileview = fileview;
}
};
export const actions = {
async setWorkspace({commit, state, dispatch}, val) {
console.log("committing setWorkspace:" + JSON.stringify(val));
// console.log("committing setWorkspace:" + JSON.stringify(val));
commit('setWorkspace', val);
},
async setWorkspaces({commit, state, dispatch}, val) {
console.log("committing setWorkspaces:" + JSON.stringify(val));
// console.log("committing setWorkspaces:" + JSON.stringify(val));
commit('setWorkspaces', val);
},
async initWorkspaces({commit, state, dispatch}, reason) {
console.log("initializing workspaces because '" + reason + "'")
// console.log("initializing workspaces because '" + reason + "'")
this.$axios.$get("/workspaces/")
.then(res => {
console.log("axios/vuex workspaces async get:" + JSON.stringify(res));
console.log("committing setWorkspaces:" + JSON.stringify(res));
// console.log("axios/vuex workspaces async get:" + JSON.stringify(res));
// console.log("committing setWorkspaces:" + JSON.stringify(res));
commit('setWorkspaces', res)
})
.catch((e) => {
@ -66,7 +73,7 @@ export const actions = {
async activateWorkspace({commit, state, dispatch}, workspace) {
const fresh_workspace = await this.$axios.$get("/workspaces/" + workspace)
.then(res => {
console.log("axios/vuex workspace async get:" + JSON.stringify(res))
// console.log("axios/vuex workspace async get:" + JSON.stringify(res))
return res;
})
.catch((e) => {

View File

@ -25,6 +25,8 @@ import io.nosqlbench.engine.api.activityapi.ratelimits.RateLimiter;
import io.nosqlbench.engine.api.activityimpl.ActivityDef;
import io.nosqlbench.engine.api.activityimpl.ParameterMap;
import io.nosqlbench.engine.api.activityimpl.SimpleActivity;
import io.nosqlbench.engine.api.activityimpl.input.ProgressCapable;
import io.nosqlbench.engine.api.activityimpl.input.StateCapable;
import java.io.InputStream;
import java.io.PrintWriter;
@ -34,15 +36,17 @@ import java.util.function.Supplier;
* Provides the components needed to build and run an activity a runtime.
* The easiest way to build a useful Activity is to extend {@link SimpleActivity}.
*/
public interface Activity extends Comparable<Activity>, ActivityDefObserver {
public interface Activity extends Comparable<Activity>, ActivityDefObserver, ProgressCapable, StateCapable {
/**
* Provide the activity with the controls needed to stop itself.
*
* @param activityController The dedicated control interface for this activity
*/
void setActivityController(ActivityController activityController);
ActivityController getActivityController();
/**
* Register an object which should be closed after this activity is shutdown.
*
@ -89,8 +93,11 @@ public interface Activity extends Comparable<Activity>, ActivityDefObserver {
void setOutputDispenserDelegate(OutputDispenser outputDispenser);
RunState getRunState();
void setRunState(RunState runState);
long getStartedAtMillis();
default void shutdownActivity() {
}

View File

@ -18,8 +18,47 @@
package io.nosqlbench.engine.api.activityapi.core;
public interface ProgressMeter {
double getProgress();
String getProgressName();
RunState getProgressState();
String getProgressDetails();
// RunState getProgressState();
long getStartedAtMillis();
long getProgressMin();
long getProgressCurrent();
long getProgressMax();
long getRecyclesCurrent();
long getRecyclesMax();
default String getProgressSummary() {
return "min=" + getProgressMin() + " cycle=" + getProgressCurrent() + " max=" + getProgressMax() +
(getRecyclesMax() > 0L ? " recycles=" + getRecyclesCurrent() + "/" + getRecyclesMax() : "");
}
default double getProgressRatio() {
return
((double) (getProgressCurrent() - getProgressMin()))
/
((double) (getProgressMax() - getProgressMin()));
}
default double getProgressTotal() {
return (getProgressMax() - getProgressMin());
}
default double getProgressETAMillis() {
long then = getStartedAtMillis();
long now = System.currentTimeMillis();
double elapsed = now - then;
double completed = getProgressCurrent() - getProgressMin();
double rate = completed / elapsed;
double remaining = getProgressMax() - getProgressCurrent();
return remaining / rate;
}
}

View File

@ -0,0 +1,55 @@
package io.nosqlbench.engine.api.activityimpl;
import io.nosqlbench.engine.api.activityapi.core.ProgressMeter;
import io.nosqlbench.engine.api.activityapi.core.RunState;
import io.nosqlbench.engine.api.activityimpl.input.StateCapable;
public class ProgressAndStateMeter implements ProgressMeter, StateCapable {
private final ProgressMeter meter;
private final StateCapable statesrc;
public ProgressAndStateMeter(ProgressMeter meter, StateCapable statesrc) {
this.meter = meter;
this.statesrc = statesrc;
}
@Override
public String getProgressName() {
return meter.getProgressName();
}
@Override
public long getStartedAtMillis() {
return meter.getStartedAtMillis();
}
@Override
public long getProgressMin() {
return meter.getProgressMin();
}
@Override
public long getProgressCurrent() {
return meter.getProgressCurrent();
}
@Override
public long getProgressMax() {
return meter.getProgressMax();
}
@Override
public long getRecyclesCurrent() {
return meter.getRecyclesCurrent();
}
@Override
public long getRecyclesMax() {
return meter.getRecyclesMax();
}
@Override
public RunState getRunState() {
return statesrc.getRunState();
}
}

View File

@ -3,6 +3,7 @@ package io.nosqlbench.engine.api.activityimpl;
import com.codahale.metrics.Timer;
import io.nosqlbench.engine.api.activityapi.core.*;
import io.nosqlbench.engine.api.activityapi.cyclelog.filters.IntPredicateDispenser;
import io.nosqlbench.engine.api.activityapi.input.Input;
import io.nosqlbench.engine.api.activityapi.input.InputDispenser;
import io.nosqlbench.engine.api.activityapi.output.OutputDispenser;
import io.nosqlbench.engine.api.activityapi.planning.OpSequence;
@ -14,6 +15,7 @@ import io.nosqlbench.engine.api.activityapi.ratelimits.RateSpec;
import io.nosqlbench.engine.api.activityconfig.StatementsLoader;
import io.nosqlbench.engine.api.activityconfig.yaml.OpTemplate;
import io.nosqlbench.engine.api.activityconfig.yaml.StmtsDocList;
import io.nosqlbench.engine.api.activityimpl.input.ProgressCapable;
import io.nosqlbench.engine.api.metrics.ActivityMetrics;
import io.nosqlbench.engine.api.templating.CommandTemplate;
import io.nosqlbench.engine.api.templating.StrInterpolator;
@ -32,11 +34,11 @@ import java.util.function.Supplier;
/**
* A default implementation of an Activity, suitable for building upon.
*/
public class SimpleActivity implements Activity {
public class SimpleActivity implements Activity, ProgressCapable {
private final static Logger logger = LoggerFactory.getLogger(SimpleActivity.class);
protected ActivityDef activityDef;
private List<AutoCloseable> closeables = new ArrayList<>();
private final List<AutoCloseable> closeables = new ArrayList<>();
private MotorDispenser motorDispenser;
private InputDispenser inputDispenser;
private ActionDispenser actionDispenser;
@ -49,6 +51,7 @@ public class SimpleActivity implements Activity {
private ActivityController activityController;
private ActivityInstrumentation activityInstrumentation;
private PrintWriter console;
private long startedAtMillis;
public SimpleActivity(ActivityDef activityDef) {
this.activityDef = activityDef;
@ -70,6 +73,14 @@ public class SimpleActivity implements Activity {
public synchronized void setRunState(RunState runState) {
this.runState = runState;
if (runState == RunState.Running) {
this.startedAtMillis = System.currentTimeMillis();
}
}
@Override
public long getStartedAtMillis() {
return startedAtMillis;
}
@Override
@ -207,6 +218,7 @@ public class SimpleActivity implements Activity {
}
@Override
public Timer getResultTimer() {
return ActivityMetrics.timer(getActivityDef(), "result");
@ -399,4 +411,16 @@ public class SimpleActivity implements Activity {
return planner.resolve();
}
@Override
public ProgressMeter getProgressMeter() {
Input input = getInputDispenserDelegate().getInput(0);
if (input instanceof ProgressCapable) {
ProgressMeter meter = ((ProgressCapable) input).getProgressMeter();
return new ProgressAndStateMeter(meter, this);
} else {
throw new RuntimeException("Progress meter must be implemented here.");
}
}
}

View File

@ -17,6 +17,7 @@
package io.nosqlbench.engine.api.activityimpl.input;
import io.nosqlbench.engine.api.activityapi.core.ActivityDefObserver;
import io.nosqlbench.engine.api.activityapi.core.ProgressMeter;
import io.nosqlbench.engine.api.activityapi.cyclelog.buffers.results.CycleSegment;
import io.nosqlbench.engine.api.activityapi.input.Input;
import io.nosqlbench.engine.api.activityimpl.ActivityDef;
@ -50,8 +51,9 @@ public class AtomicInput implements Input, ActivityDefObserver, ProgressCapable
private final AtomicLong recycleValue = new AtomicLong(0L);
private final AtomicLong recycleMax = new AtomicLong(0L);
private final long startedAt = System.currentTimeMillis();
private ActivityDef activityDef;
private final ActivityDef activityDef;
public AtomicInput(ActivityDef activityDef) {
this.activityDef = activityDef;
@ -64,15 +66,15 @@ public class AtomicInput implements Input, ActivityDefObserver, ProgressCapable
long current = this.cycleValue.get();
long next = current + stride;
if (next > max.get()) {
if (recycleValue.get()>=recycleMax.get()) {
if (recycleValue.get() >= recycleMax.get()) {
logger.trace("Exhausted input for " + activityDef.getAlias() + " at " + current + ", recycle " +
"count " + recycleValue.get());
return null;
} else {
if (cycleValue.compareAndSet(current,min.get()+stride)) {
if (cycleValue.compareAndSet(current, min.get() + stride)) {
recycleValue.getAndIncrement();
logger.trace("recycling input for " + activityDef.getAlias() + " recycle:" + recycleValue.get());
return new InputInterval.Segment(min.get(), min.get()+stride);
return new InputInterval.Segment(min.get(), min.get() + stride);
}
}
}
@ -82,30 +84,30 @@ public class AtomicInput implements Input, ActivityDefObserver, ProgressCapable
}
}
@Override
public double getProgress() {
return (double) (cycleValue.get() - min.get());
}
@Override
public double getTotal() {
return (double) (max.get() - min.get());
}
@Override
public String getProgressDetails() {
return "min=" +min.get() + " cycle=" + cycleValue.get() + " max=" + max.get() +
(recycleMax.get()>0L ? " recycles=" + recycleValue.get() +"/" + recycleMax.get() : "");
}
// @Override
// public double getProgress() {
// return (double) (cycleValue.get() - min.get());
// }
//
// @Override
// public double getTotal() {
// return (double) (max.get() - min.get());
// }
//
// @Override
// public AtomicInputProgress.Range getRange() {
// return new Range(this);
// }
//
@Override
public String toString() {
return "AtomicInput{" +
"cycleValue=" + cycleValue +
", min=" + min +
", max=" + max +
", activity=" + activityDef.getAlias() +
'}';
"cycleValue=" + cycleValue +
", min=" + min +
", max=" + max +
", activity=" + activityDef.getAlias() +
'}';
}
@Override
@ -134,7 +136,10 @@ public class AtomicInput implements Input, ActivityDefObserver, ProgressCapable
long recycles = activityDef.getParams().getOptionalString("recycles").flatMap(Unit::longCountFor).orElse(0L);
this.recycleMax.set(recycles);
}
public long getStarteAtMillis() {
return this.startedAt;
}
@Override
@ -142,4 +147,53 @@ public class AtomicInput implements Input, ActivityDefObserver, ProgressCapable
return true;
}
@Override
public AtomicInputProgress getProgressMeter() {
return new AtomicInputProgress(activityDef.getAlias(), this);
}
private static class AtomicInputProgress implements ProgressMeter {
private final AtomicInput input;
private final String name;
public AtomicInputProgress(String name, AtomicInput input) {
this.name = name;
this.input = input;
}
@Override
public String getProgressName() {
return name;
}
@Override
public long getStartedAtMillis() {
return input.getStarteAtMillis();
}
@Override
public long getProgressMin() {
return input.min.get();
}
@Override
public long getProgressCurrent() {
return input.cycleValue.get();
}
@Override
public long getProgressMax() {
return input.max.get();
}
@Override
public long getRecyclesCurrent() {
return input.recycleValue.get();
}
@Override
public long getRecyclesMax() {
return input.recycleMax.get();
}
}
}

View File

@ -17,11 +17,11 @@
package io.nosqlbench.engine.api.activityimpl.input;
import io.nosqlbench.engine.api.activityapi.core.ProgressMeter;
/**
* Any type that implements this interface can provide a double indicating relative progress.
*/
public interface ProgressCapable {
double getProgress();
double getTotal();
String getProgressDetails();
ProgressMeter getProgressMeter();
}

View File

@ -0,0 +1,7 @@
package io.nosqlbench.engine.api.activityimpl.input;
import io.nosqlbench.engine.api.activityapi.core.RunState;
public interface StateCapable {
RunState getRunState();
}

View File

@ -315,7 +315,7 @@ public class NBCLIScenarioParser {
}
String description = stmts.getDescription();
workloadDescriptions.add(new WorkloadDesc(referenced, scenarioNames, sortedTemplates, description,""));
workloadDescriptions.add(new WorkloadDesc(referenced, scenarioNames, sortedTemplates, description, ""));
}
}
Collections.sort(workloadDescriptions);
@ -324,11 +324,15 @@ public class NBCLIScenarioParser {
}
public static List<WorkloadDesc> getWorkloadsWithScenarioScripts(boolean defaultIncludes, Set<String> includes) {
return getWorkloadsWithScenarioScripts(defaultIncludes, includes.toArray(new String[0]));
}
public static List<WorkloadDesc> getWorkloadsWithScenarioScripts(boolean defaultIncludes, String... includes) {
NBPathsAPI.GetPrefix searchin = NBIO.all();
if (defaultIncludes) {
searchin= searchin.prefix(SEARCH_IN);
searchin = searchin.prefix(SEARCH_IN);
}
List<Content<?>> activities = searchin

View File

@ -48,10 +48,13 @@ public class WorkloadDesc implements Comparable<WorkloadDesc> {
public String toString() {
return toString(true);
return
((workspace != null && !workspace.isEmpty()) ? workspace + ":" : "")
+ this.yamlPath;
// + (this.description != null ? "\ndesc: " + this.description : "");
}
public String toString(boolean includeScenarios) {
public String toMarkdown(boolean includeScenarios) {
StringBuilder sb = new StringBuilder();
@ -61,10 +64,10 @@ public class WorkloadDesc implements Comparable<WorkloadDesc> {
if (!description.isEmpty()) {
// sb.append("# description:\n");
String formttedDesc = "# " + String.join("\n# ",description.split("\n"));
String formttedDesc = "# " + String.join("\n# ", description.split("\n"));
sb.append(formttedDesc).append("\n");
while (sb.toString().endsWith("\n\n")) {
sb.setLength(sb.length()-1);
sb.setLength(sb.length() - 1);
}
// if (!description.endsWith("\n")) {
// sb.append("\n");
@ -124,4 +127,5 @@ public class WorkloadDesc implements Comparable<WorkloadDesc> {
public String getWorkspace() {
return workspace;
}
}

View File

@ -17,11 +17,16 @@
package io.nosqlbench.engine.api.util;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import io.nosqlbench.engine.api.activityimpl.ActivityDef;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.KeyStore;
@ -33,16 +38,6 @@ import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Optional;
import java.util.regex.Pattern;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.nosqlbench.engine.api.activityimpl.ActivityDef;
public class SSLKsFactory {
private final static Logger logger = LoggerFactory.getLogger(SSLKsFactory.class);
@ -186,7 +181,7 @@ public class SSLKsFactory {
kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, keyPassword);
} catch (Exception e) {
throw new RuntimeException("Unable to init KeyManagerFactory. Please check.", e);
throw new RuntimeException("Unable to init KeyManagerFactory. Please check password and location.", e);
}
TrustManagerFactory tmf;

View File

@ -12,7 +12,7 @@ public class NBCLIScenarios {
NBCLIScenarioParser.getWorkloadsWithScenarioScripts(true, includes);
for (WorkloadDesc workload : workloads) {
System.out.println(workload.toString(includeScenarios));
System.out.println(workload.toMarkdown(includeScenarios));
}
if (!includeScenarios) {

View File

@ -15,10 +15,8 @@
package io.nosqlbench.engine.core;
import io.nosqlbench.engine.api.activityapi.core.*;
import io.nosqlbench.engine.api.activityapi.input.Input;
import io.nosqlbench.engine.api.activityimpl.ActivityDef;
import io.nosqlbench.engine.api.activityimpl.ParameterMap;
import io.nosqlbench.engine.api.activityimpl.SlotStateTracker;
import io.nosqlbench.engine.api.activityimpl.input.ProgressCapable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -46,7 +44,7 @@ import java.util.stream.Collectors;
* </ul>
*/
public class ActivityExecutor implements ActivityController, ParameterMap.Listener, ProgressMeter {
public class ActivityExecutor implements ActivityController, ParameterMap.Listener, ProgressCapable {
private static final Logger logger = LoggerFactory.getLogger(ActivityExecutor.class);
private static final Logger activitylogger = LoggerFactory.getLogger("ACTIVITY");
@ -121,14 +119,7 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen
}
/**
* Shutdown the activity executor, with a grace period for the motor threads.
*
* @param initialMillisToWait milliseconds to wait after graceful shutdownActivity request, before forcing
* everything to stop
*/
public synchronized void forceStopExecutor(int initialMillisToWait) {
public synchronized RuntimeException forceStopScenario(int initialMillisToWait) {
activitylogger.debug("FORCE STOP/before alias=(" + activity.getAlias() + ")");
activity.setRunState(RunState.Stopped);
@ -164,15 +155,28 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen
activity.closeAutoCloseables();
long activityShutdownEndedAt = System.currentTimeMillis();
logger.debug("took " + (activityShutdownEndedAt - activityShutdownStartedAt) + " ms to shutdown activity threads");
activitylogger.debug("FORCE STOP/after alias=(" + activity.getAlias() + ")");
if (stoppingException != null) {
activitylogger.debug("FORCE STOP/exception alias=(" + activity.getAlias() + ")");
throw stoppingException;
}
activitylogger.debug("FORCE STOP/after alias=(" + activity.getAlias() + ")");
return stoppingException;
}
/**
* Shutdown the activity executor, with a grace period for the motor threads.
*
* @param initialMillisToWait milliseconds to wait after graceful shutdownActivity request, before forcing
* everything to stop
*/
public synchronized void forceStopScenarioAndThrow(int initialMillisToWait, boolean rethrow) {
RuntimeException exception = forceStopScenario(initialMillisToWait);
if (exception != null && rethrow) {
throw exception;
}
}
public boolean requestStopExecutor(int secondsToWait) {
activitylogger.debug("REQUEST STOP/before alias=(" + activity.getAlias() + ")");
@ -465,61 +469,10 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen
return activity;
}
@Override
public synchronized double getProgress() {
ArrayList<Input> inputs = motors.stream()
.map(Motor::getInput)
.distinct()
.collect(Collectors.toCollection(ArrayList::new));
double startCycle = getActivityDef().getStartCycle();
double endCycle = getActivityDef().getEndCycle();
double totalCycles = endCycle - startCycle;
double total = 0.0D;
double progress = 0.0D;
for (Input input : inputs) {
if (input instanceof ProgressCapable) {
ProgressCapable progressInput = (ProgressCapable) input;
total += progressInput.getTotal();
progress += progressInput.getProgress();
} else {
logger.warn("input does not support activity progress: " + input);
return Double.NaN;
}
}
return progress / total;
}
@Override
public String getProgressDetails() {
return motors.stream().map(Motor::getInput).distinct().findFirst()
.filter(i -> i instanceof ProgressCapable)
.map(i -> ((ProgressCapable) i).getProgressDetails()).orElse("");
}
@Override
public String getProgressName() {
return activityDef.getAlias();
}
@Override
public RunState getProgressState() {
Optional<RunState> first = motors.stream()
.map(Motor::getSlotStateTracker).map(SlotStateTracker::getSlotState)
.distinct().sorted().findFirst();
return first.orElse(RunState.Uninitialized);
}
public synchronized void notifyException(Thread t, Throwable e) {
//logger.error("Uncaught exception in activity thread forwarded to activity executor:", e);
this.stoppingException = new RuntimeException("Error in activity thread " + t.getName(), e);
forceStopExecutor(10000);
forceStopScenario(10000);
}
@Override
@ -544,4 +497,11 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen
}
requestStopMotors();
}
@Override
public ProgressMeter getProgressMeter() {
return this.activity.getProgressMeter();
}
}

View File

@ -17,8 +17,8 @@
package io.nosqlbench.engine.core;
import io.nosqlbench.engine.api.activityapi.core.ProgressMeter;
import io.nosqlbench.engine.api.activityapi.core.RunState;
import io.nosqlbench.engine.api.activityimpl.ProgressAndStateMeter;
import io.nosqlbench.engine.api.metrics.IndicatorMode;
import io.nosqlbench.engine.api.metrics.PeriodicRunnable;
import io.nosqlbench.engine.api.util.Unit;
@ -30,18 +30,18 @@ import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
public class ProgressIndicator implements Runnable {
public class ActivityProgressIndicator implements Runnable {
private final static Logger logger = LoggerFactory.getLogger(ProgressIndicator.class);
private final static Logger logger = LoggerFactory.getLogger(ActivityProgressIndicator.class);
private final String indicatorSpec;
private final ScenarioController sc;
private PeriodicRunnable<ProgressIndicator> runnable;
private PeriodicRunnable<ActivityProgressIndicator> runnable;
private IndicatorMode indicatorMode = IndicatorMode.console;
private Set<String> seen = new HashSet<>();
private final Set<String> seen = new HashSet<>();
private long intervalMillis=1L;
private long intervalMillis = 1L;
public ProgressIndicator(ScenarioController sc, String indicatorSpec) {
public ActivityProgressIndicator(ScenarioController sc, String indicatorSpec) {
this.sc = sc;
this.indicatorSpec = indicatorSpec;
start();
@ -76,21 +76,22 @@ public class ProgressIndicator implements Runnable {
@Override
public void run() {
Collection<ProgressMeter> progressMeters = sc.getProgressMeters();
for (ProgressMeter meter : progressMeters) {
Collection<ProgressAndStateMeter> progressMeters = sc.getProgressMeters();
for (ProgressAndStateMeter meter : progressMeters) {
boolean lastReport=false;
if (meter.getProgress()>=1.0d || meter.getProgressState()== RunState.Finished) {
boolean lastReport = false;
if (meter.getProgressRatio() >= 1.0d || meter.getRunState() == RunState.Finished) {
if (seen.contains(meter.getProgressName())) {
continue;
} else {
seen.add(meter.getProgressName());
lastReport=true;
lastReport = true;
}
}
String progress = meter.getProgressName() + ": " + formatProgress(meter.getProgress()) + "/" + meter.getProgressState() +
" (details: " + meter.getProgressDetails()+")" + (lastReport ? " (last report)" : "");
String progress =
meter.getProgressName() + ": " + formatProgress(meter.getProgressRatio()) + "/" + meter.getRunState() +
" (details: " + meter.getProgressSummary() + ")" + (lastReport ? " (last report)" : "");
switch (indicatorMode) {
case console:

View File

@ -16,11 +16,11 @@ package io.nosqlbench.engine.core;
import io.nosqlbench.engine.api.activityapi.core.Activity;
import io.nosqlbench.engine.api.activityapi.core.ActivityType;
import io.nosqlbench.engine.api.activityapi.core.ProgressMeter;
import io.nosqlbench.engine.api.activityimpl.ActivityDef;
import io.nosqlbench.engine.api.activityimpl.ParameterMap;
import io.nosqlbench.nb.api.errors.BasicError;
import io.nosqlbench.engine.api.activityimpl.ProgressAndStateMeter;
import io.nosqlbench.engine.api.metrics.ActivityMetrics;
import io.nosqlbench.nb.api.errors.BasicError;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -332,9 +332,9 @@ public class ScenarioController {
*
* @param waitTimeMillis grace period during which an activity may cooperatively shut down
*/
public void forceStopScenario(int waitTimeMillis) {
public void forceStopScenario(int waitTimeMillis, boolean rethrow) {
logger.debug("Scenario force stopped.");
activityExecutors.values().forEach(a -> a.forceStopExecutor(waitTimeMillis));
activityExecutors.values().forEach(a -> a.forceStopScenarioAndThrow(waitTimeMillis, rethrow));
}
/**
@ -414,7 +414,11 @@ public class ScenarioController {
return activityMap;
}
public Collection<ProgressMeter> getProgressMeters() {
return this.activityExecutors.values().stream().map(e -> (ProgressMeter) e).collect(Collectors.toList());
public Collection<ProgressAndStateMeter> getProgressMeters() {
List<ProgressAndStateMeter> indicators = new ArrayList<>();
for (ActivityExecutor ae : activityExecutors.values()) {
indicators.add(new ProgressAndStateMeter(ae.getProgressMeter(), ae.getActivity()));
}
return indicators;
}
}

View File

@ -26,16 +26,12 @@ are automatically included when NoSQLBench is built.
## NoSQLBench Slack
There is a new slack channel at nosqlbench.slack.com. In order to access the slack channel, you'll need an invite. You
can get that through this simple form, which will send you an invite in email:
[Slack Invite](https://docs.google.com/forms/d/e/1FAIpQLSdUOJ8iAPqyxsLfh1nBBsKShI53RAeuzYW4bKExmRMWjj4ufQ/viewform).
This is just a simple google form that automates the invite process.
Please click the
[Invite Link for nosqlbench.slack.com](https://join.slack.com/t/nosqlbench/shared_invite/zt-grrg64g3-6SeVi2jaum0cxp51WnvOVA)
to join us.
Please join it if you are a new or existing NoSQLBench user and help us get it going!
## General Feedback
These guidelines are mirrored at the

View File

@ -10,6 +10,8 @@ import javax.inject.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
@Service(WebServiceObject.class)
@ -18,6 +20,9 @@ import javax.ws.rs.core.MediaType;
public class ServiceStatusEndpoint implements WebServiceObject {
private final static Logger logger = LogManager.getLogger(AutoDocsWebService.class);
@Context
private Configuration config;
@GET
@Produces(MediaType.APPLICATION_JSON)
public boolean isEnabled() {