2fc321aea3
Experimentation led to the discovery (as described in the story noted below) that the global RC_CACHE is not safe in a multi-process environment, which is common for a web service designed to scale horizontally. In rare circumstances it is possible for a custom resource class to be deleted in one process but still appear to exist in another. For many situations this wouldn't really matter, but there are cases, even more rare, where it would be possible to write allocations or resource provider inventory using the wrong resource class id. On the related story, a variety of options were discussed to fix this. Reading through the code this one (which is option 2) was the only one that proved workable in a relatively straightforward fashion: Have a per request cache. To that end, when a RequestContext is created (per request) the resource class table is scanned to create a cache. Because the context is local to this request, we no longer need to do any locking around the cache, either when we create it or when we clear it: The caller is linear. The cost of this is that now every single request starts with a scan of the resource class table. This isn't horrible: if we had no cache at all we'd be reading rows from that table multiple times throughout any request (read or write). We should probably do some performance analysis to see what the impact of this might be. The perfload jobs may be able to give a limited sense of what the impact is, but profiling will be required for accuracy. It is the case that the functional tests seem a bit slower because of that additional db query. Change-Id: I409a5e819a72d64e66ee390e4528da0c503d8d05 Story: 2006232 Task: 35833
79 lines
3.1 KiB
Python
79 lines
3.1 KiB
Python
# Copyright 2010 United States Government as represented by the
|
|
# Administrator of the National Aeronautics and Space Administration.
|
|
# All Rights Reserved.
|
|
#
|
|
# 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.
|
|
|
|
"""Fixtures for Placement tests."""
|
|
from __future__ import absolute_import
|
|
|
|
|
|
from oslo_config import cfg
|
|
from oslo_db.sqlalchemy import test_fixtures
|
|
|
|
from placement.db.sqlalchemy import migration
|
|
from placement import db_api as placement_db
|
|
from placement import deploy
|
|
from placement.objects import resource_class
|
|
from placement.objects import trait
|
|
|
|
|
|
class Database(test_fixtures.GeneratesSchema, test_fixtures.AdHocDbFixture):
|
|
def __init__(self, conf_fixture, set_config=False):
|
|
"""Create a database fixture."""
|
|
super(Database, self).__init__()
|
|
if set_config:
|
|
try:
|
|
conf_fixture.register_opt(
|
|
cfg.StrOpt('connection'), group='placement_database')
|
|
except cfg.DuplicateOptError:
|
|
# already registered
|
|
pass
|
|
conf_fixture.config(connection='sqlite://',
|
|
group='placement_database')
|
|
self.conf_fixture = conf_fixture
|
|
self.get_engine = placement_db.get_placement_engine
|
|
placement_db.configure(self.conf_fixture.conf)
|
|
|
|
def get_enginefacade(self):
|
|
return placement_db.placement_context_manager
|
|
|
|
def generate_schema_create_all(self, engine):
|
|
# note: at this point in oslo_db's fixtures, the incoming
|
|
# Engine has **not** been associated with the global
|
|
# context manager yet.
|
|
migration.create_schema(engine)
|
|
|
|
# so, to work around that placement's setup code really wants to
|
|
# use the enginefacade, we will patch the engine into it early.
|
|
# oslo_db is going to patch it anyway later. So the bug in oslo.db
|
|
# is that code these days really wants the facade to be set up fully
|
|
# when it's time to create the database. When oslo_db's fixtures
|
|
# were written, enginefacade was not in use yet so it was not
|
|
# anticipated that everyone would be doing things this way
|
|
_reset_facade = placement_db.placement_context_manager.patch_engine(
|
|
engine)
|
|
self.addCleanup(_reset_facade)
|
|
|
|
# Make sure db flags are correct at both the start and finish
|
|
# of the test.
|
|
self.addCleanup(self.cleanup)
|
|
self.cleanup()
|
|
|
|
# Sync traits and resource classes.
|
|
deploy.update_database(self.conf_fixture.conf)
|
|
|
|
def cleanup(self):
|
|
trait._TRAITS_SYNCED = False
|
|
resource_class._RESOURCE_CLASSES_SYNCED = False
|