Skip to content

Commit af82bd6

Browse files
committed
Compare fog vs storage-cli benchmarks
1 parent 3fc9e39 commit af82bd6

File tree

4 files changed

+182
-49
lines changed

4 files changed

+182
-49
lines changed

lib/cloud_controller/benchmark/blobstore.rb

Lines changed: 98 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,77 @@
1+
# frozen_string_literal: true
2+
13
require 'benchmark'
24
require 'find'
35
require 'zip'
6+
require 'tempfile'
7+
require 'fileutils'
8+
require 'securerandom'
49

510
module VCAP::CloudController
611
module Benchmark
712
class Blobstore
13+
SIZES = [
14+
['0.005MB', (0.005 * 1024 * 1024).to_i],
15+
['0.01MB', (0.01 * 1024 * 1024).to_i],
16+
['0.1MB', (0.1 * 1024 * 1024).to_i],
17+
['1MB', 1 * 1024 * 1024],
18+
['10MB', 10 * 1024 * 1024],
19+
['100MB', 100 * 1024 * 1024],
20+
['200MB', 200 * 1024 * 1024],
21+
['300MB', 300 * 1024 * 1024],
22+
['400MB', 400 * 1024 * 1024],
23+
['500MB', 500 * 1024 * 1024],
24+
['600MB', 600 * 1024 * 1024],
25+
['700MB', 700 * 1024 * 1024],
26+
['800MB', 800 * 1024 * 1024],
27+
['900MB', 900 * 1024 * 1024],
28+
['1000MB', 1000 * 1024 * 1024]
29+
].freeze
30+
31+
CHUNK_1MB = '0'.b * (1024 * 1024)
32+
833
def perform
34+
big_droplet_guids = []
935
resource_dir = generate_resources
10-
11-
resource_timing = resource_match(resource_dir)
12-
puts("resource match timing: #{resource_timing * 1000}ms")
36+
log_timing('resource match timing', resource_match(resource_dir))
1337

1438
zip_output_dir = Dir.mktmpdir
1539
zip_file = zip_resources(resource_dir, zip_output_dir)
1640

17-
package_guid, resource_timing = upload_package(zip_file)
18-
puts("package upload timing: #{resource_timing * 1000}ms")
19-
20-
resource_timing = download_package(package_guid, resource_dir)
21-
puts("package download timing: #{resource_timing * 1000}ms")
41+
package_guid, timing = upload_package(zip_file)
42+
log_timing('package upload timing', timing)
43+
log_timing('package download timing', download_package(package_guid, resource_dir))
2244

23-
bytes_read, resource_timing = download_buildpacks(resource_dir)
45+
bytes_read, timing = download_buildpacks(resource_dir)
2446
puts("downloaded #{Buildpack.count} buildpacks, total #{bytes_read} bytes read")
25-
puts("buildpack download timing: #{resource_timing * 1000}ms")
47+
log_timing('buildpack download timing', timing)
2648

27-
droplet_guid, resource_timing = upload_droplet(zip_file)
28-
puts("droplet upload timing: #{resource_timing * 1000}ms")
49+
droplet_results = []
2950

30-
resource_timing = download_droplet(droplet_guid, resource_dir)
31-
puts("droplet download timing: #{resource_timing * 1000}ms")
51+
SIZES.each do |label, bytes|
52+
Tempfile.create(["big-droplet-#{label}", '.bin'], resource_dir) do |tempfile|
53+
write_file_of_size(tempfile.path, bytes)
3254

33-
big_droplet_file = Tempfile.new('big-droplet', resource_dir)
34-
big_droplet_file.write('abc' * 1024 * 1024 * 100)
35-
big_droplet_guid, resource_timing = upload_droplet(big_droplet_file.path)
36-
puts("big droplet upload timing: #{resource_timing * 1000}ms")
55+
guid, upload_timing = upload_droplet(tempfile.path)
56+
big_droplet_guids << guid
57+
droplet_results << { label: "droplet #{label}", guid: guid, upload_timing: upload_timing }
58+
end
59+
end
60+
# rubocop:disable Style/CombinableLoops
61+
droplet_results.each do |r|
62+
log_timing("#{r[:label]} upload timing", r[:upload_timing])
63+
end
3764

