From 16af61a0de1ae1d6165babac0f026c4d4c3b5cb9 Mon Sep 17 00:00:00 2001 From: ZhiQiang Fan Date: Fri, 16 Oct 2015 02:45:45 -0700 Subject: [PATCH] avoid generate temporary table when query samples Currently, when we call query-samples API, for SQL backend, we do an inner join to get a temporary table, then apply filters, orderby and limit options on it. Such implementation will drain disk space or cause timeout exception when data set is growing, even with specified limit and filters. This patch applies filters and limit on the inner join itself, and uses a fake mapper object instead of temporary table to suit current query transformer. Change-Id: I261a2dd362ed51c16a6fa191dadcce1b45fce2e4 Closes-Bug: #1506738 (cherry picked from commit b9bf5f1f571f21aee4347c80a5a7ac8a1c456e45) --- ceilometer/storage/impl_sqlalchemy.py | 17 +++++++++- ceilometer/storage/sqlalchemy/models.py | 42 +++++++++++-------------- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/ceilometer/storage/impl_sqlalchemy.py b/ceilometer/storage/impl_sqlalchemy.py index d8f4550112..40fba7ef16 100644 --- a/ceilometer/storage/impl_sqlalchemy.py +++ b/ceilometer/storage/impl_sqlalchemy.py @@ -601,7 +601,22 @@ class Connection(base.Connection): session = self._engine_facade.get_session() engine = self._engine_facade.get_engine() - query = session.query(models.FullSample) + query = session.query(models.Sample.timestamp, + models.Sample.recorded_at, + models.Sample.message_id, + models.Sample.message_signature, + models.Sample.volume.label('counter_volume'), + models.Meter.name.label('counter_name'), + models.Meter.type.label('counter_type'), + models.Meter.unit.label('counter_unit'), + models.Resource.source_id, + models.Resource.user_id, + models.Resource.project_id, + models.Resource.resource_metadata, + models.Resource.resource_id).join( + models.Meter, models.Meter.id == models.Sample.meter_id).join( + models.Resource, + models.Resource.internal_id == models.Sample.resource_id) transformer = sql_utils.QueryTransformer(models.FullSample, query, dialect=engine.dialect.name) if filter_expr is not None: diff --git a/ceilometer/storage/sqlalchemy/models.py b/ceilometer/storage/sqlalchemy/models.py index 4057c7f00e..163ffeb4ec 100644 --- a/ceilometer/storage/sqlalchemy/models.py +++ b/ceilometer/storage/sqlalchemy/models.py @@ -20,7 +20,7 @@ from oslo_utils import timeutils import six from sqlalchemy import (Column, Integer, String, ForeignKey, Index, UniqueConstraint, BigInteger) -from sqlalchemy import event, select +from sqlalchemy import event from sqlalchemy import Float, Boolean, Text, DateTime from sqlalchemy.dialects.mysql import DECIMAL from sqlalchemy.ext.declarative import declarative_base @@ -222,29 +222,23 @@ class Sample(Base): message_id = Column(String(128)) -class FullSample(Base): - """Mapper model. - - It's needed as many of the filters work against raw data which is split - between Meter, Sample, and Resource tables - """ - meter = Meter.__table__ - sample = Sample.__table__ - resource = Resource.__table__ - __table__ = (select([sample.c.id, meter.c.name.label('counter_name'), - meter.c.type.label('counter_type'), - meter.c.unit.label('counter_unit'), - sample.c.volume.label('counter_volume'), - resource.c.resource_id, resource.c.source_id, - resource.c.user_id, resource.c.project_id, - resource.c.resource_metadata, resource.c.internal_id, - sample.c.timestamp, sample.c.message_id, - sample.c.message_signature, sample.c.recorded_at]) - .select_from( - sample.join(meter, sample.c.meter_id == meter.c.id).join( - resource, - sample.c.resource_id == resource.c.internal_id)) - .alias()) +class FullSample(object): + """A fake model for query samples.""" + id = Sample.id + timestamp = Sample.timestamp + message_id = Sample.message_id + message_signature = Sample.message_signature + recorded_at = Sample.recorded_at + counter_name = Meter.name + counter_type = Meter.type + counter_unit = Meter.unit + counter_volume = Sample.volume + resource_id = Resource.resource_id + source_id = Resource.source_id + user_id = Resource.user_id + project_id = Resource.project_id + resource_metadata = Resource.resource_metadata + internal_id = Resource.internal_id class Alarm(Base):