1+ # frozen_string_literal: true
2+
13require 'benchmark'
24require 'find'
35require 'zip'
6+ require 'tempfile'
7+ require 'fileutils'
8+ require 'securerandom'
49
510module 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" )
26-
27- droplet_guid , resource_timing = upload_droplet ( zip_file )
28- puts ( "droplet upload timing: #{ resource_timing * 1000 } ms" )
29-
30- resource_timing = download_droplet ( droplet_guid , resource_dir )
31- puts ( "droplet download timing: #{ resource_timing * 1000 } ms" )
32-
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" )
37-
38- [
39- [ '0.005MB' , ( 0.005 * 1024 * 1024 ) . to_i ] ,
40- [ '10MB' , 10 * 1024 * 1024 ] ,
41- [ '200MB' , 200 * 1024 * 1024 ] ,
42- [ '500MB' , 500 * 1024 * 1024 ]
43- ] . each do |label , size |
44- tempfile = Tempfile . new ( "big-droplet-#{ label } " , resource_dir )
45- File . open ( tempfile . path , 'wb' ) do |f |
46- chunk = '0' * ( 1024 * 1024 ) # 1MB chunk
47- written = 0
48- while written < size
49- to_write = [ chunk . bytesize , size - written ] . min
50- f . write ( chunk . byteslice ( 0 , to_write ) )
51- written += to_write
52- end
53- end
47+ log_timing ( 'buildpack download timing' , timing )
5448
55- big_droplet_guid , resource_timing = upload_droplet ( tempfile . path )
56- puts ( "big droplet #{ label } upload timing: #{ resource_timing * 1000 } ms" )
49+ droplet_guid , timing = upload_droplet ( zip_file )
50+ log_timing ( 'droplet upload timing' , timing )
51+ log_timing ( 'droplet download timing' , download_droplet ( droplet_guid , resource_dir ) )
5752
58- tempfile . close!
59- end
53+ SIZES . each do |label , bytes |
54+ Tempfile . create ( [ "big-droplet-#{ label } " , '.bin' ] , resource_dir ) do |tempfile |
55+ write_file_of_size ( tempfile . path , bytes )
6056
61- resource_timing = download_droplet ( big_droplet_guid , resource_dir )
62- puts ( "big droplet download timing: #{ resource_timing * 1000 } ms" )
57+ guid , upload_timing = upload_droplet ( tempfile . path )
58+ big_droplet_guids << guid
59+ log_timing ( "big droplet #{ label } upload timing" , upload_timing )
60+ log_timing ( "big droplet #{ label } download timing" , download_droplet ( guid , resource_dir ) )
61+ end
62+ end
6363 ensure
64- FileUtils . remove_dir ( resource_dir , true )
65- FileUtils . remove_dir ( zip_output_dir , true )
66- package_blobstore_client . delete ( package_guid ) if package_guid
67- droplet_blobstore_client . delete ( droplet_guid ) if droplet_guid
68- droplet_blobstore_client . delete ( big_droplet_guid ) if big_droplet_guid
64+ FileUtils . remove_dir ( resource_dir , true ) if resource_dir
65+ FileUtils . remove_dir ( zip_output_dir , true ) if zip_output_dir
66+
67+ safe_delete ( package_blobstore_client , package_guid )
68+ safe_delete ( droplet_blobstore_client , droplet_guid )
69+ Array ( big_droplet_guids ) . each { |g | safe_delete ( droplet_blobstore_client , g ) }
6970 end
7071
7172 def resource_match ( dir_path )
72- resources = Find . find ( dir_path ) .
73- select { |f | File . file? ( f ) } .
74- map { |f | { 'size' => File . stat ( f ) . size , 'sha1' => Digester . new . digest_path ( f ) } }
73+ resources = Find . find ( dir_path )
74+ . select { |f | File . file? ( f ) }
75+ . map { |f | { 'size' => File . stat ( f ) . size , 'sha1' => Digester . new . digest_path ( f ) } }
7576
7677 ::Benchmark . realtime do
7778 resource_pool . match_resources ( resources )
@@ -83,46 +84,69 @@ def upload_package(package_path)
8384 end
8485
8586 def download_package ( package_guid , tmp_dir )
86- tempfile = Tempfile . new ( 'package-download-benchmark' , tmp_dir )
87- ::Benchmark . realtime do
88- package_blobstore_client . download_from_blobstore ( package_guid , tempfile . path )
87+ Tempfile . create ( 'package-download-benchmark' , tmp_dir ) do |tempfile |
88+ ::Benchmark . realtime do
89+ package_blobstore_client . download_from_blobstore ( package_guid , tempfile . path )
90+ end
8991 end
9092 end
9193
9294 def download_buildpacks ( tmp_dir )
93- tempfile = Tempfile . new ( 'buildpack-download-benchmark' , tmp_dir )
94- bytes_read = 0
95-
96- timing = ::Benchmark . realtime do
97- bytes_read = Buildpack . map do |buildpack |
98- buildpack_blobstore_client . download_from_blobstore ( buildpack . key , tempfile . path )
99- File . stat ( tempfile . path ) . size
100- end . sum
95+ Tempfile . create ( 'buildpack-download-benchmark' , tmp_dir ) do |tempfile |
96+ bytes_read = 0
97+ timing = ::Benchmark . realtime do
98+ bytes_read = Buildpack . map do |buildpack |
99+ buildpack_blobstore_client . download_from_blobstore ( buildpack . key , tempfile . path )
100+ File . stat ( tempfile . path ) . size
101+ end . sum
102+ end
103+ [ bytes_read , timing ]
101104 end
102-
103- [ bytes_read , timing ]
104105 end
105106
106107 def upload_droplet ( droplet_path )
107108 copy_to_blobstore ( droplet_path , droplet_blobstore_client )
108109 end
109110
110111 def download_droplet ( droplet_guid , tmp_dir )
111- tempfile = Tempfile . new ( 'droplet-download-benchmark' , tmp_dir )
112-
113- :: Benchmark . realtime do
114- droplet_blobstore_client . download_from_blobstore ( droplet_guid , tempfile . path )
112+ Tempfile . create ( 'droplet-download-benchmark' , tmp_dir ) do | tempfile |
113+ :: Benchmark . realtime do
114+ droplet_blobstore_client . download_from_blobstore ( droplet_guid , tempfile . path )
115+ end
115116 end
116117 end
117118
118119 private
119120
121+ def log_timing ( label , seconds )
122+ puts ( "#{ label } : #{ ( seconds * 1000 ) . round ( 3 ) } ms" )
123+ end
124+
125+ def safe_delete ( client , guid )
126+ return if guid . nil?
127+
128+ client . delete ( guid )
129+ rescue StandardError => e
130+ # don't fail the benchmark run if cleanup fails
131+ warn ( "cleanup failed for guid=#{ guid } : #{ e . class } : #{ e . message } " )
132+ end
133+
134+ def write_file_of_size ( path , bytes )
135+ File . open ( path , 'wb' ) do |f |
136+ remaining = bytes
137+ while remaining > 0
138+ to_write = [ CHUNK_1MB . bytesize , remaining ] . min
139+ f . write ( CHUNK_1MB , to_write )
140+ remaining -= to_write
141+ end
142+ end
143+ end
144+
120145 def generate_resources
121146 dir = Dir . mktmpdir
122147
123- 100 . times . each do |i |
124- f = File . open ( File . join ( dir , i . to_s ) , 'w' )
125- f . write ( 'foo' * ( 65_536 + i ) )
148+ 100 . times do |i |
149+ File . write ( File . join ( dir , i . to_s ) , 'foo' * ( 65_536 + i ) )
126150 end
127151
128152 dir
@@ -131,11 +155,9 @@ def generate_resources
131155 def zip_resources ( resource_dir , output_dir )
132156 zip_file = File . join ( output_dir , 'zipped_package' )
133157 Zip ::File . open ( zip_file , create : true ) do |zipfile |
134- Find . find ( resource_dir ) .
135- select { |f | File . file? ( f ) } .
136- each do |file |
137- zipfile . add ( File . basename ( file ) , file )
138- end
158+ Find . find ( resource_dir )
159+ . select { |f | File . file? ( f ) }
160+ . each { |file | zipfile . add ( File . basename ( file ) , file ) }
139161 end
140162 zip_file
141163 end
0 commit comments