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+ '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
0 commit comments