Skip to content

Commit a9e311b

Browse files
authored
Compare fog vs storage-cli benchmarks (#4738)
* Compare fog vs storage-cli benchmarks
1 parent 0c2c53d commit a9e311b

File tree

4 files changed

+177
-47
lines changed

4 files changed

+177
-47
lines changed

lib/cloud_controller/benchmark/blobstore.rb

Lines changed: 93 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,70 @@
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+
'50MB' => 50 * 1024 * 1024,
20+
'100MB' => 100 * 1024 * 1024,
21+
'500MB' => 400 * 1024 * 1024,
22+
'1000MB' => 1000 * 1024 * 1024
23+
}.freeze
24+
25+
CHUNK_1MB = '0'.b * (1024 * 1024)
26+
827
def perform
28+
big_droplet_guids = []
929
resource_dir = generate_resources
10-
11-
resource_timing = resource_match(resource_dir)
12-
puts("resource match timing: #{resource_timing * 1000}ms")
30+
log_timing('resource match timing', resource_match(resource_dir))
1331

1432
zip_output_dir = Dir.mktmpdir
1533
zip_file = zip_resources(resource_dir, zip_output_dir)
1634

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")
35+
package_guid, timing = upload_package(zip_file)
36+
log_timing('package upload timing', timing)
37+
log_timing('package download timing', download_package(package_guid, resource_dir))
2238

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

27-
droplet_guid, resource_timing = upload_droplet(zip_file)
28-
puts("droplet upload timing: #{resource_timing * 1000}ms")
43+
upload_lines = []
44+
download_lines = []
2945

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

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")
50+
guid, upload_timing = upload_droplet(tempfile.path)
51+
big_droplet_guids << guid
52+
53+
download_timing = download_droplet(guid, resource_dir)
54+
55+
upload_lines << format_timing("droplet #{label} upload timing", upload_timing)
56+
download_lines << format_timing("droplet #{label} download timing", download_timing)
57+
end
58+
end
3759

38-
resource_timing = download_droplet(big_droplet_guid, resource_dir)
39-
puts("big droplet download timing: #{resource_timing * 1000}ms")
60+
puts(upload_lines.join("\n"))
61+
puts(download_lines.join("\n"))
4062
ensure
4163
FileUtils.remove_dir(resource_dir, true)
4264
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
65+
66+
safe_delete(package_blobstore_client, package_guid)
67+
Array(big_droplet_guids).each { |g| safe_delete(droplet_blobstore_client, g) }
4668
end
4769

4870
def resource_match(dir_path)
@@ -60,46 +82,73 @@ def upload_package(package_path)
6082
end
6183

6284
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)
85+
Tempfile.create('package-download-benchmark', tmp_dir) do |tempfile|
86+
::Benchmark.realtime do
87+
package_blobstore_client.download_from_blobstore(package_guid, tempfile.path)
88+
end
6689
end
6790
end
6891

6992
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
93+
Tempfile.create('buildpack-download-benchmark', tmp_dir) do |tempfile|
94+
bytes_read = 0
95+
timing = ::Benchmark.realtime do
96+
bytes_read = Buildpack.map do |buildpack|
97+
buildpack_blobstore_client.download_from_blobstore(buildpack.key, tempfile.path)
98+
File.stat(tempfile.path).size
99+
end.sum
100+
end
101+
[bytes_read, timing]
78102
end
79-
80-
[bytes_read, timing]
81103
end
82104

83105
def upload_droplet(droplet_path)
84106
copy_to_blobstore(droplet_path, droplet_blobstore_client)
85107
end
86108

87109
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)
110+
Tempfile.create('droplet-download-benchmark', tmp_dir) do |tempfile|
111+
::Benchmark.realtime do
112+
droplet_blobstore_client.download_from_blobstore(droplet_guid, tempfile.path)
113+
end
92114
end
93115
end
94116

95117
private
96118

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

100-
100.times.each do |i|
101-
f = File.open(File.join(dir, i.to_s), 'w')
102-
f.write('foo' * (65_536 + i))
150+
100.times do |i|
151+
File.write(File.join(dir, i.to_s), 'foo' * (65_536 + i))
103152
end
104153

105154
dir
@@ -110,9 +159,7 @@ def zip_resources(resource_dir, output_dir)
110159
Zip::File.open(zip_file, create: true) do |zipfile|
111160
Find.find(resource_dir).
112161
select { |f| File.file?(f) }.
113-
each do |file|
114-
zipfile.add(File.basename(file), file)
115-
end
162+
each { |file| zipfile.add(File.basename(file), file) }
116163
end
117164
zip_file
118165
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)