Skip to content
This repository has been archived by the owner on Mar 17, 2023. It is now read-only.

Avoid chunk argument #3

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
language: ruby
before_install:
- gem install bundler -v 1.9.10
rvm:
- 2.1
- 2.0.0
- 1.9.3
- 2.3.1
script: bundle exec rspec
6 changes: 3 additions & 3 deletions lib/tina/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ def restore(prefix_file)

prefixes = File.readlines(prefix_file).map(&:chomp)
objects = RestorePlan::ObjectCollection.new(s3_client.list_bucket_prefixes(prefixes))
restore_plan = RestorePlan.new(total_storage.to_i, objects)
price = restore_plan.price(duration_in_seconds)
chunks = objects.chunk(duration_in_seconds)
restore_plan = RestorePlan.new(total_storage.to_i, objects, duration_in_seconds)
price = restore_plan.price
chunks = restore_plan.object_chunks
say
say "Restores will be performed in the following chunks:"
say "-" * 60
Expand Down
39 changes: 21 additions & 18 deletions lib/tina/restore_plan.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,34 @@ class RestorePlan
DAYS_PER_MONTH = 30
PRICE_PER_GB_PER_HOUR = 0.011

def initialize(total_storage_size, objects, options = {})
def initialize(total_storage_size, objects, total_time, options = {})
@total_storage_size = total_storage_size
@objects = objects
@total_time = [total_time, 4 * 3600].max
@price_per_gb_per_hour = options[:price_per_gb_per_hour] || PRICE_PER_GB_PER_HOUR

@daily_allowance = @total_storage_size * MONTHLY_FREE_TIER_ALLOWANCE_FACTOR / DAYS_PER_MONTH
end

def price(total_time)
total_time = [total_time, 4 * 3600].max
chunk_size = quadhourly_restore_rate(total_time)
chunks = @objects.chunk(chunk_size)
largest_chunk_object_size = chunks.map { |chunk| chunk.map(&:size).reduce(&:+) }.max
quadhours = chunks.size
def price
largest_chunk_object_size = object_chunks.map { |chunk| chunk.map(&:size).reduce(&:+) }.max
quadhours = object_chunks.size
quadhourly_allowance = @daily_allowance / ( [(24 / 4), quadhours].min * 4)

peak_retrieval_rate = largest_chunk_object_size / 4
peak_billable_retrieval_rate = [0, peak_retrieval_rate - quadhourly_allowance].max

peak_billable_retrieval_rate * (@price_per_gb_per_hour / 1024 ** 3) * 720
end

def object_chunks
@object_chunks ||= @objects.chunk(quadhourly_restore_rate)
end

private

def quadhourly_restore_rate(total_time)
@objects.total_size / (total_time / (4 * 3600))
def quadhourly_restore_rate
@objects.total_size / (@total_time / (4 * 3600))
end

class ObjectCollection
Expand All @@ -45,17 +48,17 @@ def size
end

def chunk(max_chunk_size)
@chunks ||= begin
chunks = @objects.chunk(sum: 0, index: 0) do |object, state|
state[:sum] += object.size
if state[:sum] > max_chunk_size
state[:sum] = object.size
state[:index] += 1
end
state[:index]
sum = 0
index = 0
chunks = @objects.chunk do |object|
sum += object.size
if sum > max_chunk_size
sum = object.size
index += 1
end
chunks.map(&:last)
index
end
chunks.map(&:last)
end
end
end
Expand Down
45 changes: 33 additions & 12 deletions spec/tina/restore_plan_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@

module Tina
describe RestorePlan do
subject do
described_class.new(total_storage_size, object_collection, options)
end

let :total_storage_size do
75 * (1024 ** 4)
end
Expand All @@ -29,15 +25,15 @@ module Tina
# http://aws.amazon.com/glacier/faqs/
context 'with the examples given on the Amazon Glacier pricing FAQ' do
it 'matches the the price for a restore with everything at once' do
expect(subject.price(4 * 3600)).to be_within(0.05).of(21.6)
expect(described_class.new(total_storage_size, object_collection, 4 * 3600, options).price).to be_within(0.05).of(21.6)
end

it 'matches the the price for a restore over 8 hours' do
expect(subject.price(8 * 3600)).to be_within(0.05).of(10.8)
expect(described_class.new(total_storage_size, object_collection, 8 * 3600, options).price).to be_within(0.05).of(10.8)
end

it 'matches the the price for a restore over 28 hours' do
expect(subject.price(28 * 3600)).to eq 0
expect(described_class.new(total_storage_size, object_collection, 28 * 3600, options).price).to eq 0
end
end

Expand All @@ -58,19 +54,19 @@ module Tina
end

it 'matches the price for a restore over a month' do
expect(subject.price(30 * 24 * 3600)).to be_within(0.05).of(4.16)
expect(described_class.new(total_storage_size, object_collection, 30 * 24 * 3600, options).price).to be_within(0.05).of(4.16)
end

it 'matches the price for a restore over a week' do
expect(subject.price(7 * 24 * 3600)).to be_within(0.05).of(437.87)
expect(described_class.new(total_storage_size, object_collection, 7 * 24 * 3600, options).price).to be_within(0.05).of(437.87)
end

it 'matches the price for a restore over a day' do
expect(subject.price(1 * 24 * 3600)).to be_within(0.05).of(3832.16)
expect(described_class.new(total_storage_size, object_collection, 1 * 24 * 3600, options).price).to be_within(0.05).of(3832.16)
end

it 'matches the price for a restore over a 4 hour period' do
expect(subject.price(4 * 3600)).to be_within(0.05).of(22992.93)
expect(described_class.new(total_storage_size, object_collection, 4 * 3600, options).price).to be_within(0.05).of(22992.93)
end
end

Expand All @@ -84,12 +80,37 @@ module Tina
end

it 'matches the price for a restore over 4 days' do
expect(subject.price(4 * 24 * 3600)).to be_within(20).of(768)
expect(described_class.new(total_storage_size, object_collection, 4 * 24 * 3600, options).price).to be_within(20).of(768)
end
end
end
end
end

describe RestorePlan::ObjectCollection do
describe '#chunk' do
let :object_collection do
described_class.new(objects)
end

let :objects do
[
double(:fake_object1, size: 3),
double(:fake_object2, size: 3),
double(:fake_object3, size: 3),
]
end

it 'chunks objects into chunks of the maximum size given' do
expect(object_collection.chunk(6)).to eq([objects[0..1], objects[2..2]])
end

it 'places objects larger than the given maximum size into their own chunks' do
objects[1] = double(:large_object, size: 42)
expect(object_collection.chunk(6)).to eq([objects[0..0], objects[1..1], objects[2..2]])
end
end
end
end

module SpecHelpers
Expand Down