Skip to content

Commit

Permalink
feat(data): Use usage_date instead of refreshed_at for daily usage
Browse files Browse the repository at this point in the history
  • Loading branch information
rsempe committed Dec 22, 2024
1 parent b595ddd commit d21037a
Show file tree
Hide file tree
Showing 11 changed files with 36 additions and 33 deletions.
4 changes: 2 additions & 2 deletions app/models/daily_usage.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ class DailyUsage < ApplicationRecord
belongs_to :customer
belongs_to :subscription

scope :refreshed_at_in_timezone, ->(timestamp) do
scope :usage_date_in_timezone, ->(timestamp) do
at_time_zone = Utils::Timezone.at_time_zone_sql(customer: "cus", organization: "org")

joins("INNER JOIN customers AS cus ON daily_usages.customer_id = cus.id")
.joins("INNER JOIN organizations AS org ON daily_usages.organization_id = org.id")
.where("DATE((daily_usages.refreshed_at)#{at_time_zone}) = DATE(:timestamp#{at_time_zone})", timestamp:)
.where("DATE((daily_usages.usage_date)#{at_time_zone}) = DATE(:timestamp#{at_time_zone})", timestamp:)
end
end

Expand Down
12 changes: 6 additions & 6 deletions app/services/daily_usages/compute_all_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ def subscriptions
# NOTE(DailyUsage): For now the query filters organizations having revenue_analytics premium integrations
# This might change in the future
Subscription
.with(already_refreshed_today: already_refreshed_today)
.with(existing_daily_usage:)
.joins(customer: :organization)
.merge(Organization.with_revenue_analytics_support)
.joins("LEFT JOIN already_refreshed_today ON subscriptions.id = already_refreshed_today.subscription_id")
.joins("LEFT JOIN existing_daily_usage ON subscriptions.id = existing_daily_usage.subscription_id")
.active
.where("already_refreshed_today.subscription_id IS NULL") # Exclude subscriptions that already have a daily usage record for today in customer's timezone
.where("DATE_PART('hour', (:timestamp#{at_time_zone})) IN (0, 1, 2)", timestamp:) # Refresh the usage as soom as a subscription starts a new day in customer's timezone
.where("existing_daily_usage.subscription_id IS NULL") # Exclude subscriptions that already have a daily usage record for yesterday in customer's timezone
.where("DATE_PART('hour', (:timestamp#{at_time_zone})) IN (0, 1, 2)", timestamp:) # Store the usage as soon as a subscription starts a new day in customer's timezone
end

def already_refreshed_today
DailyUsage.refreshed_at_in_timezone(timestamp).select(:subscription_id)
def existing_daily_usage
DailyUsage.usage_date_in_timezone(timestamp.to_date - 1.day).select(:subscription_id)
end
end
end
10 changes: 4 additions & 6 deletions app/services/daily_usages/compute_diff_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,12 @@ def call

attr_reader :daily_usage

delegate :subscription, :refreshed_at, :from_datetime, :to_datetime, to: :daily_usage
delegate :subscription, :usage_date, :from_datetime, :to_datetime, to: :daily_usage

def previous_daily_usage
@previous_daily_usage ||= DailyUsage
.where(subscription_id: subscription.id, from_datetime:, to_datetime:)
.where("refreshed_at < ?", refreshed_at)
.order(refreshed_at: :desc)
.first
@previous_daily_usage ||= subscription.daily_usages
.where(from_datetime:, to_datetime:)
.find_by(usage_date: usage_date - 1.day)
end

def apply_diff(previous_values, current_values)
Expand Down
10 changes: 7 additions & 3 deletions app/services/daily_usages/compute_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def initialize(subscription:, timestamp:)

def call
if subscription_billing_day?
# Usage on billing day will be computed using the periodic invoice as we cannont rely on the caching mechanism
# Usage on billing day will be computed using the periodic invoice as we cannot rely on the caching mechanism
return result
end

Expand All @@ -28,7 +28,7 @@ def call
from_datetime: current_usage.from_datetime,
to_datetime: current_usage.to_datetime,
refreshed_at: timestamp,
usage_date: date_in_timezone - 1.day
usage_date:
)

daily_usage.usage_diff = diff_usage(daily_usage)
Expand Down Expand Up @@ -65,7 +65,7 @@ def current_usage
end

def existing_daily_usage
@existing_daily_usage ||= DailyUsage.refreshed_at_in_timezone(timestamp)
@existing_daily_usage ||= DailyUsage.usage_date_in_timezone(usage_date)
.find_by(subscription_id: subscription.id)
end

Expand All @@ -86,5 +86,9 @@ def subscription_billing_day?
def date_in_timezone
@date_in_timezone ||= timestamp.in_time_zone(customer.applicable_timezone).to_date
end

def usage_date
@usage_date ||= date_in_timezone - 1.day
end
end
end
4 changes: 2 additions & 2 deletions app/services/daily_usages/fill_from_invoice_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def call
from_datetime: invoice_subscription.from_datetime,
to_datetime: invoice_subscription.to_datetime,
refreshed_at: invoice_subscription.timestamp,
usage_date: invoice_subscription.charges_to_datetime
usage_date: invoice_subscription.charges_to_datetime.to_date
)

