- fixed bug in validator causing it to validate files in excluded directories and updated the pre-commit hook
 - hardware uniqueness is now based on the cpu, memory, storage, and storage_type fields, fixes #230
 - initial settings of a minval/maxval for a session knob are calculated based on its resource type and the user's hardware
 - squashed migrations
This commit is contained in:
Dana Van Aken 2019-10-03 02:08:11 -04:00
parent c14a337695
commit e676433ece
26 changed files with 408 additions and 442 deletions

View File

@ -14,29 +14,35 @@
# ln -s ../../script/git-hooks/pre-commit ./pre-commit
FILES=$(git diff --name-only HEAD --cached --diff-filter=d | grep '\.\(py\)$')
FILES=$(git diff --name-only --cached --diff-filter=d | grep -E '*\.(py|java)$')
SERVER_TESTS_RESULT=0
WEBSITE_TESTS_RESULT=0
ANALYSIS_TESTS_RESULT=0
CONTROLLER_TESTS_RESULT=0
VALIDATOR_RESULT=0
if [ -n "$FILES" ]; then
# Uncomment to run the server tests
# cd server/website && python manage.py test -v 2
# SERVER_TESTS_RESULT=$?
# Uncomment to run the website tests
# cd server/website && python3 manage.py test --noinput -v 2
# WEBSITE_TESTS_RESULT=$?
# cd ../..
# Uncomment to run the analysis tests
# cd server && python3 -m unittest discover -s analysis/tests -v
# ANALYSIS_TESTS_RESULT=$?
# cd ..
# Uncomment to run the controller tests
# cd controller && gradle build -q
# CONTROLLER_TESTS_RESULT=$?
# cd ..
# Run source code validator
python script/validators/source_validator.py $FILES
python3 script/validators/source_validator.py $FILES
VALIDATOR_RESULT=$?
if [ "$VALIDATOR_RESULT" -ne 0 ] || [ "$SERVER_TESTS_RESULT" -ne 0 ] || [ "$CONTROLLER_TESTS_RESULT" -ne 0 ]; then
if [ "$VALIDATOR_RESULT" -ne 0 ] || [ "$WEBSITE_TESTS_RESULT" -ne 0 ] || [ "$ANALYSIS_TESTS_RESULT" -ne 0 ] || [ "$CONTROLLER_TESTS_RESULT" -ne 0 ]; then
echo " +------------------------------------------------------------+"
echo " | |"
@ -45,8 +51,13 @@ if [ -n "$FILES" ]; then
echo " +------------------------------------------------------------+"
echo ""
if [ "$SERVER_TESTS_RESULT" -ne 0 ]; then
echo " FAILED server tests!"
if [ "$WEBSITE_TESTS_RESULT" -ne 0 ]; then
echo " FAILED website tests!"
echo ""
fi
if [ "$ANALYSIS_TESTS_RESULT" -ne 0 ]; then
echo " FAILED analysis tests!"
echo ""
fi

View File

@ -18,7 +18,6 @@ import logging
import imp
import os
import re
import subprocess
import sys
import json
import functools
@ -67,6 +66,9 @@ EXCLUDE_DIRECTORIES = [
# Django manage.py extensions
os.path.join(OTTERTUNE_DIR, "server/website/website/management"),
# Old management scripts
os.path.join(OTTERTUNE_DIR, "server/website/script/management"),
]
# Files that should NOT be checked
@ -151,6 +153,9 @@ def validate_file(file_path):
return True
if not file_path.endswith(".py") and not file_path.endswith(".java"):
return True
for exclude_dir in EXCLUDE_DIRECTORIES:
if file_path.startswith(exclude_dir):
return True
LOG.debug("Validating file: %s", file_path)
status = True
@ -172,13 +177,25 @@ def validate_file(file_path):
# Validate all the files in the root_dir passed as argument
def validate_dir(root_dir):
if root_dir in EXCLUDE_DIRECTORIES:
for exclude_dir in EXCLUDE_DIRECTORIES:
if root_dir.startswith(exclude_dir):
return True
status = True
for root, dirs, files in os.walk(root_dir): # pylint: disable=not-an-iterable
# Remove excluded dirs from list
dirs[:] = [d for d in dirs if os.path.join(root, d) not in EXCLUDE_DIRECTORIES]
valid_dirs = []
for d in dirs:
valid = True
for exclude_dir in EXCLUDE_DIRECTORIES:
if d.startswith(exclude_dir):
valid = False
break
if valid:
valid_dirs.append(d)
dirs[:] = valid_dirs
# Validate files
for file_path in files:
file_path = os.path.join(root, file_path)
@ -187,6 +204,35 @@ def validate_dir(root_dir):
return status
def get_git_files(state):
if state == 'staged':
# Files staged for commit
cmd = r"git diff --name-only --cached --diff-filter=d | grep -E '*\.(py|java)$'"
elif state == 'unstaged':
# Tracked files not staged for commit
cmd = r"git diff --name-only --diff-filter=d | grep -E '*\.(py|java)$'"
elif state == 'untracked':
# Untracked files not staged for commit
cmd = r"git ls-files --other --exclude-standard | grep -E '*\.(py|java)$'"
with settings(warn_only=True):
res = local(cmd, capture=True)
if res.succeeded:
targets = res.stdout.strip().split('\n')
if not targets:
LOG.warning("No %s files found.", state)
else:
LOG.error("An error occurred while fetching %s files (exit code %d). "
"Exiting...\n\n%s\n", state, res.return_code, res.stderr)
sys.exit(EXIT_FAILURE)
return targets
# ==============================================
# VALIDATOR FUNCTION DEFINITIONS
# ==============================================
@ -204,9 +250,12 @@ def check_pylint(file_path, config_path=None):
with settings(warn_only=True), quiet():
res = local('pylint {} {}'.format(' '.join(options), file_path), capture=True)
if res.stdout == '':
assert res.return_code == 0, 'return_code={}, expected=0\n{}'.format(
res.return_code, res.stderr)
if res.return_code != 0:
raise Exception(
'An error occurred while running pylint on {} (exit code {}).\n\n{}\n'.format(
file_path, res.return_code, res.stderr))
return True, None
output = []
@ -393,40 +442,66 @@ def main():
parser = argparse.ArgumentParser(description="Validate OtterTune's source code")
parser.add_argument('paths', metavar='PATH', type=str, nargs='*',
help='Files or directories to (recursively) validate')
parser.add_argument('-v', '--verbose', action='store_true',
help='Enable verbose output')
parser.add_argument('--staged-files', action='store_true',
help='Apply the selected action(s) to all staged files (git)')
parser.add_argument('--unstaged-files', action='store_true',
help='Apply the selected action(s) to all unstaged tracked files (git)')
parser.add_argument('--untracked-files', action='store_true',
help='Apply the selected action(s) to all untracked files (git)')
args = parser.parse_args()
if args.verbose:
LOG.setLevel(logging.DEBUG)
LOG.info('\nRunning source validators:\n%s\n',
'\n'.join(' ' + v.name for v in VALIDATORS))
for validator in VALIDATORS:
if not validate_validator(validator.modules, validator.config_path):
sys.exit(EXIT_FAILURE)
targets = []
if args.paths or args.staged_files or args.unstaged_files or args.untracked_files:
if args.paths:
targets += args.paths
if args.staged_files:
targets = [os.path.abspath(os.path.join(OTTERTUNE_DIR, f))
for f in subprocess.check_output(["git", "diff", "--name-only", "HEAD",
"--cached", "--diff-filter=d"]).split()]
targets += get_git_files('staged')
if args.unstaged_files:
targets += get_git_files('unstaged')
if args.untracked_files:
targets += get_git_files('untracked')
if not targets:
LOG.error("No staged files or not calling from a repository. Exiting...")
LOG.error("No files/directories found. Exiting...")
sys.exit(EXIT_FAILURE)
elif args.paths:
targets = args.paths
else:
targets = DEFAULT_DIRS
targets = sorted(os.path.abspath(t) for t in targets)
LOG.info('\nFiles/directories to validate:\n%s\n',
'\n'.join(' ' + t for t in targets))
status = True
for target in targets:
target = os.path.abspath(target)
if os.path.isfile(target):
LOG.debug("Scanning file: %s\n", target)
status = validate_file(target)
target_status = validate_file(target)
elif os.path.isdir(target):
LOG.debug("Scanning directory: %s\n", target)
status = validate_dir(target)
target_status = validate_dir(target)
else:
LOG.error("%s isn't a file or directory", target)
sys.exit(EXIT_FAILURE)
if not target_status:
status = False
if not status:
LOG.info(SEPARATOR + '\n')
LOG.info("Validation NOT successful\n")