38-
resource_timing = download_droplet(big_droplet_guid, resource_dir)
39-
puts("big droplet download timing: #{resource_timing * 1000}ms")
65+
droplet_results.each do |r|
66+
log_timing("#{r[:label]} download timing", download_droplet(r[:guid], resource_dir))
67+
end
68+
# rubocop:enable Style/CombinableLoops
4069
ensure
41-
FileUtils.remove_dir(resource_dir, true)
42-
FileUtils.remove_dir(zip_output_dir, true)
43-
package_blobstore_client.delete(package_guid) if package_guid
44-
droplet_blobstore_client.delete(droplet_guid) if droplet_guid
45-
droplet_blobstore_client.delete(big_droplet_guid) if big_droplet_guid
70+
FileUtils.remove_dir(resource_dir, true) if resource_dir
71+
FileUtils.remove_dir(zip_output_dir, true) if zip_output_dir
72+
73+
safe_delete(package_blobstore_client, package_guid)
74+
Array(big_droplet_guids).each { |g| safe_delete(droplet_blobstore_client, g) }
4675
end
4776

4877
def resource_match(dir_path)
@@ -60,46 +89,69 @@ def upload_package(package_path)
6089
end
6190

6291
def download_package(package_guid, tmp_dir)
63-
tempfile = Tempfile.new('package-download-benchmark', tmp_dir)
64-
::Benchmark.realtime do
65-
package_blobstore_client.download_from_blobstore(package_guid, tempfile.path)
92+
Tempfile.create('package-download-benchmark', tmp_dir) do |tempfile|
93+
::Benchmark.realtime do
94+
package_blobstore_client.download_from_blobstore(package_guid, tempfile.path)
95+
end
6696
end
6797
end
6898

6999
def download_buildpacks(tmp_dir)
70-
tempfile = Tempfile.new('buildpack-download-benchmark', tmp_dir)
71-
bytes_read = 0
72-
73-
timing = ::Benchmark.realtime do
74-
bytes_read = Buildpack.map do |buildpack|
75-
buildpack_blobstore_client.download_from_blobstore(buildpack.key, tempfile.path)
76-
File.stat(tempfile.path).size
77-
end.sum
100+
Tempfile.create('buildpack-download-benchmark', tmp_dir) do |tempfile|
101+
bytes_read = 0
102+
timing = ::Benchmark.realtime do
103+
bytes_read = Buildpack.map do |buildpack|
104+
buildpack_blobstore_client.download_from_blobstore(buildpack.key, tempfile.path)
105+
File.stat(tempfile.path).size
106+
end.sum
107+
end
108+
[bytes_read, timing]
78109
end
79-
80-
[bytes_read, timing]
81110
end
82111

83112
def upload_droplet(droplet_path)
84113
copy_to_blobstore(droplet_path, droplet_blobstore_client)
85114
end
86115

87116
def download_droplet(droplet_guid, tmp_dir)
88-
tempfile = Tempfile.new('droplet-download-benchmark', tmp_dir)
89-
90-
::Benchmark.realtime do
91-
droplet_blobstore_client.download_from_blobstore(droplet_guid, tempfile.path)
117+
Tempfile.create('droplet-download-benchmark', tmp_dir) do |tempfile|
118+
::Benchmark.realtime do
119+
droplet_blobstore_client.download_from_blobstore(droplet_guid, tempfile.path)
120+
end
92121
end
93122
end
94123

95124
private
96125

126+
def log_timing(label, seconds)
127+
puts("#{label}: #{(seconds * 1000).round(3)}ms")
128+
end
129+
130+
def safe_delete(client, guid)
131+
return if guid.nil?
132+
133+
client.delete(guid)
134+
rescue StandardError => e
135+
# don't fail the benchmark run if cleanup fails
136+
warn("cleanup failed for guid=#{guid}: #{e.class}: #{e.message}")
137+
end
138+
139+
def write_file_of_size(path, bytes)
140+
File.open(path, 'wb') do |f|
141+
remaining = bytes
142+
while remaining > 0
143+
to_write = [CHUNK_1MB.bytesize, remaining].min
144+
f.write(CHUNK_1MB, to_write)
145+
remaining -= to_write
146+
end
147+
end
148+
end
149+
97150
def generate_resources
98151
dir = Dir.mktmpdir
99152

100-
100.times.each do |i|
101-
f = File.open(File.join(dir, i.to_s), 'w')
102-
f.write('foo' * (65_536 + i))
153+
100.times do |i|
154+
File.write(File.join(dir, i.to_s), 'foo' * (65_536 + i))
103155
end
104156

