tuning-box/tuning_box/library/resource_values.py
Alexander Kislitsky 3705aa1526 Fetching effective resource values optimized
Now only required resource values are fetched on calculating
effective values.
Calculation of effective values optimised to use indexed resource
values insead of going throw nested for loop.
Test for fetching values and effective values for 500 items
(nodes) added. Each resource value contains over than 10000 keys.
Logging added to the resource values fetching.

Change-Id: Icea7ece8a2bc6957bcb00c9d90d8cf59e5ca1da0
Closes-Bug: #1626147
2016-09-22 14:12:37 +03:00

144 lines
5.9 KiB
Python

# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import flask
from flask import current_app as app
import flask_restful
import six
from sqlalchemy import or_
from tuning_box import db
from tuning_box import library
from tuning_box.library import hierarchy_levels
from tuning_box.library import resource_keys_operation
class ResourceValues(flask_restful.Resource):
@db.with_transaction
def put(self, environment_id, levels, resource_id_or_name):
environment = db.Environment.query.get_or_404(environment_id)
res_def = library.get_resource_definition(
resource_id_or_name, environment_id)
level_value = hierarchy_levels.get_environment_level_value(
environment, levels)
esv = db.get_or_create(
db.ResourceValues,
environment=environment,
resource_definition=res_def,
level_value=level_value,
)
esv.values = flask.request.json
return None, 204
def _calculate_effective_values(self, result, level_value,
resource_values_idx, show_lookup,
lookup_path):
level_value_id = getattr(level_value, 'id', None)
if level_value_id in resource_values_idx:
resource_value = resource_values_idx[level_value_id]
if show_lookup:
values = ((k, (v, lookup_path)) for k, v in
six.iteritems(resource_value.values))
overrides = ((k, (v, lookup_path)) for k, v in
six.iteritems(resource_value.overrides))
else:
values = resource_value.values
overrides = resource_value.overrides
result.update(values)
result.update(overrides)
@db.with_transaction
def get(self, environment_id, resource_id_or_name, levels):
app.logger.debug("Getting resource value. Env: %s, "
"resource: %s, levels: %s", environment_id,
resource_id_or_name, levels)
environment = db.Environment.query.get_or_404(environment_id)
res_def = library.get_resource_definition(
resource_id_or_name, environment_id)
level_values = list(hierarchy_levels.iter_environment_level_values(
environment, levels))
level_values_ids = [l.id for l in level_values]
app.logger.debug("Got level values ids: %s", level_values_ids)
if 'effective' in flask.request.args:
app.logger.debug("Getting effective resource value. Env: %s, "
"resource: %s, levels: %s", environment_id,
resource_id_or_name, levels)
show_lookup = 'show_lookup' in flask.request.args
resource_values = db.ResourceValues.query.filter(
or_(
db.ResourceValues.level_value_id.in_(level_values_ids),
db.ResourceValues.level_value_id.is_(None)
),
db.ResourceValues.resource_definition == res_def,
db.ResourceValues.environment == environment
).all()
app.logger.debug("Processing values for resource: %s, env: %s. "
"Loaded resource values: %s",
res_def.id, environment.id, len(resource_values))
# Creating index of resource_values by level_value_id
resource_values_idx = {r.level_value_id: r
for r in resource_values}
app.logger.debug("Resource values index size: %s",
len(resource_values_idx))
result = {}
lookup_path = '/'
self._calculate_effective_values(
result, None, resource_values_idx, show_lookup,
lookup_path)
for level_value in level_values:
name = level_value.level.name
value = level_value.value
lookup_path += name + '/' + value + '/'
self._calculate_effective_values(
result, level_value, resource_values_idx, show_lookup,
lookup_path)
app.logger.debug("Effective values got for resource: "
"%s, env: %s", res_def.id, environment.id)
return result
else:
if not level_values:
level_value = None
else:
level_value = level_values[-1]
resource_values = db.ResourceValues.query.filter_by(
resource_definition=res_def,
environment=environment,
level_value=level_value,
).one_or_none()
app.logger.debug("Values got for resource: "
"%s, env: %s", res_def.id, environment.id)
if not resource_values:
return {}
return resource_values.values
class ResourceValuesKeys(flask_restful.Resource,
resource_keys_operation.ResourceKeysMixin):
def put(self, environment_id, levels, resource_id_or_name, operation):
return self.patch(environment_id, levels,
resource_id_or_name, operation)
def patch(self, environment_id, levels, resource_id_or_name, operation):
self._do_update(environment_id, levels, resource_id_or_name,
operation, 'values')
return None, 204