daily_usage.usage_diff = diff_usage(daily_usage)
Expand Down Expand Up @@ -67,7 +67,7 @@ def existing_daily_usage(invoice_subscription)
DailyUsage.find_by(
from_datetime: invoice_subscription.from_datetime,
to_datetime: invoice_subscription.to_datetime,
refreshed_at: invoice_subscription.timestamp,
usage_date: invoice_subscription.charges_to_datetime.to_date,
subscription_id: invoice_subscription.subscription_id
)
end
Expand Down
3 changes: 1 addition & 2 deletions app/services/daily_usages/fill_history_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ def call
datetime = date.in_time_zone(subscription.customer.applicable_timezone).beginning_of_day.utc

next if date == Time.zone.today ||
subscription.daily_usages.where(usage_date: datetime.to_date - 1.day).exists? ||
DailyUsage.refreshed_at_in_timezone(datetime).where(subscription_id: subscription.id).exists?
subscription.daily_usages.where(usage_date: datetime.to_date - 1.day).exists?

Timecop.thread_safe = true
Timecop.freeze(datetime + 5.minutes) do
Expand Down
1 change: 1 addition & 0 deletions spec/factories/daily_usages.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
to_datetime { Time.current.end_of_month }
refreshed_at { Time.current }
usage { {} }
usage_date { Date.yesterday }
end
end
2 changes: 1 addition & 1 deletion spec/models/daily_usage_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

require 'rails_helper'
require "rails_helper"

RSpec.describe DailyUsage, type: :model do
it { is_expected.to belong_to(:organization) }
Expand Down
2 changes: 1 addition & 1 deletion spec/services/daily_usages/compute_all_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
end

context "when subscription usage was already computed" do
before { create(:daily_usage, subscription:, refreshed_at: timestamp + 2.minutes) }
before { create(:daily_usage, subscription:, usage_date: timestamp.to_date - 1.day) }

it "does not enqueue any job" do
expect(compute_service.call).to be_success
Expand Down
8 changes: 4 additions & 4 deletions spec/services/daily_usages/compute_diff_service_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

require 'rails_helper'
require "rails_helper"

RSpec.describe DailyUsages::ComputeDiffService, type: :service do
subject(:diff_service) { described_class.new(daily_usage:, previous_daily_usage:) }
Expand Down Expand Up @@ -167,7 +167,7 @@
}
end

it 'computes the diff between the two daily usages' do
it "computes the diff between the two daily usages" do
result = diff_service.call

expect(result).to be_success
Expand Down Expand Up @@ -269,10 +269,10 @@
)
end

context 'when the previous daily usage is nil' do
context "when the previous daily usage is nil" do
let(:previous_daily_usage) { nil }

it 'returns the current usage as diff' do
it "returns the current usage as diff" do
result = diff_service.call

expect(result).to be_success
Expand Down
13 changes: 7 additions & 6 deletions spec/services/daily_usages/compute_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
end

let(:timestamp) { Time.zone.parse("2024-10-22 00:05:00") }
let(:usage_date) { Date.parse("2024-10-21") }

describe "#call" do
it "creates a daily usage", aggregate_failures: true do
it "creates a daily usage" do
travel_to(timestamp) do
expect { compute_service.call }.to change(DailyUsage, :count).by(1)

Expand All @@ -37,12 +38,12 @@

context "when a daily usage already exists" do
let(:existing_daily_usage) do
create(:daily_usage, subscription:, organization:, customer:, refreshed_at: timestamp)
create(:daily_usage, subscription:, organization:, customer:, usage_date:)
end

before { existing_daily_usage }

it "returns the existing daily usage", aggregate_failure: true do
it "returns the existing daily usage" do
result = compute_service.call

expect(result).to be_success
Expand All @@ -53,7 +54,7 @@
let(:organization) { create(:organization, timezone: "America/Sao_Paulo") }

let(:existing_daily_usage) do
create(:daily_usage, subscription:, organization:, customer:, refreshed_at: timestamp - 4.hours)
create(:daily_usage, subscription:, organization:, customer:, usage_date: usage_date - 4.hours)
end

it "takes the timezone into account" do
Expand All @@ -68,7 +69,7 @@
let(:customer) { create(:customer, organization:, timezone: "America/Sao_Paulo") }

let(:existing_daily_usage) do
create(:daily_usage, subscription:, organization:, customer:, refreshed_at: timestamp - 4.hours)
create(:daily_usage, subscription:, organization:, customer:, usage_date: usage_date - 4.hours)
end

it "takes the timezone into account" do
Expand Down Expand Up @@ -99,7 +100,7 @@

let(:timestamp) { subscription.terminated_at - 1.day }

it "creates a daily usage", aggregate_failures: true do
it "creates a daily usage" do
result = compute_service.call

expect(result).to be_success
Expand Down

0 comments on commit d21037a

Please sign in to comment.