105157
dir
@@ -110,9 +162,7 @@ def zip_resources(resource_dir, output_dir)
110162
Zip::File.open(zip_file, create: true) do |zipfile|
111163
Find.find(resource_dir).
112164
select { |f| File.file?(f) }.
113-
each do |file|
114-
zipfile.add(File.basename(file), file)
115-
end
165+
each { |file| zipfile.add(File.basename(file), file) }
116166
end
117167
zip_file
118168
end

lib/cloud_controller/config.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
require 'cloud_controller/config_schemas/worker_schema'
1313
require 'cloud_controller/config_schemas/deployment_updater_schema'
1414
require 'cloud_controller/config_schemas/rotate_database_key_schema'
15+
require 'cloud_controller/config_schemas/blobstore_benchmarks_schema'
1516
require 'utils/hash_utils'
1617

1718
module VCAP::CloudController
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
require 'vcap/config'
2+
3+
module VCAP::CloudController
4+
module ConfigSchemas
5+
class BlobstoreBenchmarksSchema < VCAP::Config
6+
# rubocop:disable Metrics/BlockLength
7+
define_schema do
8+
blobstore_section = {
9+
blobstore_type: String,
10+
blobstore_provider: String,
11+
12+
optional(:connection_config) => Hash,
13+
optional(:fog_connection) => Hash,
14+
15+
fog_aws_storage_options: Hash,
16+
fog_gcp_storage_options: Hash,
17+
18+
optional(:resource_directory_key) => String,
19+
optional(:buildpack_directory_key) => String,
20+
optional(:app_package_directory_key) => String,
21+
optional(:droplet_directory_key) => String,
22+
23+
optional(:maximum_size) => Integer,
24+
optional(:minimum_size) => Integer,
25+
optional(:max_package_size) => Integer,
26+
optional(:max_valid_packages_stored) => Integer,
27+
optional(:max_staged_droplets_stored) => Integer
28+
}
29+
30+
{
31+
optional(:logging) => {
32+
optional(:level) => String,
33+
optional(:file) => String,
34+
optional(:syslog) => String,
35+
optional(:stdout_sink_enabled) => bool
36+
},
37+
38+
db: {
39+
optional(:database) => Hash, # db connection hash for sequel\
40+
max_connections: Integer, # max connections in the connection pool
41+
pool_timeout: Integer, # timeout before raising an error when connection can't be established to the db
42+
log_level: String, # debug, info, etc.
43+
log_db_queries: bool,
44+
ssl_verify_hostname: bool,
45+
connection_validation_timeout: Integer,
46+
optional(:ca_cert_path) => String
47+
},
48+
storage_cli_config_file_resource_pool: String,
49+
storage_cli_config_file_buildpacks: String,
50+
storage_cli_config_file_packages: String,
51+
storage_cli_config_file_droplets: String,
52+
53+
db_encryption_key: enum(String, NilClass),
54+
55+
optional(:database_encryption) => {
56+
keys: Hash,
57+
current_key_label: String,
58+
optional(:pbkdf2_hmac_iterations) => Integer
59+
},
60+
61+
resource_pool: blobstore_section,
62+
buildpacks: blobstore_section,
63+
packages: blobstore_section,
64+
droplets: blobstore_section,
65+
66+
pid_filename: String,
67+
index: Integer, # Component index (cc-0, cc-1, etc)
68+
name: String, # Component name (api_z1, api_z2)
69+
default_app_ssh_access: bool
70+
}
71+
end
72+
# rubocop:enable Metrics/BlockLength
73+
74+
class << self
75+
def configure_components(config)
76+
ResourcePool.instance = ResourcePool.new(config)
77+
end
78+
end
79+
end
80+
end
81+
end

lib/tasks/blobstore_benchmarks.rake

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ require 'cloud_controller/benchmark/blobstore'
33
namespace :benchmarks do
44
desc 'Perform blobstore benchmark'
55
task perform_blobstore_benchmark: :environment do
6-
BackgroundJobEnvironment.new(RakeConfig.config).setup_environment do
6+
RakeConfig.context = :blobstore_benchmarks
7+
BoshErrandEnvironment.new(RakeConfig.config).setup_environment do
78
VCAP::CloudController::Benchmark::Blobstore.new.perform
89
end
910
end

0 commit comments

Comments
 (0)