| Greatly improve performance. - reportable - Fork of reportable required by WarV… | |
| Log | |
| Files | |
| Refs | |
| README | |
| --- | |
| commit 5f29dfb16b4b34158a89f37283d31d2c42852f04 | |
| parent 880e98fc581fd44bac7c4e9337a755be19c7ed24 | |
| Author: Cristi Balan <[email protected]> | |
| Date: Fri, 21 Dec 2012 17:55:51 +0200 | |
| Greatly improve performance. | |
| Fetch only missing cached data if we're only missing a contiguous part at the e… | |
| Diffstat: | |
| M lib/saulabs/reportable/report_cach… | 50 +++++++++++++++++++++------… | |
| M spec/classes/report_cache_spec.rb | 16 ++++++++++++---- | |
| M spec/classes/report_spec.rb | 136 +++++++++++++++++++++++++++++… | |
| 3 files changed, 182 insertions(+), 20 deletions(-) | |
| --- | |
| diff --git a/lib/saulabs/reportable/report_cache.rb b/lib/saulabs/reportable/re… | |
| @@ -69,6 +69,15 @@ module Saulabs | |
| # | |
| def self.process(report, options, &block) | |
| raise ArgumentError.new('A block must be given') unless block_given? | |
| + | |
| + # If end_date is in the middle of the current reporting period it mean… | |
| + # Update the options hash to reflect reality. | |
| + current_reporting_period = ReportingPeriod.new(options[:grouping]) | |
| + if options[:end_date] && options[:end_date] > current_reporting_period… | |
| + options[:live_data] = true | |
| + options.delete(:end_date) | |
| + end | |
| + | |
| self.transaction do | |
| cached_data = read_cached_data(report, options) | |
| new_data = read_new_data(cached_data, options, &block) | |
| @@ -140,11 +149,10 @@ module Saulabs | |
| serialize_conditions(options[:conditions]) | |
| ] | |
| first_reporting_period = get_first_reporting_period(options) | |
| - last_reporting_period = get_last_reporting_period(options) | |
| - if last_reporting_period | |
| + if options[:end_date] | |
| conditions.first << ' AND reporting_period BETWEEN ? AND ?' | |
| conditions << first_reporting_period.date_time | |
| - conditions << last_reporting_period.date_time | |
| + conditions << ReportingPeriod.new(options[:grouping], options[:end… | |
| else | |
| conditions.first << ' AND reporting_period >= ?' | |
| conditions << first_reporting_period.date_time | |
| @@ -157,16 +165,30 @@ module Saulabs | |
| end | |
| def self.read_new_data(cached_data, options, &block) | |
| - if !options[:live_data] && cached_data.length == options[:limit] | |
| - [] | |
| + return [] if !options[:live_data] && cached_data.length == options[:… | |
| + | |
| + first_reporting_period_to_read = get_first_reporting_period_to_read(… | |
| + last_reporting_period_to_read = options[:end_date] ? ReportingPeriod… | |
| + | |
| + yield(first_reporting_period_to_read.date_time, last_reporting_perio… | |
| + end | |
| + | |
| + def self.get_first_reporting_period_to_read(cached_data, options) | |
| + return get_first_reporting_period(options) if cached_data.empty? | |
| + | |
| + last_cached_reporting_period = ReportingPeriod.new(options[:grouping… | |
| + missing_reporting_periods = options[:limit] - cached_data.length | |
| + last_reporting_period = if !options[:live_data] && options[:end_date] | |
| + ReportingPeriod.new(options[:grouping], options[:end_date]) | |
| else | |
| - first_reporting_period_to_read = if cached_data.length < options[:… | |
| - get_first_reporting_period(options) | |
| - else | |
| - ReportingPeriod.new(options[:grouping], cached_data.last.reporti… | |
| - end | |
| - last_reporting_period_to_read = options[:end_date] ? ReportingPeri… | |
| - yield(first_reporting_period_to_read.date_time, last_reporting_per… | |
| + ReportingPeriod.new(options[:grouping]).previous | |
| + end | |
| + | |
| + if missing_reporting_periods == 0 || last_cached_reporting_period.of… | |
| + # cache only has missing data contiguously at the end | |
| + last_cached_reporting_period.next | |
| + else | |
| + get_first_reporting_period(options) | |
| end | |
| end | |
| @@ -178,10 +200,6 @@ module Saulabs | |
| end | |
| end | |
| - def self.get_last_reporting_period(options) | |
| - return ReportingPeriod.new(options[:grouping], options[:end_date]) i… | |
| - end | |
| - | |
| end | |
| end | |
| diff --git a/spec/classes/report_cache_spec.rb b/spec/classes/report_cache_spec… | |
| @@ -184,7 +184,7 @@ describe Saulabs::Reportable::ReportCache do | |
| describe 'with :end_date = <some date>' do | |
| before do | |
| - @options = @report.options.merge(:end_date => Time.now) | |
| + @options = @report.options.merge(:end_date => Time.now - 1.send(@rep… | |
| end | |
| it 'should yield the last date and time of the reporting period for th… | |
| @@ -221,7 +221,7 @@ describe Saulabs::Reportable::ReportCache do | |
| end | |
| it 'should utilize the end_date in the conditions' do | |
| - end_date = Time.now | |
| + end_date = Time.now - 1.send(@report.options[:grouping].identifier) | |
| Saulabs::Reportable::ReportCache.should_receive(:all).once.with( | |
| :conditions => [ | |
| %w(model_name report_name grouping aggregation conditions).map do |c… | |
| @@ -232,7 +232,7 @@ describe Saulabs::Reportable::ReportCache do | |
| @report.options[:grouping].identifier.to_s, | |
| @report.aggregation.to_s, | |
| '', | |
| - Saulabs::Reportable::ReportingPeriod.first(@report.options[:grouping… | |
| + Saulabs::Reportable::ReportingPeriod.first(@report.options[:grouping… | |
| Saulabs::Reportable::ReportingPeriod.new(@report.options[:grouping],… | |
| ], | |
| :limit => 10, | |
| @@ -271,7 +271,15 @@ describe Saulabs::Reportable::ReportCache do | |
| end | |
| end | |
| end | |
| - | |
| + | |
| + describe '.get_first_reporting_period_to_read' do | |
| + it 'returns first reporting period if no cached data' do | |
| + Saulabs::Reportable::ReportCache.should_receive(:get_first_reporting_per… | |
| + result = Saulabs::Reportable::ReportCache.send(:get_first_reporting_peri… | |
| + result.should == 'first' | |
| + end | |
| + end | |
| + | |
| describe '.serialize_conditions' do | |
| it 'should serialize empty conditions correctly' do | |
| diff --git a/spec/classes/report_spec.rb b/spec/classes/report_spec.rb | |
| @@ -77,6 +77,142 @@ describe Saulabs::Reportable::Report do | |
| User.create!(:login => 'test 4', :created_at => Time.now - 3.send(gr… | |
| end | |
| + describe 'optimized querying with contiguously cached data' do | |
| + it "should be optimized with specified end_date" do | |
| + @end_date = DateTime.now - 1.send(grouping) | |
| + @report = Saulabs::Reportable::Report.new(User, :registrations, | |
| + :grouping => grouping, | |
| + :limit => 10, | |
| + :end_date => @end_date | |
| + ) | |
| + @result = @report.run | |
| + | |
| + Saulabs::Reportable::ReportCache.last.delete | |
| + | |
| + grouping_instance = Saulabs::Reportable::Grouping.new(grouping) | |
| + reporting_period = Saulabs::Reportable::ReportingPeriod.new(groupi… | |
| + | |
| + @report.should_receive(:read_data) do |begin_at, end_at, options| | |
| + begin_at.should == reporting_period.date_time | |
| + end_at.should == reporting_period.last_date_time | |
| + [] # without this rspec whines about an ambiguous return value | |
| + end | |
| + | |
| + @result = @report.run | |
| + end | |
| + | |
| + it "should be optimized without specific end_date and live_data" do | |
| + @report = Saulabs::Reportable::Report.new(User, :registrations, | |
| + :grouping => grouping, | |
| + :limit => 10, | |
| + :live_data => true | |
| + ) | |
| + @result = @report.run.to_a | |
| + | |
| + Saulabs::Reportable::ReportCache.last.delete | |
| + | |
| + grouping_instance = Saulabs::Reportable::Grouping.new(grouping) | |
| + reporting_period = Saulabs::Reportable::ReportingPeriod.new(groupi… | |
| + | |
| + @report.should_receive(:read_data) do |begin_at, end_at, options| | |
| + begin_at.should == reporting_period.date_time | |
| + end_at.should == nil | |
| + [] # without this rspec whines about an ambiguous return value | |
| + end | |
| + | |
| + @result = @report.run | |
| + end | |
| + | |
| + it "should be optimized without specific end_date and without live_d… | |
| + @report = Saulabs::Reportable::Report.new(User, :registrations, | |
| + :grouping => grouping, | |
| + :limit => 10 | |
| + ) | |
| + @result = @report.run.to_a | |
| + | |
| + Saulabs::Reportable::ReportCache.last.delete | |
| + | |
| + grouping_instance = Saulabs::Reportable::Grouping.new(grouping) | |
| + reporting_period = Saulabs::Reportable::ReportingPeriod.new(groupi… | |
| + | |
| + @report.should_receive(:read_data) do |begin_at, end_at, options| | |
| + begin_at.should == reporting_period.date_time | |
| + end_at.should == nil | |
| + [] # without this rspec whines about an ambiguous return value | |
| + end | |
| + | |
| + @result = @report.run | |
| + end | |
| + end | |
| + | |
| + describe 'non optimized querying when gaps present in cached data' do | |
| + it "should not be optimized with specified end_date" do | |
| + @end_date = DateTime.now - 1.send(grouping) | |
| + @report = Saulabs::Reportable::Report.new(User, :registrations, | |
| + :grouping => grouping, | |
| + :limit => 10, | |
| + :end_date => @end_date | |
| + ) | |
| + @result = @report.run | |
| + | |
| + Saulabs::Reportable::ReportCache.first.delete | |
| + | |
| + grouping_instance = Saulabs::Reportable::Grouping.new(grouping) | |
| + reporting_period = Saulabs::Reportable::ReportingPeriod.new(groupi… | |
| + | |
| + @report.should_receive(:read_data) do |begin_at, end_at, options| | |
| + begin_at.should == reporting_period.offset(-9).date_time | |
| + end_at.should == reporting_period.last_date_time | |
| + [] # without this rspec whines about an ambiguous return value | |
| + end | |
| + | |
| + @result = @report.run | |
| + end | |
| + | |
| + it "should not be optimized without specific end_date and live_data"… | |
| + @report = Saulabs::Reportable::Report.new(User, :registrations, | |
| + :grouping => grouping, | |
| + :limit => 10, | |
| + :live_data => true | |
| + ) | |
| + @result = @report.run.to_a | |
| + | |
| + Saulabs::Reportable::ReportCache.first.delete | |
| + | |
| + grouping_instance = Saulabs::Reportable::Grouping.new(grouping) | |
| + reporting_period = Saulabs::Reportable::ReportingPeriod.new(groupi… | |
| + | |
| + @report.should_receive(:read_data) do |begin_at, end_at, options| | |
| + begin_at.should == reporting_period.offset(-9).date_time | |
| + end_at.should == nil | |
| + [] # without this rspec whines about an ambiguous return value | |
| + end | |
| + | |
| + @result = @report.run | |
| + end | |
| + | |
| + it "should not be optimized without specific end_date and without li… | |
| + @report = Saulabs::Reportable::Report.new(User, :registrations, | |
| + :grouping => grouping, | |
| + :limit => 10 | |
| + ) | |
| + @result = @report.run.to_a | |
| + | |
| + Saulabs::Reportable::ReportCache.first.delete | |
| + | |
| + grouping_instance = Saulabs::Reportable::Grouping.new(grouping) | |
| + reporting_period = Saulabs::Reportable::ReportingPeriod.new(groupi… | |
| + | |
| + @report.should_receive(:read_data) do |begin_at, end_at, options| | |
| + begin_at.should == reporting_period.offset(-9).date_time | |
| + end_at.should == nil | |
| + [] # without this rspec whines about an ambiguous return value | |
| + end | |
| + | |
| + @result = @report.run | |
| + end | |
| + end | |
| + | |
| describe 'when :end_date is specified' do | |
| it 'should not raise a SQL duplicate key error after multiple runs' … |