View File

@ -3,6 +3,7 @@ celery==3.1.23
Django==1.10.1
django-celery==3.2.1
django-debug-toolbar==1.5
django-db-logger>=0.1.7
django-request-logging==0.4.6
mock==2.0.0
Fabric3==1.13.1.post1

View File

@ -138,6 +138,18 @@ class SessionViewsTests(TestCase):
fixtures = ['test_website.json']
post_data = {
'name': 'test_create_basic_session',
'description': 'testing create basic session...',
'tuning_session': 'no_tuning_session',
'algorithm': 1,
'cpu': '2',
'memory': '16',
'storage': '32',
'storage_type': 5,
'dbms': 1
}
def setUp(self):
self.client.login(username=TEST_USERNAME, password=TEST_PASSWORD)
@ -154,17 +166,7 @@ class SessionViewsTests(TestCase):
def test_create_basic_session_ok(self):
form_addr = reverse('new_session', kwargs={'project_id': TEST_PROJECT_ID})
post_data = {
'name': 'test_create_basic_session',
'description': 'testing create basic session...',
'tuning_session': 'no_tuning_session',
'algorithm': 1,
'cpu': '2',
'memory': '16.0',
'storage': '32',
'dbms': 1
}
response = self.client.post(form_addr, post_data, follow=True)
response = self.client.post(form_addr, self.post_data, follow=True)
self.assertEqual(response.status_code, 200)
session_id = response.context['session'].pk
self.assertRedirects(response, reverse('session',
@ -173,17 +175,8 @@ class SessionViewsTests(TestCase):
def test_create_tuning_session_ok(self):
form_addr = reverse('new_session', kwargs={'project_id': TEST_PROJECT_ID})
post_data = {
'name': 'test_create_basic_session',
'description': 'testing create basic session...',
'tuning_session': 'tuning_session',
'cpu': '2',
'memory': '16.0',
'storage': '32',
'algorithm': 1,
'dbms': 1,
'target_objective': 'throughput_txn_per_sec'
}
post_data = dict(self.post_data)
post_data.update(tuning_session='tuning_session')
response = self.client.post(form_addr, post_data, follow=True)
self.assertEqual(response.status_code, 200)
session_id = response.context['session'].pk
@ -201,17 +194,8 @@ class SessionViewsTests(TestCase):
def test_edit_basic_session_ok(self):
form_addr = reverse('edit_session', kwargs={'project_id': TEST_PROJECT_ID,
'session_id': TEST_BASIC_SESSION_ID})
post_data = {
'name': 'new_session_name',
'description': 'testing edit basic session...',
'tuning_session': 'tuning_session',
'cpu': '2',
'memory': '16.0',
'storage': '32',
'algorithm': 1,
'dbms': 1,
'target_objective': 'throughput_txn_per_sec'
}
post_data = dict(self.post_data)
post_data.update(name='new_session_name')
response = self.client.post(form_addr, post_data, follow=True)
self.assertEqual(response.status_code, 200)
self.assertRedirects(response, reverse('session',
@ -255,18 +239,9 @@ class SessionViewsTests(TestCase):
def test_delete_multiple_sessions(self):
create_form_addr = reverse('new_session', kwargs={'project_id': TEST_PROJECT_ID})
session_ids = []
post_data = dict(self.post_data)
for i in range(5):
post_data = {
'name': 'session_{}'.format(i),
'description': "",
'tuning_session': 'no_tuning_session',
'cpu': '2',
'memory': '16.0',
'storage': '32',
'algorithm': 1,
'dbms': 1,
'target_objective': 'throughput_txn_per_sec'
}
post_data.update(name='session_{}'.format(i))
response = self.client.post(create_form_addr, post_data, follow=True)
self.assertEqual(response.status_code, 200)
session_ids.append(response.context['session'].pk)

View File

@ -4,6 +4,7 @@
# Copyright (c) 2017-18, Carnegie Mellon University Database Group
#
from django.contrib import admin
from django.db.utils import ProgrammingError
from django.utils.html import format_html
from django_db_logger.admin import StatusLogAdmin
from django_db_logger.models import StatusLog
@ -185,6 +186,9 @@ UNUSED_DJCELERY_MODELS = (
djcelery_models.WorkerState,
)
try:
for model in UNUSED_DJCELERY_MODELS:
if model.objects.count() == 0:
admin.site.unregister(model)
except ProgrammingError:
pass

View File

@ -84,7 +84,7 @@
"category": "Resource Usage / Asynchronous Behavior",
"maxval": 262143,
"dbms": 4,
"resource": 4,
"resource": 2,
"name": "global.max_worker_processes",
"minval": 0,
"default": 8,

View File

@ -3095,7 +3095,7 @@
"summary": "Sets the maximum number of parallel processes per executor node.",
"unit": "3",
"description": "",
"resource": "4"
"resource": "2"
},
"model": "website.KnobCatalog"
},
@ -3255,7 +3255,7 @@
"summary": "Sets the WAL size that triggers a checkpoint.",
"unit": "1",
"description": "",
"resource": "4"
"resource": "3"
},
"model": "website.KnobCatalog"
},
@ -3275,7 +3275,7 @@
"summary": "Maximum number of concurrent worker processes.",
"unit": "3",
"description": "",
"resource": "4"
"resource": "2"
},
"model": "website.KnobCatalog"
},
@ -3315,7 +3315,7 @@
"summary": "Sets the minimum size to shrink the WAL to.",
"unit": "1",
"description": "",
"resource": "4"
"resource": "3"
},
"model": "website.KnobCatalog"
},

View File

@ -12,13 +12,11 @@
},
{
"fields": {
"type": 1,
"name": "New Hardware",
"cpu": 4,
"memory": 16.0,
"storage": "32",
"storage_type": "",
"additional_specs": ""
"memory": 16,
"storage": 32,
"storage_type": 5,
"additional_specs": null
},
"model": "website.Hardware",
"pk": 1

File diff suppressed because one or more lines are too long

View File

@ -10,9 +10,9 @@ Created on Jul 25, 2017
'''
from django import forms
from django.db.models import Max
from .models import Session, Project, Hardware, SessionKnob
from .types import StorageType
class NewResultForm(forms.Form):
@ -44,18 +44,20 @@ class SessionForm(forms.ModelForm):
required=False,
label='Get new upload code')
cpu = forms.IntegerField(label='Number of Processors')
memory = forms.FloatField(label='RAM (GB)')
storage = forms.IntegerField(label='Storage (GB)')
cpu = forms.IntegerField(label='Number of CPUs', min_value=1)
memory = forms.IntegerField(label='Memory (GB)', min_value=1)
storage = forms.IntegerField(label='Storage (GB)', min_value=1)
storage_type = forms.ChoiceField(label='Storage Type', choices=StorageType.choices())
def __init__(self, *args, **kwargs):
super(SessionForm, self).__init__(*args, **kwargs)
self.fields['description'].required = False
self.fields['target_objective'].required = False
self.fields['tuning_session'].required = True
self.fields['cpu'].initial = 2
self.fields['memory'].initial = 16.0
self.fields['cpu'].initial = 4
self.fields['memory'].initial = 16
self.fields['storage'].initial = 32
self.fields['storage_type'].initial = StorageType.SSD
def save(self, commit=True):
model = super(SessionForm, self).save(commit=False)
@ -63,23 +65,14 @@ class SessionForm(forms.ModelForm):
cpu2 = self.cleaned_data['cpu']
memory2 = self.cleaned_data['memory']
storage2 = self.cleaned_data['storage']
storage_type2 = self.cleaned_data['storage_type']
if hasattr(model, 'hardware'):
model.hardware.cpu = cpu2
model.hardware.memory = memory2
model.hardware.storage = storage2
model.hardware.save()
else:
last_type = Hardware.objects.aggregate(Max('type'))['type__max']
if last_type is None:
last_type = 0
model.hardware = Hardware.objects.create(type=last_type + 1,
name='New Hardware',
cpu=cpu2,
hardware, _ = Hardware.objects.get_or_create(cpu=cpu2,
memory=memory2,
storage=storage2,
storage_type='Default',
additional_specs='{}')
storage_type=storage_type2)
model.hardware = hardware
if commit:
model.save()

View File

@ -26,7 +26,7 @@ class Command(BaseCommand):
metavar='SESSIONNAME',
help='Specifies the name of the session.')
parser.add_argument(
'--upload-code',
'--uploadcode',
metavar='UPLOADCODE',
default=None,
help='Specifies the value to set the upload code to.')
@ -39,7 +39,7 @@ class Command(BaseCommand):
project__name=projectname,
name=sessionname).first()
if session:
upload_code = options['upload_code'] or MediaUtil.upload_code_generator()
upload_code = options['uploadcode'] or MediaUtil.upload_code_generator()
session.upload_code = upload_code
session.save()
self.stdout.write(self.style.NOTICE(upload_code))

View File

@ -87,7 +87,7 @@ class Command(BaseCommand):
pipe = '' if 'console' in settings.LOGGING['loggers']['celery']['handlers'] \
else '> /dev/null 2>&1'
with lcd(settings.PROJECT_ROOT), hide('command'):
with lcd(settings.PROJECT_ROOT), hide('commands'):
if not options['celerybeat_only']:
local(self.celery_cmd(
cmd='celery worker', opts=' '.join(celery_options), pipe=pipe))
@ -103,7 +103,7 @@ class Command(BaseCommand):
time.sleep(1)
wait_sec += 1
for i in range(len(pidfiles)):
for i in range(len(pidfiles))[::-1]:
if os.path.exists(pidfiles[i]):
pidfiles.pop(i)

View File

@ -1,12 +1,10 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.1 on 2018-03-26 02:21
# Generated by Django 1.10.1 on 2019-10-02 07:59
from __future__ import unicode_literals
from django.conf import settings
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import re
class Migration(migrations.Migration):
@ -37,7 +35,7 @@ class Migration(migrations.Migration):
name='DBMSCatalog',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('type', models.IntegerField(choices=[(1, b'MySQL'), (2, b'Postgres'), (3, b'Db2'), (4, b'Oracle'), (5, b'SQL Server'), (6, b'SQLite'), (7, b'HStore'), (8, b'Vector'), (9, b'MyRocks')])),
('type', models.IntegerField(choices=[(1, 'MySQL'), (2, 'Postgres'), (3, 'Db2'), (4, 'Oracle'), (6, 'SQLite'), (7, 'HStore'), (8, 'Vector'), (5, 'SQL Server'), (9, 'MyRocks')])),
('version', models.CharField(max_length=16)),
],
options={
@ -48,36 +46,31 @@ class Migration(migrations.Migration):
name='Hardware',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('type', models.IntegerField()),
('name', models.CharField(max_length=32)),
('cpu', models.IntegerField()),
('memory', models.FloatField()),
('storage', models.CharField(max_length=64, validators=[django.core.validators.RegexValidator(re.compile('^\\d+(?:\\,\\d+)*\\Z'), code='invalid', message='Enter only digits separated by commas.')])),
('storage_type', models.CharField(max_length=16)),
('additional_specs', models.TextField(null=True)),
('cpu', models.IntegerField(default=4, verbose_name='Number of CPUs')),
('memory', models.IntegerField(default=16, verbose_name='Memory (GB)')),
('storage', models.IntegerField(default=32, verbose_name='Storage (GB)')),
('storage_type', models.IntegerField(choices=[(5, 'SSD'), (10, 'HDD')], default=5, verbose_name='Storage Type')),
('additional_specs', models.TextField(default=None, null=True)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='KnobCatalog',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=128)),
('vartype', models.IntegerField(choices=[(1, b'STRING'), (2, b'INTEGER'), (3, b'REAL'), (4, b'BOOL'), (5, b'ENUM'), (6, b'TIMESTAMP')], verbose_name=b'variable type')),
('unit', models.IntegerField(choices=[(1, b'bytes'), (2, b'milliseconds'), (3, b'other')])),
('vartype', models.IntegerField(choices=[(1, 'STRING'), (2, 'INTEGER'), (3, 'REAL'), (4, 'BOOL'), (5, 'ENUM'), (6, 'TIMESTAMP')], verbose_name='variable type')),
('unit', models.IntegerField(choices=[(1, 'bytes'), (2, 'milliseconds'), (3, 'other')])),
('category', models.TextField(null=True)),
('summary', models.TextField(null=True, verbose_name=b'description')),
('summary', models.TextField(null=True, verbose_name='description')),
('description', models.TextField(null=True)),
('scope', models.CharField(max_length=16)),
('minval', models.CharField(max_length=32, null=True, verbose_name=b'minimum value')),
('maxval', models.CharField(max_length=32, null=True, verbose_name=b'maximum value')),
('default', models.TextField(verbose_name=b'default value')),
('enumvals', models.TextField(null=True, verbose_name=b'valid values')),
('minval', models.CharField(max_length=32, null=True, verbose_name='minimum value')),
('maxval', models.CharField(max_length=32, null=True, verbose_name='maximum value')),
('default', models.TextField(verbose_name='default value')),
('enumvals', models.TextField(null=True, verbose_name='valid values')),
('context', models.CharField(max_length=32)),
('tunable', models.BooleanField(verbose_name=b'tunable')),
('resource', models.IntegerField(choices=[(1, b'Memory'), (2, b'CPU'), (3, b'Storage'), (4, b'Other')], default=4)),
('tunable', models.BooleanField(verbose_name='tunable')),
('resource', models.IntegerField(choices=[(1, 'Memory'), (2, 'CPU'), (3, 'Storage'), (4, 'Other')], default=4)),
('dbms', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.DBMSCatalog')),
],
options={
@ -103,10 +96,10 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=128)),
('vartype', models.IntegerField(choices=[(1, b'STRING'), (2, b'INTEGER'), (3, b'REAL'), (4, b'BOOL'), (5, b'ENUM'), (6, b'TIMESTAMP')])),
('summary', models.TextField(null=True, verbose_name=b'description')),
('vartype', models.IntegerField(choices=[(1, 'STRING'), (2, 'INTEGER'), (3, 'REAL'), (4, 'BOOL'), (5, 'ENUM'), (6, 'TIMESTAMP')])),
('summary', models.TextField(null=True, verbose_name='description')),
('scope', models.CharField(max_length=16)),
('metric_type', models.IntegerField(choices=[(1, b'COUNTER'), (2, b'INFO'), (3,b'STATISTICS')])),
('metric_type', models.IntegerField(choices=[(1, 'COUNTER'), (2, 'INFO'), (3, 'STATISTICS')])),
('dbms', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.DBMSCatalog')),
],
options={
@ -131,7 +124,7 @@ class Migration(migrations.Migration):
name='PipelineData',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('task_type', models.IntegerField(choices=[(1, b'Pruned Metrics'), (2, b'Ranked Knobs'), (3, b'Knob Data'), (4, b'Metric Data')])),
('task_type', models.IntegerField(choices=[(1, 'Pruned Metrics'), (2, 'Ranked Knobs'), (3, 'Knob Data'), (4, 'Metric Data')])),
('data', models.TextField()),
('creation_time', models.DateTimeField()),
],
@ -151,7 +144,7 @@ class Migration(migrations.Migration):
name='Project',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=64, verbose_name=b'project name')),
('name', models.CharField(max_length=64, verbose_name='project name')),
('description', models.TextField(blank=True, null=True)),
('creation_time', models.DateTimeField()),
('last_update', models.DateTimeField()),
@ -183,17 +176,18 @@ class Migration(migrations.Migration):
name='Session',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=64, verbose_name=b'session name')),
('name', models.CharField(max_length=64, verbose_name='session name')),
('description', models.TextField(blank=True, null=True)),
('ddpg_actor_model', models.BinaryField(null=True, blank=True)),
('ddpg_critic_model', models.BinaryField(null=True, blank=True)),
('ddpg_reply_memory', models.BinaryField(null=True, blank=True)),
('dnn_model', models.BinaryField(null=True, blank=True)),
('algorithm', models.IntegerField(choices=[(1, 'Gaussian Process Bandits'), (2, 'Deep Deterministic Policy Gradients'), (3, 'Deep Neural Network')], default=1)),
('ddpg_actor_model', models.BinaryField(blank=True, null=True)),
('ddpg_critic_model', models.BinaryField(blank=True, null=True)),
('ddpg_reply_memory', models.BinaryField(blank=True, null=True)),
('dnn_model', models.BinaryField(blank=True, null=True)),
('creation_time', models.DateTimeField()),
('last_update', models.DateTimeField()),
('upload_code', models.CharField(max_length=30, unique=True)),
('tuning_session', models.CharField(choices=[('tuning_session', 'Tuning Session'), ('no_tuning_session', 'No Tuning'), ('randomly_generate', 'Randomly Generate')], max_length=64)),
('target_objective', models.CharField(choices=[(b'throughput_txn_per_sec', b'Throughput'), (b'99th_lat_ms', b'99 Percentile Latency')], max_length=64, null=True)),
('tuning_session', models.CharField(choices=[('tuning_session', 'Tuning Session'), ('no_tuning_session', 'No Tuning'), ('randomly_generate', 'Randomly Generate')], default='tuning_session', max_length=64)),
('target_objective', models.CharField(choices=[('throughput_txn_per_sec', 'Throughput'), ('99th_lat_ms', '99 Percentile Latency')], max_length=64, null=True)),
('dbms', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.DBMSCatalog')),
('hardware', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.Hardware')),
('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.Project')),
@ -203,11 +197,26 @@ class Migration(migrations.Migration):
'abstract': False,
},
),
migrations.CreateModel(
name='SessionKnob',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('minval', models.CharField(max_length=32, null=True, verbose_name='minimum value')),
('maxval', models.CharField(max_length=32, null=True, verbose_name='maximum value')),
('tunable', models.BooleanField(verbose_name='tunable')),
('knob', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.KnobCatalog')),
('session', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.Session')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Workload',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=128, verbose_name=b'workload name')),
('name', models.CharField(max_length=128, verbose_name='workload name')),
('status', models.IntegerField(choices=[(1, 'MODIFIED'), (2, 'PROCESSING'), (3, 'PROCESSED')], default=1, editable=False)),
('dbms', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.DBMSCatalog')),
('hardware', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.Hardware')),
],
@ -215,7 +224,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='result',
name='session',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.Session', verbose_name=b'session name'),
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.Session', verbose_name='session name'),
),
migrations.AddField(
model_name='result',
@ -225,7 +234,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='pipelinedata',
name='pipeline_run',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.PipelineRun'),
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.PipelineRun', verbose_name='group'),
),
migrations.AddField(
model_name='pipelinedata',
@ -242,6 +251,10 @@ class Migration(migrations.Migration):
name='session',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.Session'),
),
migrations.AlterUniqueTogether(
name='hardware',
unique_together=set([('cpu', 'memory', 'storage', 'storage_type')]),
),
migrations.AddField(
model_name='backupdata',
name='result',

View File

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.1 on 2018-08-02 07:58
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('website', '0002_enable_compression'),
]
operations = [
migrations.AddField(
model_name='workload',
name='status',
field=models.IntegerField(choices=[(1, 'MODIFIED'), (2, 'PROCESSING'), (3, 'PROCESSED')], default=1, editable=False),
)
]

View File

@ -43,7 +43,7 @@ def unload_initial_data(apps, schema_editor):
class Migration(migrations.Migration):
dependencies = [
('website', '0003_background_task_optimization'),
('website', '0002_enable_compression'),
]
operations = [

View File

@ -1,138 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.1 on 2019-08-07 18:18
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('website', '0004_load_initial_data'),
]
operations = [
migrations.CreateModel(
name='SessionKnob',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('minval', models.CharField(max_length=32, null=True, verbose_name='minimum value')),
('maxval', models.CharField(max_length=32, null=True, verbose_name='maximum value')),
('tunable', models.BooleanField(verbose_name='tunable')),
],
options={
'abstract': False,
},
),
migrations.AlterField(
model_name='dbmscatalog',
name='type',
field=models.IntegerField(choices=[(1, 'MySQL'), (2, 'Postgres'), (3, 'Db2'), (4, 'Oracle'), (6, 'SQLite'), (7, 'HStore'), (8, 'Vector'), (5, 'SQL Server'), (9, 'MyRocks')]),
),
migrations.AlterField(
model_name='knobcatalog',
name='default',
field=models.TextField(verbose_name='default value'),
),
migrations.AlterField(
model_name='knobcatalog',
name='enumvals',
field=models.TextField(null=True, verbose_name='valid values'),
),
migrations.AlterField(
model_name='knobcatalog',
name='maxval',
field=models.CharField(max_length=32, null=True, verbose_name='maximum value'),
),
migrations.AlterField(
model_name='knobcatalog',
name='minval',
field=models.CharField(max_length=32, null=True, verbose_name='minimum value'),
),
migrations.AlterField(
model_name='knobcatalog',
name='resource',
field=models.IntegerField(choices=[(1, 'Memory'), (2, 'CPU'), (3, 'Storage'), (4, 'Other')], default=4),
),
migrations.AlterField(
model_name='knobcatalog',
name='summary',
field=models.TextField(null=True, verbose_name='description'),
),
migrations.AlterField(
model_name='knobcatalog',
name='tunable',
field=models.BooleanField(verbose_name='tunable'),
),
migrations.AlterField(
model_name='knobcatalog',
name='unit',
field=models.IntegerField(choices=[(1, 'bytes'), (2, 'milliseconds'), (3, 'other')]),
),
migrations.AlterField(
model_name='knobcatalog',
name='vartype',
field=models.IntegerField(choices=[(1, 'STRING'), (2, 'INTEGER'), (3, 'REAL'), (4, 'BOOL'), (5, 'ENUM'), (6, 'TIMESTAMP')], verbose_name='variable type'),
),
migrations.AlterField(
model_name='metriccatalog',
name='metric_type',
field=models.IntegerField(choices=[(1, 'COUNTER'), (2, 'INFO'), (3, 'STATISTICS')]),
),
migrations.AlterField(
model_name='metriccatalog',
name='summary',
field=models.TextField(null=True, verbose_name='description'),
),
migrations.AlterField(
model_name='metriccatalog',
name='vartype',
field=models.IntegerField(choices=[(1, 'STRING'), (2, 'INTEGER'), (3, 'REAL'), (4, 'BOOL'), (5, 'ENUM'), (6, 'TIMESTAMP')]),
),
migrations.AlterField(
model_name='pipelinedata',
name='task_type',
field=models.IntegerField(choices=[(1, 'Pruned Metrics'), (2, 'Ranked Knobs'), (3, 'Knob Data'), (4, 'Metric Data')]),
),
migrations.AlterField(
model_name='project',
name='name',
field=models.CharField(max_length=64, verbose_name='project name'),
),
migrations.AlterField(
model_name='result',
name='session',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.Session', verbose_name='session name'),
),
migrations.AlterField(
model_name='session',
name='name',
field=models.CharField(max_length=64, verbose_name='session name'),
),
migrations.AlterField(
model_name='session',
name='target_objective',
field=models.CharField(choices=[('throughput_txn_per_sec', 'Throughput'), ('99th_lat_ms', '99 Percentile Latency')], max_length=64, null=True),
),
migrations.AlterField(
model_name='session',
name='tuning_session',
field=models.CharField(choices=[('tuning_session', 'Tuning Session'), ('no_tuning_session', 'No Tuning'), ('randomly_generate', 'Randomly Generate')], default='tuning_session', max_length=64),
),
migrations.AlterField(
model_name='workload',
name='name',
field=models.CharField(max_length=128, verbose_name='workload name'),
),
migrations.AddField(
model_name='sessionknob',
name='knob',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.KnobCatalog'),
),
migrations.AddField(
model_name='sessionknob',
name='session',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.Session'),
),
]

View File

@ -1,28 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.1 on 2019-09-10 04:25
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('website', '0005_adding_session_knob'),
]
operations = [
migrations.AddField(
model_name='session',
name='algorithm',
field=models.IntegerField(choices=[(1, 'Ottertune Default'),
(2, 'Deep Deterministic Policy Gradients'),
(3, 'Deep Neural Network')], default=1),
),
migrations.AlterField(
model_name='pipelinedata',
name='pipeline_run',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.PipelineRun', verbose_name='group'),
),
]

View File

@ -6,13 +6,12 @@
from collections import namedtuple, OrderedDict
from django.contrib.auth.models import User
from django.core.validators import validate_comma_separated_integer_list
from django.db import models, DEFAULT_DB_ALIAS
from django.utils.timezone import now
from .types import (DBMSType, LabelStyleType, MetricType, KnobUnitType,
PipelineTaskType, VarType, KnobResourceType,
WorkloadStatusType, AlgorithmType)
WorkloadStatusType, AlgorithmType, StorageType)
class BaseModel(models.Model):
@ -167,17 +166,21 @@ class Project(BaseModel):
class Hardware(BaseModel):
type = models.IntegerField()
name = models.CharField(max_length=32)
cpu = models.IntegerField()
memory = models.FloatField()
storage = models.CharField(
max_length=64, validators=[validate_comma_separated_integer_list])
storage_type = models.CharField(max_length=16)
additional_specs = models.TextField(null=True)
def __unicode__(self):
return 'CPU:{}, RAM:{}, Storage:{}'.format(self.cpu, self.memory, self.storage)
@property
def name(self):
return '{} CPUs, {}GB RAM, {}GB {}'.format(
self.cpu, self.memory, self.storage, StorageType.name(self.storage_type))
cpu = models.IntegerField(default=4, verbose_name='Number of CPUs')
memory = models.IntegerField(default=16, verbose_name='Memory (GB)')
storage = models.IntegerField(default=32, verbose_name='Storage (GB)')
storage_type = models.IntegerField(choices=StorageType.choices(),
default=StorageType.SSD, verbose_name='Storage Type')
additional_specs = models.TextField(null=True, default=None)
class Meta: # pylint: disable=old-style-class,no-init
unique_together = ('cpu', 'memory', 'storage', 'storage_type')
class Session(BaseModel):
@ -187,7 +190,7 @@ class Session(BaseModel):
dbms = models.ForeignKey(DBMSCatalog)
hardware = models.ForeignKey(Hardware)
algorithm = models.IntegerField(choices=AlgorithmType.choices(),
default=AlgorithmType.OTTERTUNE)
default=AlgorithmType.GPR)
ddpg_actor_model = models.BinaryField(null=True, blank=True)
ddpg_critic_model = models.BinaryField(null=True, blank=True)
ddpg_reply_memory = models.BinaryField(null=True, blank=True)
@ -217,11 +220,11 @@ class Session(BaseModel):
self.target_objective = MetricManager.get_default_objective_function()
def delete(self, using=DEFAULT_DB_ALIAS, keep_parents=False):
targets = KnobData.objects.filter(session=self)
SessionKnob.objects.get(session=self).delete()
results = Result.objects.filter(session=self)
for t in targets:
t.delete()
for r in results:
r.knob_data.delete()
r.metric_data.delete()
r.delete()
super(Session, self).delete(using=DEFAULT_DB_ALIAS, keep_parents=False)

View File

@ -18,6 +18,10 @@ class MyRocksParser(BaseParser):
def transactions_counter(self):
return 'session_status.questions'
@property
def latency_timer(self):
raise NotImplementedError()
def convert_integer(self, int_value, metadata):
converted = None
try:

View File

@ -17,3 +17,7 @@ class OracleParser(BaseParser):
@property
def transactions_counter(self):
return 'global.user commits'
@property
def latency_timer(self):
raise NotImplementedError()

View File

@ -22,6 +22,10 @@ class PostgresParser(BaseParser):
def transactions_counter(self):
return 'pg_stat_database.xact_commit'
@property
def latency_timer(self):
raise NotImplementedError()
def convert_integer(self, int_value, metadata):
converted = None
try:

View File

@ -3,50 +3,80 @@
#
# Copyright (c) 2017-18, Carnegie Mellon University Database Group
#
import logging
from .models import KnobCatalog, SessionKnob
from .types import DBMSType
from .types import DBMSType, KnobResourceType, VarType
LOG = logging.getLogger(__name__)
GB = 1024 ** 3
DEFAULT_TUNABLE_KNOBS = {
DBMSType.POSTGRES: {
"global.checkpoint_completion_target",
"global.default_statistics_target",
"global.effective_cache_size",
"global.maintenance_work_mem",
"global.max_wal_size",
"global.max_worker_processes",
"global.shared_buffers",
"global.temp_buffers",
"global.wal_buffers",
"global.work_mem",
}
}
def turn_knobs_off(session, knob_names):
for knob_name in knob_names:
knob = KnobCatalog.objects.filter(dbms=session.dbms, name=knob_name).first()
SessionKnob.objects.create(session=session,
knob=knob,
minval=knob.minval,
maxval=knob.maxval,
tunable=False)
def set_default_knobs(session):
dbtype = session.dbms.type
default_tunable_knobs = DEFAULT_TUNABLE_KNOBS.get(dbtype)
if not default_tunable_knobs:
default_tunable_knobs = set(KnobCatalog.objects.filter(
dbms=session.dbms, tunable=True).values_list('name', flat=True))
for knob in KnobCatalog.objects.filter(dbms=session.dbms):
tunable = knob.name in default_tunable_knobs
minval = knob.minval
if knob.vartype in (VarType.INTEGER, VarType.REAL):
vtype = int if knob.vartype == VarType.INTEGER else float
minval = vtype(minval)
knob_maxval = vtype(knob.maxval)
if knob.resource == KnobResourceType.CPU:
maxval = session.hardware.cpu * 2
elif knob.resource == KnobResourceType.MEMORY:
maxval = session.hardware.memory * GB
elif knob.resource == KnobResourceType.STORAGE:
maxval = session.hardware.storage * GB
else:
maxval = knob_maxval
# Special cases
if dbtype == DBMSType.POSTGRES:
if knob.name == 'global.work_mem':
maxval /= 50.0
if maxval > knob_maxval:
maxval = knob_maxval
if maxval < minval:
LOG.warning(("Invalid range for session knob '%s': maxval <= minval "
"(minval: %s, maxval: %s). Setting maxval to the vendor setting: %s."),
knob.name, minval, maxval, knob_maxval)
maxval = knob_maxval
maxval = vtype(maxval)
else:
assert knob.resource == KnobResourceType.OTHER
maxval = knob.maxval
def set_knob_tuning_range(session, knob_name, minval, maxval):
knob = KnobCatalog.objects.filter(dbms=session.dbms, name=knob_name).first()
SessionKnob.objects.create(session=session,
knob=knob,
minval=minval,
maxval=maxval,
tunable=True)
def set_default_knobs(session):
if session.dbms.type == DBMSType.POSTGRES and session.dbms.version == '9.6':
turn_knobs_off(session, ["global.backend_flush_after", "global.bgwriter_delay",
"global.bgwriter_flush_after", "global.bgwriter_lru_multiplier",
"global.checkpoint_flush_after", "global.commit_delay",
"global.commit_siblings", "global.deadlock_timeout",
"global.effective_io_concurrency", "global.from_collapse_limit",
"global.join_collapse_limit", "global.maintenance_work_mem",
"global.max_worker_processes",
"global.min_parallel_relation_size", "global.min_wal_size",
"global.seq_page_cost", "global.wal_buffers",
"global.wal_sync_method", "global.wal_writer_delay",
"global.wal_writer_flush_after"])
set_knob_tuning_range(session, "global.checkpoint_completion_target", 0.1, 0.9)
set_knob_tuning_range(session, "global.checkpoint_timeout", 60000, 1800000)
set_knob_tuning_range(session, "global.default_statistics_target", 100, 2048)
set_knob_tuning_range(session, "global.effective_cache_size", 4294967296, 17179869184)
set_knob_tuning_range(session, "global.max_parallel_workers_per_gather", 0, 8)
set_knob_tuning_range(session, "global.max_wal_size", 268435456, 17179869184)
set_knob_tuning_range(session, "global.random_page_cost", 1, 10)
set_knob_tuning_range(session, "global.shared_buffers", 134217728, 12884901888)
set_knob_tuning_range(session, "global.temp_buffers", 8388608, 1073741824)
set_knob_tuning_range(session, "global.work_mem", 4194304, 1073741824)
tunable=tunable)

View File

@ -148,9 +148,10 @@ def aggregate_target_results(result_id, algorithm):
# implement a sampling technique to generate new training data).
newest_result = Result.objects.get(pk=result_id)
has_pipeline_data = PipelineData.objects.filter(workload=newest_result.workload).exists()
if not has_pipeline_data:
LOG.debug("Background tasks haven't ran for this workload yet, picking random data.")
if not has_pipeline_data or newest_result.session.tuning_session == 'randomly_generate':
if not has_pipeline_data and newest_result.session.tuning_session == 'tuning_session':
LOG.debug("Background tasks haven't ran for this workload yet, picking random data.")
result = Result.objects.filter(pk=result_id)
knobs = SessionKnob.objects.get_knobs_for_session(newest_result.session)
@ -160,8 +161,10 @@ def aggregate_target_results(result_id, algorithm):
agg_data['newest_result_id'] = result_id
agg_data['bad'] = True
agg_data['config_recommend'] = random_knob_result
return agg_data, algorithm
LOG.debug('%s: Finished generating a random config.\n\ndata=%s\n',
AlgorithmType.name(algorithm), JSONUtil.dumps(agg_data, pprint=True))
else:
# Aggregate all knob config results tried by the target so far in this
# tuning session and this tuning workload.
target_results = Result.objects.filter(session=newest_result.session,
@ -180,6 +183,9 @@ def aggregate_target_results(result_id, algorithm):
agg_data['X_matrix'] = np.array(cleaned_agg_data[0])
agg_data['X_columnlabels'] = np.array(cleaned_agg_data[1])
LOG.debug('%s: Finished aggregating target results.\n\ndata=%s\n',
AlgorithmType.name(algorithm), JSONUtil.dumps(agg_data, pprint=True))
return agg_data, algorithm
@ -336,19 +342,22 @@ def configuration_recommendation_ddpg(result_info): # pylint: disable=invalid-n
def configuration_recommendation(recommendation_input):
target_data, algorithm = recommendation_input
LOG.info('configuration_recommendation called')
latest_pipeline_run = PipelineRun.objects.get_latest()
if target_data['bad'] is True:
target_data_res = {}
target_data_res['status'] = 'bad'
target_data_res['result_id'] = target_data['newest_result_id']
target_data_res['info'] = 'WARNING: no training data, the config is generated randomly'
target_data_res['recommendation'] = target_data['config_recommend']
target_data_res = dict(
status='bad',
result_id=target_data['newest_result_id'],
info='WARNING: no training data, the config is generated randomly',
recommendation=target_data['config_recommend'],
pipeline_run=target_data['pipeline_run'])
LOG.debug('%s: Skipping configuration recommendation.\n\ndata=%s\n',
AlgorithmType.name(algorithm), JSONUtil.dumps(target_data, pprint=True))
return target_data_res
# Load mapped workload data
mapped_workload_id = target_data['mapped_workload'][0]
latest_pipeline_run = PipelineRun.objects.get(pk=target_data['pipeline_run'])
mapped_workload = Workload.objects.get(pk=mapped_workload_id)
workload_knob_data = PipelineData.objects.get(
pipeline_run=latest_pipeline_run,
@ -563,7 +572,7 @@ def configuration_recommendation(recommendation_input):
session.dnn_model = model_nn.get_weights_bin()
session.save()
elif algorithm == AlgorithmType.OTTERTUNE:
elif algorithm == AlgorithmType.GPR:
# default gpr model
model = GPRGD(length_scale=DEFAULT_LENGTH_SCALE,
magnitude=DEFAULT_MAGNITUDE,
@ -595,11 +604,15 @@ def configuration_recommendation(recommendation_input):
best_config = np.maximum(best_config, X_min_inv)
conf_map = {k: best_config[i] for i, k in enumerate(X_columnlabels)}
conf_map_res = {}
conf_map_res['status'] = 'good'
conf_map_res['result_id'] = target_data['newest_result_id']
conf_map_res['recommendation'] = conf_map
conf_map_res['info'] = 'INFO: training data size is {}'.format(X_scaled.shape[0])
conf_map_res = dict(
status='good',
result_id=target_data['newest_result_id'],
recommendation=conf_map,
info='INFO: training data size is {}'.format(X_scaled.shape[0]),
pipeline_run=latest_pipeline_run.pk)
LOG.debug('%s: Finished selecting the next config.\n\ndata=%s\n',
AlgorithmType.name(algorithm), JSONUtil.dumps(conf_map_res, pprint=True))
return conf_map_res
@ -613,12 +626,19 @@ def load_data_helper(filtered_pipeline_data, workload, task_type):
@task(base=MapWorkload, name='map_workload')
def map_workload(map_workload_input):
target_data, algorithm = map_workload_input
# Get the latest version of pipeline data that's been computed so far.
latest_pipeline_run = PipelineRun.objects.get_latest()
if target_data['bad']:
assert target_data is not None
target_data['pipeline_run'] = None
LOG.debug('%s: Skipping workload mapping.\n\ndata=%s\n',
AlgorithmType.name(algorithm), JSONUtil.dumps(target_data, pprint=True))
return target_data, algorithm
# Get the latest version of pipeline data that's been computed so far.
latest_pipeline_run = PipelineRun.objects.get_latest()
assert latest_pipeline_run is not None
target_data['pipeline_run'] = latest_pipeline_run.pk
newest_result = Result.objects.get(pk=target_data['newest_result_id'])
target_workload = newest_result.workload
@ -752,7 +772,7 @@ def map_workload(map_workload_input):
# Find the best (minimum) score
best_score = np.inf
best_workload_id = None
# scores_info = {workload_id: (workload_name, score)}
best_workload_name = None
scores_info = {}
for workload_id, similarity_score in list(scores.items()):
workload_name = Workload.objects.get(pk=workload_id).name
@ -761,7 +781,9 @@ def map_workload(map_workload_input):
best_workload_id = workload_id
best_workload_name = workload_name
scores_info[workload_id] = (workload_name, similarity_score)
target_data['mapped_workload'] = (best_workload_id, best_workload_name, best_score)
target_data.update(mapped_workload=(best_workload_id, best_workload_name, best_score),
scores=scores_info)
LOG.debug('%s: Finished mapping the workload.\n\ndata=%s\n',
AlgorithmType.name(algorithm), JSONUtil.dumps(target_data, pprint=True))
target_data['scores'] = scores_info
return target_data, algorithm

View File

@ -33,6 +33,10 @@
<td>{{ form.storage.label_tag }}</td>
<td>{{ form.storage }}</td>
</tr>
<tr id="storage_type_row">
<td>{{ form.storage_type.label_tag }}</td>
<td>{{ form.storage_type }}</td>
</tr>
<tr id="algorithm_row">
<td>{{ form.algorithm.label_tag }}</td>
<td>{{ form.algorithm }}</td>
@ -71,6 +75,7 @@ $(function() {
$("#cpu_row").hide();
$("#memory_row").hide();
$("#storage_row").hide();
$("#storage_type_row").hide();
$("#algorithm_row").hide();
} else {
$("#upload_code_row").hide();

View File

@ -173,12 +173,22 @@ class LabelStyleType(BaseType):
class AlgorithmType(BaseType):
OTTERTUNE = 1
GPR = 1
DDPG = 2
DNN = 3
TYPE_NAMES = {
OTTERTUNE: 'Ottertune Default',
GPR: 'Gaussian Process Bandits',
DDPG: 'Deep Deterministic Policy Gradients',
DNN: 'Deep Neural Network',
}
class StorageType(BaseType):
SSD = 5
HDD = 10
TYPE_NAMES = {
SSD: 'SSD',
HDD: 'HDD',
}

View File

@ -314,7 +314,7 @@ def create_or_edit_session(request, project_id, session_id=''):
initial={
'dbms': DBMSCatalog.objects.get(
type=DBMSType.POSTGRES, version='9.6'),
'algorithm': AlgorithmType.OTTERTUNE,
'algorithm': AlgorithmType.GPR,
'target_objective': 'throughput_txn_per_sec',
})
context = {
@ -342,15 +342,12 @@ def edit_knobs(request, project_id, session_id):
instance.save()
return HttpResponse(status=204)
else:
knobs = KnobCatalog.objects.filter(dbms=session.dbms).order_by('-tunable')
knobs = SessionKnob.objects.filter(session=session).order_by('-tunable', 'knob__name')
forms = []
for knob in knobs:
knob_values = model_to_dict(knob)
if SessionKnob.objects.filter(session=session, knob=knob).exists():
new_knob = SessionKnob.objects.filter(session=session, knob=knob)[0]
knob_values["minval"] = new_knob.minval
knob_values["maxval"] = new_knob.maxval
knob_values["tunable"] = new_knob.tunable
knob_values['session'] = session
knob_values['name'] = KnobCatalog.objects.get(pk=knob.knob.pk).name
forms.append(SessionKnobForm(initial=knob_values))
context = {
'project': project,
@ -526,7 +523,7 @@ def handle_result_files(session, files):
result_id = result.pk
response = None
if session.algorithm == AlgorithmType.OTTERTUNE:
if session.algorithm == AlgorithmType.GPR:
response = chain(aggregate_target_results.s(result.pk, session.algorithm),
map_workload.s(),
configuration_recommendation.s()).apply_async()
@ -967,13 +964,17 @@ def give_result(request, upload_code): # pylint: disable=unused-argument
res = Result.objects.get(pk=lastest_result.pk)
response = HttpResponse(JSONUtil.dumps(res.next_configuration),
content_type='application/json')
elif overall_status in ('PENDING', 'RECEIVED', 'STARTED'):
response = HttpResponse("{}: Result not ready".format(overall_status), status=202)
else: # overall_status in ('FAILURE', 'REVOKED', 'RETRY'):
elif overall_status in ('FAILURE', 'REVOKED', 'RETRY'):
msg = "STATUS: {}\nRESULT ID: {}\n".format(overall_status, lastest_result)
if tasks:
failed_task_idx = min(len(tasks) - 1, num_completed + 1)
failed_task = tasks[failed_task_idx]
response = HttpResponse(
"{}: {}".format(overall_status, failed_task.traceback), status=400)
msg += "TRACEBACK: {}".format(failed_task.traceback)
response = HttpResponse(msg, status=400)
else: # overall_status in ('PENDING', 'RECEIVED', 'STARTED'):
response = HttpResponse("{}: Result not ready".format(overall_status), status=202)
return response