| Filename | /home/ss5/perl5/perlbrew/perls/perl-5.22.0/lib/site_perl/5.22.0/BenchmarkAnything/Storage/Backend/SQL.pm |
| Statements | Executed 167097 statements in 656ms |
| Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
|---|---|---|---|---|---|
| 1000 | 1 | 1 | 414ms | 27.9s | BenchmarkAnything::Storage::Backend::SQL::add_single_benchmark |
| 1000 | 1 | 1 | 160ms | 53.0s | BenchmarkAnything::Storage::Backend::SQL::process_queued_multi_benchmark |
| 1000 | 1 | 1 | 34.7ms | 28.0s | BenchmarkAnything::Storage::Backend::SQL::add_multi_benchmark |
| 1000 | 1 | 1 | 31.0ms | 941ms | BenchmarkAnything::Storage::Backend::SQL::get_single_benchmark_point |
| 1 | 1 | 1 | 1.24ms | 39.7ms | BenchmarkAnything::Storage::Backend::SQL::new |
| 1 | 1 | 1 | 96µs | 98µs | BenchmarkAnything::Storage::Backend::SQL::BEGIN@8 |
| 1 | 1 | 1 | 52µs | 34.3ms | BenchmarkAnything::Storage::Backend::SQL::gc |
| 1 | 1 | 1 | 11µs | 11µs | BenchmarkAnything::Storage::Backend::SQL::BEGIN@7 |
| 1 | 1 | 1 | 7µs | 14µs | BenchmarkAnything::Storage::Backend::SQL::BEGIN@10 |
| 1 | 1 | 1 | 6µs | 7µs | BenchmarkAnything::Storage::Backend::SQL::BEGIN@9 |
| 0 | 0 | 0 | 0s | 0s | BenchmarkAnything::Storage::Backend::SQL::__ANON__[:105] |
| 0 | 0 | 0 | 0s | 0s | BenchmarkAnything::Storage::Backend::SQL::_get_additional_key_id |
| 0 | 0 | 0 | 0s | 0s | BenchmarkAnything::Storage::Backend::SQL::benchmark_operators |
| 0 | 0 | 0 | 0s | 0s | BenchmarkAnything::Storage::Backend::SQL::default_columns |
| 0 | 0 | 0 | 0s | 0s | BenchmarkAnything::Storage::Backend::SQL::enqueue_multi_benchmark |
| 0 | 0 | 0 | 0s | 0s | BenchmarkAnything::Storage::Backend::SQL::get_full_benchmark_points |
| 0 | 0 | 0 | 0s | 0s | BenchmarkAnything::Storage::Backend::SQL::get_stats |
| 0 | 0 | 0 | 0s | 0s | BenchmarkAnything::Storage::Backend::SQL::init_search_engine |
| 0 | 0 | 0 | 0s | 0s | BenchmarkAnything::Storage::Backend::SQL::list_additional_keys |
| 0 | 0 | 0 | 0s | 0s | BenchmarkAnything::Storage::Backend::SQL::list_benchmark_names |
| 0 | 0 | 0 | 0s | 0s | BenchmarkAnything::Storage::Backend::SQL::search |
| 0 | 0 | 0 | 0s | 0s | BenchmarkAnything::Storage::Backend::SQL::search_array |
| 0 | 0 | 0 | 0s | 0s | BenchmarkAnything::Storage::Backend::SQL::search_hash |
| 0 | 0 | 0 | 0s | 0s | BenchmarkAnything::Storage::Backend::SQL::subsume |
| 0 | 0 | 0 | 0s | 0s | BenchmarkAnything::Storage::Backend::SQL::sync_search_engine |
| Line | State ments |
Time on line |
Calls | Time in subs |
Code |
|---|---|---|---|---|---|
| 1 | package BenchmarkAnything::Storage::Backend::SQL; | ||||
| 2 | # git description: v0.022-5-g18cf68b | ||||
| 3 | |||||
| 4 | 1 | 400ns | our $AUTHORITY = 'cpan:TAPPER'; | ||
| 5 | # ABSTRACT: Autonomous SQL backend to store benchmarks | ||||
| 6 | 1 | 100ns | $BenchmarkAnything::Storage::Backend::SQL::VERSION = '0.023'; | ||
| 7 | 2 | 28µs | 1 | 11µs | # spent 11µs within BenchmarkAnything::Storage::Backend::SQL::BEGIN@7 which was called:
# once (11µs+0s) by BenchmarkAnything::Storage::Frontend::Lib::connect at line 7 # spent 11µs making 1 call to BenchmarkAnything::Storage::Backend::SQL::BEGIN@7 |
| 8 | 2 | 105µs | 2 | 99µs | # spent 98µs (96+1) within BenchmarkAnything::Storage::Backend::SQL::BEGIN@8 which was called:
# once (96µs+1µs) by BenchmarkAnything::Storage::Frontend::Lib::connect at line 8 # spent 98µs making 1 call to BenchmarkAnything::Storage::Backend::SQL::BEGIN@8
# spent 2µs making 1 call to utf8::import |
| 9 | 2 | 15µs | 2 | 8µs | # spent 7µs (6+1) within BenchmarkAnything::Storage::Backend::SQL::BEGIN@9 which was called:
# once (6µs+1µs) by BenchmarkAnything::Storage::Frontend::Lib::connect at line 9 # spent 7µs making 1 call to BenchmarkAnything::Storage::Backend::SQL::BEGIN@9
# spent 1µs making 1 call to strict::import |
| 10 | 2 | 2.39ms | 2 | 20µs | # spent 14µs (7+7) within BenchmarkAnything::Storage::Backend::SQL::BEGIN@10 which was called:
# once (7µs+7µs) by BenchmarkAnything::Storage::Frontend::Lib::connect at line 10 # spent 14µs making 1 call to BenchmarkAnything::Storage::Backend::SQL::BEGIN@10
# spent 7µs making 1 call to warnings::import |
| 11 | |||||
| 12 | 1 | 5µs | my $hr_default_config = { | ||
| 13 | select_cache => 0, | ||||
| 14 | default_aggregation => 'min', | ||||
| 15 | tables => { | ||||
| 16 | unit_table => 'bench_units', | ||||
| 17 | benchmark_table => 'benchs', | ||||
| 18 | benchmark_value_table => 'bench_values', | ||||
| 19 | subsume_type_table => 'bench_subsume_types', | ||||
| 20 | benchmark_backup_value_table => 'bench_backup_values', | ||||
| 21 | additional_type_table => 'bench_additional_types', | ||||
| 22 | additional_value_table => 'bench_additional_values', | ||||
| 23 | additional_relation_table => 'bench_additional_relations', | ||||
| 24 | additional_type_relation_table => 'bench_additional_type_relations', | ||||
| 25 | backup_additional_relation_table => 'bench_backup_additional_relations', | ||||
| 26 | }, | ||||
| 27 | }; | ||||
| 28 | |||||
| 29 | 1 | 900ns | my $hr_column_ba_mapping = { | ||
| 30 | bench_value_id => 'VALUE_ID', | ||||
| 31 | bench => 'NAME', | ||||
| 32 | bench_value => 'VALUE', | ||||
| 33 | bench_unit => 'UNIT', | ||||
| 34 | created_at => 'CREATED', | ||||
| 35 | }; | ||||
| 36 | |||||
| 37 | my $fn_add_subsumed_point = sub { | ||||
| 38 | |||||
| 39 | my ( $or_self, $hr_atts ) = @_; | ||||
| 40 | |||||
| 41 | $or_self->{query}->start_transaction(); | ||||
| 42 | |||||
| 43 | eval { | ||||
| 44 | |||||
| 45 | # insert subsumed benchmark value | ||||
| 46 | $or_self->{query}->insert_benchmark_value( | ||||
| 47 | $hr_atts->{rows}[0]{bench_id}, | ||||
| 48 | $hr_atts->{type_id}, | ||||
| 49 | $hr_atts->{VALUE}, | ||||
| 50 | ); | ||||
| 51 | my $i_bench_value_id = $or_self->{query}->last_insert_id( | ||||
| 52 | $or_self->{config}{tables}{benchmark_value_table}, | ||||
| 53 | 'bench_value_id', | ||||
| 54 | ); | ||||
| 55 | |||||
| 56 | # insert subsumed benchmark additional values | ||||
| 57 | $or_self->{query}->copy_additional_values({ | ||||
| 58 | new_bench_value_id => $i_bench_value_id, | ||||
| 59 | old_bench_value_id => $hr_atts->{rows}[0]{bench_value_id}, | ||||
| 60 | }); | ||||
| 61 | |||||
| 62 | for my $hr_backup_row ( @{$hr_atts->{rows}} ) { | ||||
| 63 | |||||
| 64 | if ( $hr_backup_row->{bench_subsume_type_rank} == 1 ) { | ||||
| 65 | if ( $hr_atts->{backup} ) { | ||||
| 66 | # copy data rows to backup table | ||||
| 67 | $or_self->{query}->copy_benchmark_backup_value({ | ||||
| 68 | new_bench_value_id => $i_bench_value_id, | ||||
| 69 | old_bench_value_id => $hr_backup_row->{bench_value_id}, | ||||
| 70 | }); | ||||
| 71 | my $i_bench_backup_value_id = $or_self->{query}->last_insert_id( | ||||
| 72 | $or_self->{config}{tables}{benchmark_backup_value_table}, | ||||
| 73 | 'bench_backup_value_id', | ||||
| 74 | ); | ||||
| 75 | $or_self->{query}->copy_benchmark_backup_additional_relations({ | ||||
| 76 | new_bench_value_id => $i_bench_backup_value_id, | ||||
| 77 | old_bench_value_id => $hr_backup_row->{bench_value_id}, | ||||
| 78 | }); | ||||
| 79 | } | ||||
| 80 | } | ||||
| 81 | else { | ||||
| 82 | # update bench_value_id in backup table | ||||
| 83 | $or_self->{query}->update_benchmark_backup_value({ | ||||
| 84 | new_bench_value_id => $i_bench_value_id, | ||||
| 85 | old_bench_value_id => $hr_backup_row->{bench_value_id}, | ||||
| 86 | }); | ||||
| 87 | } | ||||
| 88 | |||||
| 89 | # now lets remove the old rows | ||||
| 90 | $or_self->{query}->delete_benchmark_additional_relations( | ||||
| 91 | $hr_backup_row->{bench_value_id}, | ||||
| 92 | ); | ||||
| 93 | $or_self->{query}->delete_benchmark_value( | ||||
| 94 | $hr_backup_row->{bench_value_id}, | ||||
| 95 | ); | ||||
| 96 | |||||
| 97 | } | ||||
| 98 | |||||
| 99 | }; | ||||
| 100 | |||||
| 101 | $or_self->{query}->finish_transaction( $@ ); | ||||
| 102 | |||||
| 103 | return 1; | ||||
| 104 | |||||
| 105 | 1 | 3µs | }; | ||
| 106 | |||||
| 107 | # spent 39.7ms (1.24+38.4) within BenchmarkAnything::Storage::Backend::SQL::new which was called:
# once (1.24ms+38.4ms) by BenchmarkAnything::Storage::Frontend::Lib::connect at line 209 of BenchmarkAnything/Storage/Frontend/Lib.pm | ||||
| 108 | |||||
| 109 | 1 | 500ns | my ( $s_self, $hr_atts ) = @_; | ||
| 110 | |||||
| 111 | 1 | 1µs | my $or_self = bless {}, $s_self; | ||
| 112 | |||||
| 113 | 1 | 500ns | for my $s_key (qw/ dbh /) { | ||
| 114 | 1 | 700ns | if (! $hr_atts->{$s_key} ) { | ||
| 115 | require Carp; | ||||
| 116 | Carp::confess("missing '$s_key' parameter"); | ||||
| 117 | return; | ||||
| 118 | } | ||||
| 119 | } | ||||
| 120 | |||||
| 121 | # get tapper benchmark configuration | ||||
| 122 | 1 | 5µs | $or_self->{config} = { %{$hr_default_config} }; | ||
| 123 | |||||
| 124 | 1 | 500ns | if ( $hr_atts->{config} ) { | ||
| 125 | require Hash::Merge; | ||||
| 126 | $or_self->{config} = { | ||||
| 127 | Hash::Merge | ||||
| 128 | ->new('LEFT_PRECEDENT') | ||||
| 129 | ->merge( | ||||
| 130 | %{$hr_atts->{config}}, | ||||
| 131 | %{$or_self->{config}}, | ||||
| 132 | ) | ||||
| 133 | }; | ||||
| 134 | } | ||||
| 135 | |||||
| 136 | 1 | 58µs | require CHI; | ||
| 137 | 1 | 1µs | if ( $or_self->{config}{select_cache} ) { | ||
| 138 | $or_self->{cache} = CHI->new( driver => 'RawMemory', global => 1 ); | ||||
| 139 | } | ||||
| 140 | |||||
| 141 | 1 | 28µs | 2 | 10µs | my $s_module = "BenchmarkAnything::Storage::Backend::SQL::Query::$hr_atts->{dbh}{Driver}{Name}"; # spent 10µs making 2 calls to DBI::common::FETCH, avg 5µs/call |
| 142 | |||||
| 143 | 1 | 200ns | my $fn_new_sub; | ||
| 144 | 1 | 400ns | eval { | ||
| 145 | 1 | 54µs | require Module::Load; | ||
| 146 | 1 | 1µs | 1 | 2µs | Module::Load::load( $s_module ); # spent 2µs making 1 call to Module::Load::load |
| 147 | 1 | 8µs | 1 | 2µs | $fn_new_sub = $s_module->can('new'); # spent 2µs making 1 call to UNIVERSAL::can |
| 148 | }; | ||||
| 149 | |||||
| 150 | 1 | 2µs | if ( $@ || !$fn_new_sub ) { | ||
| 151 | require Carp; | ||||
| 152 | Carp::confess("database engine '$hr_atts->{dbh}{Driver}{Name}' not supported"); | ||||
| 153 | return; | ||||
| 154 | } | ||||
| 155 | else { | ||||
| 156 | $or_self->{query} = $s_module->new({ | ||||
| 157 | dbh => $hr_atts->{dbh}, | ||||
| 158 | driver => $hr_atts->{dbh}{Driver}{Name}, | ||||
| 159 | debug => $hr_atts->{debug} || 0, | ||||
| 160 | verbose=> $hr_atts->{verbose} || 0, | ||||
| 161 | config => $or_self->{config}, | ||||
| 162 | 1 | 15µs | 3 | 11µs | }); # spent 6µs making 1 call to BenchmarkAnything::Storage::Backend::SQL::Query::new
# spent 5µs making 2 calls to DBI::common::FETCH, avg 2µs/call |
| 163 | } | ||||
| 164 | |||||
| 165 | 1 | 1µs | $or_self->{searchengine} = $hr_atts->{searchengine} if $hr_atts->{searchengine}; | ||
| 166 | 1 | 700ns | $or_self->{debug} = $hr_atts->{debug} || 0; | ||
| 167 | 1 | 600ns | $or_self->{verbose} = $hr_atts->{verbose} || 0; | ||
| 168 | 1 | 500ns | $or_self->{dbh_config} = $hr_atts->{dbh_config}; | ||
| 169 | |||||
| 170 | 1 | 5µs | return $or_self; | ||
| 171 | |||||
| 172 | } | ||||
| 173 | |||||
| 174 | |||||
| 175 | # spent 27.9s (414ms+27.5) within BenchmarkAnything::Storage::Backend::SQL::add_single_benchmark which was called 1000 times, avg 27.9ms/call:
# 1000 times (414ms+27.5s) by BenchmarkAnything::Storage::Backend::SQL::add_multi_benchmark at line 505, avg 27.9ms/call | ||||
| 176 | |||||
| 177 | 1000 | 525µs | my ( $or_self, $hr_benchmark, $hr_options ) = @_; | ||
| 178 | |||||
| 179 | 1000 | 1.01ms | my $hr_config = $or_self->{config}; | ||
| 180 | |||||
| 181 | 1000 | 415µs | my $VALUE_ID; # same spelling as reserved key in BenchmarkAnything schema | ||
| 182 | |||||
| 183 | # benchmark | ||||
| 184 | my $i_benchmark_id; | ||||
| 185 | 1000 | 1.92ms | if ( $hr_benchmark->{NAME} ) { | ||
| 186 | 1000 | 15.3ms | 3000 | 262ms | if ( # spent 180ms making 1000 calls to BenchmarkAnything::Storage::Backend::SQL::Query::common::select_benchmark, avg 180µs/call
# spent 57.8ms making 1000 calls to DBI::st::fetchrow_hashref, avg 58µs/call
# spent 23.9ms making 1000 calls to DBD::mysql::st::__ANON__[DBD/mysql.pm:870], avg 24µs/call |
| 187 | my $hr_bench_select = $or_self->{query} | ||||
| 188 | ->select_benchmark( $hr_benchmark->{NAME} ) | ||||
| 189 | ->fetchrow_hashref() | ||||
| 190 | ) { | ||||
| 191 | $i_benchmark_id = $hr_bench_select->{bench_id}; | ||||
| 192 | } | ||||
| 193 | else { | ||||
| 194 | my $i_unit_id; | ||||
| 195 | if ( $hr_benchmark->{UNIT} ) { | ||||
| 196 | if ( | ||||
| 197 | my $hr_unit_select = $or_self->{query} | ||||
| 198 | ->select_unit( $hr_benchmark->{UNIT} ) | ||||
| 199 | ->fetchrow_hashref() | ||||
| 200 | ) { | ||||
| 201 | $i_unit_id = $hr_unit_select->{bench_unit_id}; | ||||
| 202 | } | ||||
| 203 | else { | ||||
| 204 | $or_self->{query}->insert_unit( | ||||
| 205 | $hr_benchmark->{UNIT}, | ||||
| 206 | ); | ||||
| 207 | $i_unit_id = $or_self->{query}->last_insert_id( | ||||
| 208 | $hr_config->{tables}{unit_table}, | ||||
| 209 | 'bench_unit_id', | ||||
| 210 | ); | ||||
| 211 | } | ||||
| 212 | } | ||||
| 213 | $or_self->{query}->insert_benchmark( | ||||
| 214 | $hr_benchmark->{NAME}, $i_unit_id, | ||||
| 215 | ); | ||||
| 216 | $i_benchmark_id = $or_self->{query}->last_insert_id( | ||||
| 217 | $hr_config->{tables}{benchmark_table}, | ||||
| 218 | 'bench_id', | ||||
| 219 | ); | ||||
| 220 | } | ||||
| 221 | } | ||||
| 222 | else { | ||||
| 223 | require Carp; | ||||
| 224 | Carp::confess('missing element "NAME"'); | ||||
| 225 | return 0; | ||||
| 226 | } | ||||
| 227 | |||||
| 228 | 1000 | 2.77ms | if ( | ||
| 229 | $hr_benchmark->{data} | ||||
| 230 | && ref( $hr_benchmark->{data} ) eq 'ARRAY' | ||||
| 231 | && @{$hr_benchmark->{data}} | ||||
| 232 | ) { | ||||
| 233 | |||||
| 234 | my $i_benchmark_subsume_type_id = $or_self->{query} | ||||
| 235 | ->select_min_subsume_type() | ||||
| 236 | ->fetchrow_hashref() | ||||
| 237 | ->{bench_subsume_type_id} | ||||
| 238 | 1000 | 7.24ms | 3000 | 195ms | ; # spent 150ms making 1000 calls to BenchmarkAnything::Storage::Backend::SQL::Query::common::select_min_subsume_type, avg 150µs/call
# spent 23.7ms making 1000 calls to DBI::st::fetchrow_hashref, avg 24µs/call
# spent 21.4ms making 1000 calls to DBD::mysql::st::__ANON__[DBD/mysql.pm:870], avg 21µs/call |
| 239 | |||||
| 240 | 1000 | 568µs | my $i_counter = 1; | ||
| 241 | 1000 | 2.00ms | for my $hr_point ( @{$hr_benchmark->{data}} ) { | ||
| 242 | |||||
| 243 | 1000 | 600µs | if ( not exists $hr_point->{VALUE} ) { | ||
| 244 | require Carp; | ||||
| 245 | if ( $hr_options->{force} ) { | ||||
| 246 | Carp::cluck("missing parameter 'VALUE' in element $i_counter"); | ||||
| 247 | } | ||||
| 248 | else { | ||||
| 249 | Carp::confess("missing parameter 'VALUE' in element $i_counter"); | ||||
| 250 | } | ||||
| 251 | } | ||||
| 252 | |||||
| 253 | # benchmark value | ||||
| 254 | $or_self->{query}->insert_benchmark_value( | ||||
| 255 | $i_benchmark_id, $i_benchmark_subsume_type_id, $hr_point->{VALUE}, | ||||
| 256 | 1000 | 4.00ms | 1000 | 247ms | ); # spent 247ms making 1000 calls to BenchmarkAnything::Storage::Backend::SQL::Query::mysql::insert_benchmark_value, avg 247µs/call |
| 257 | my $i_benchmark_value_id = $or_self->{query}->last_insert_id( | ||||
| 258 | $hr_config->{tables}{benchmark_value_table}, | ||||
| 259 | 1000 | 9.48ms | 1000 | 7.75ms | 'bench_value_id', # spent 7.75ms making 1000 calls to BenchmarkAnything::Storage::Backend::SQL::Query::last_insert_id, avg 8µs/call |
| 260 | ); | ||||
| 261 | 1000 | 625µs | $VALUE_ID = $i_benchmark_value_id; | ||
| 262 | |||||
| 263 | 1000 | 3.84ms | ADDITIONAL: for my $s_key ( keys %{$hr_point} ) { | ||
| 264 | |||||
| 265 | 5650 | 3.25ms | next ADDITIONAL if $s_key eq 'VALUE'; | ||
| 266 | 4650 | 2.58ms | next ADDITIONAL if not defined $hr_point->{$s_key}; | ||
| 267 | |||||
| 268 | # additional type | ||||
| 269 | 4650 | 939µs | my $i_addtype_id; | ||
| 270 | 4650 | 1.87ms | if ( $or_self->{cache} ) { | ||
| 271 | $i_addtype_id = $or_self->{cache}->get("addtype||$s_key"); | ||||
| 272 | } | ||||
| 273 | 4650 | 2.00ms | if ( !$i_addtype_id ) { | ||
| 274 | 4650 | 33.4ms | 13950 | 852ms | if ( # spent 646ms making 4650 calls to BenchmarkAnything::Storage::Backend::SQL::Query::common::select_addtype, avg 139µs/call
# spent 110ms making 4650 calls to DBI::st::fetchrow_hashref, avg 24µs/call
# spent 95.4ms making 4650 calls to DBD::mysql::st::__ANON__[DBD/mysql.pm:870], avg 21µs/call |
| 275 | my $hr_addtype_select = $or_self->{query} | ||||
| 276 | ->select_addtype( $s_key ) | ||||
| 277 | ->fetchrow_hashref() | ||||
| 278 | ) { | ||||
| 279 | $i_addtype_id = $hr_addtype_select->{bench_additional_type_id}; | ||||
| 280 | } | ||||
| 281 | else { | ||||
| 282 | $or_self->{query}->insert_addtype( | ||||
| 283 | $s_key, | ||||
| 284 | ); | ||||
| 285 | $i_addtype_id = $or_self->{query}->last_insert_id( | ||||
| 286 | $hr_config->{tables}{addition_type_table}, | ||||
| 287 | 'bench_additional_type_id', | ||||
| 288 | ); | ||||
| 289 | } | ||||
| 290 | 4650 | 4.34ms | if ( $or_self->{cache} ) { | ||
| 291 | $or_self->{cache}->set( "addtype||$s_key" => $i_addtype_id ); | ||||
| 292 | } | ||||
| 293 | } | ||||
| 294 | |||||
| 295 | # benchmark - additional type - relation | ||||
| 296 | 4650 | 1.30ms | my $b_inserted = 0; | ||
| 297 | 4650 | 3.99ms | my $s_addtyperel = "$i_benchmark_id|$i_addtype_id"; | ||
| 298 | 4650 | 1.21ms | if ( $or_self->{cache} ) { | ||
| 299 | if ( $or_self->{cache}->get("addtyperel||$s_addtyperel") ) { | ||||
| 300 | $b_inserted = 1; | ||||
| 301 | } | ||||
| 302 | } | ||||
| 303 | 4650 | 1.98ms | if (! $b_inserted ) { | ||
| 304 | 4650 | 37.5ms | 13950 | 891ms | if(! # spent 676ms making 4650 calls to BenchmarkAnything::Storage::Backend::SQL::Query::common::select_addtyperelation, avg 145µs/call
# spent 118ms making 4650 calls to DBI::st::fetchrow_hashref, avg 25µs/call
# spent 96.5ms making 4650 calls to DBD::mysql::st::__ANON__[DBD/mysql.pm:870], avg 21µs/call |
| 305 | $or_self->{query} | ||||
| 306 | ->select_addtyperelation( $i_benchmark_id, $i_addtype_id ) | ||||
| 307 | ->fetchrow_hashref() | ||||
| 308 | ) { | ||||
| 309 | $or_self->{query} | ||||
| 310 | ->insert_addtyperelation( $i_benchmark_id, $i_addtype_id ) | ||||
| 311 | ; | ||||
| 312 | } | ||||
| 313 | 4650 | 2.67ms | if ( $or_self->{cache} ) { | ||
| 314 | $or_self->{cache}->set("addtyperel||$s_addtyperel" => 1 ); | ||||
| 315 | } | ||||
| 316 | } | ||||
| 317 | |||||
| 318 | # additional value | ||||
| 319 | 4650 | 836µs | my $i_addvalue_id; | ||
| 320 | 4650 | 5.05ms | my $s_addvalue_key = "$i_addtype_id|$hr_point->{$s_key}"; | ||
| 321 | 4650 | 1.08ms | if ( $or_self->{cache} ) { | ||
| 322 | $i_addvalue_id = $or_self->{cache}->get("addvalue||$s_addvalue_key"); | ||||
| 323 | } | ||||
| 324 | 4650 | 1.87ms | if (! $i_addvalue_id ) { | ||
| 325 | 4650 | 43.8ms | 13950 | 827ms | if ( # spent 651ms making 4650 calls to BenchmarkAnything::Storage::Backend::SQL::Query::common::select_addvalue, avg 140µs/call
# spent 95.7ms making 4650 calls to DBI::st::fetchrow_hashref, avg 21µs/call
# spent 80.4ms making 4650 calls to DBD::mysql::st::__ANON__[DBD/mysql.pm:870], avg 17µs/call |
| 326 | my $hr_addvalue_select = $or_self->{query} | ||||
| 327 | ->select_addvalue( $i_addtype_id, $hr_point->{$s_key} ) | ||||
| 328 | ->fetchrow_hashref() | ||||
| 329 | ) { | ||||
| 330 | $i_addvalue_id = $hr_addvalue_select->{bench_additional_value_id}; | ||||
| 331 | } | ||||
| 332 | else { | ||||
| 333 | $or_self->{query}->insert_addvalue( | ||||
| 334 | 28 | 76µs | 28 | 4.65ms | $i_addtype_id, $hr_point->{$s_key}, # spent 4.65ms making 28 calls to BenchmarkAnything::Storage::Backend::SQL::Query::mysql::insert_addvalue, avg 166µs/call |
| 335 | ); | ||||
| 336 | $i_addvalue_id = $or_self->{query}->last_insert_id( | ||||
| 337 | $hr_config->{tables}{addition_type_table}, | ||||
| 338 | 28 | 81µs | 28 | 158µs | 'bench_additional_value_id', # spent 158µs making 28 calls to BenchmarkAnything::Storage::Backend::SQL::Query::last_insert_id, avg 6µs/call |
| 339 | ); | ||||
| 340 | } | ||||
| 341 | 4650 | 3.87ms | if ( $or_self->{cache} ) { | ||
| 342 | $or_self->{cache}->set( "addvalue||$s_addvalue_key" => $i_addvalue_id ); | ||||
| 343 | } | ||||
| 344 | } | ||||
| 345 | |||||
| 346 | # additional value relation | ||||
| 347 | $or_self->{query}->insert_addvaluerelation( | ||||
| 348 | 4650 | 12.6ms | 4650 | 941ms | $i_benchmark_value_id, $i_addvalue_id, # spent 941ms making 4650 calls to BenchmarkAnything::Storage::Backend::SQL::Query::mysql::insert_addvaluerelation, avg 202µs/call |
| 349 | ); | ||||
| 350 | |||||
| 351 | } # ADDITIONAL | ||||
| 352 | |||||
| 353 | 1000 | 1.11ms | $i_counter++; | ||
| 354 | |||||
| 355 | } | ||||
| 356 | } | ||||
| 357 | else { | ||||
| 358 | require Carp; | ||||
| 359 | Carp::cluck('no benchmark data found'); | ||||
| 360 | return 0; | ||||
| 361 | } | ||||
| 362 | |||||
| 363 | 1000 | 4.38ms | 1000 | 1.46ms | if ( $or_self->{searchengine}{elasticsearch}{index_single_added_values_immediately} ) # spent 1.46ms making 1000 calls to Cpanel::JSON::XS::DESTROY, avg 1µs/call |
| 364 | { | ||||
| 365 | 1000 | 1.93ms | require BenchmarkAnything::Storage::Search::Elasticsearch; | ||
| 366 | my ($or_es, $s_index, $s_type) = BenchmarkAnything::Storage::Search::Elasticsearch::get_elasticsearch_client | ||||
| 367 | ( | ||||
| 368 | 1000 | 5.30ms | 1000 | 1.87s | {searchengine => $or_self->{searchengine}, ownjson => 1} # spent 1.87s making 1000 calls to BenchmarkAnything::Storage::Search::Elasticsearch::get_elasticsearch_client, avg 1.87ms/call |
| 369 | ); | ||||
| 370 | |||||
| 371 | # Sic, we re-read from DB to get the very same data we | ||||
| 372 | # *really got* stored, not just what we wish it should | ||||
| 373 | # have stored. That gives us translations like | ||||
| 374 | # num->string, CREATED date, etc., etc. | ||||
| 375 | |||||
| 376 | 1000 | 3.40ms | 1000 | 941ms | my $hr_bmk = $or_self->get_single_benchmark_point($VALUE_ID); # spent 941ms making 1000 calls to BenchmarkAnything::Storage::Backend::SQL::get_single_benchmark_point, avg 941µs/call |
| 377 | 1000 | 77.7ms | 1000 | 20.8s | my $ret = $or_es->index(index => $s_index, # spent 20.8s making 1000 calls to Search::Elasticsearch::Role::Client::Direct::__ANON__[Search/Elasticsearch/Role/Client/Direct.pm:140], avg 20.8ms/call |
| 378 | type => $s_type, | ||||
| 379 | id => $VALUE_ID, | ||||
| 380 | body => $hr_bmk); | ||||
| 381 | } | ||||
| 382 | |||||
| 383 | 1000 | 11.2ms | return 1; | ||
| 384 | |||||
| 385 | } | ||||
| 386 | |||||
| 387 | sub enqueue_multi_benchmark { | ||||
| 388 | |||||
| 389 | my ( $or_self, $ar_data_points, $hr_options ) = @_; | ||||
| 390 | |||||
| 391 | require Sereal::Encoder; | ||||
| 392 | |||||
| 393 | my $s_serialized = Sereal::Encoder->new->encode($ar_data_points); | ||||
| 394 | $or_self->{query}->insert_raw_bench_bundle($s_serialized); | ||||
| 395 | |||||
| 396 | return 1; | ||||
| 397 | |||||
| 398 | } | ||||
| 399 | |||||
| 400 | # dequeues a single bundle (can contain multiple data points) | ||||
| 401 | # spent 53.0s (160ms+52.8) within BenchmarkAnything::Storage::Backend::SQL::process_queued_multi_benchmark which was called 1000 times, avg 53.0ms/call:
# 1000 times (160ms+52.8s) by BenchmarkAnything::Storage::Frontend::Lib::process_raw_result_queue at line 689 of BenchmarkAnything/Storage/Frontend/Lib.pm, avg 53.0ms/call | ||||
| 402 | |||||
| 403 | 1000 | 1.24ms | my ( $or_self, $hr_options ) = @_; | ||
| 404 | |||||
| 405 | 1000 | 409µs | my $i_id; | ||
| 406 | my $s_serialized; | ||||
| 407 | my $ar_data_points; | ||||
| 408 | my $ar_results; | ||||
| 409 | my $or_result; | ||||
| 410 | 1000 | 13.5ms | 2000 | 4.32ms | my $driver = $or_self->{query}{dbh}{Driver}{Name}; # spent 4.32ms making 2000 calls to DBI::common::FETCH, avg 2µs/call |
| 411 | |||||
| 412 | # ===== exclusively pick single raw entry ===== | ||||
| 413 | # Lock single row via processing=1 so that only one worker handles it! | ||||
| 414 | 1000 | 67.2ms | 1000 | 60.5ms | $or_self->{query}{dbh}->do("set transaction isolation level read committed") if $driver eq "mysql"; # avoid deadlocks due to gap locking # spent 60.5ms making 1000 calls to DBI::db::do, avg 61µs/call |
| 415 | 1000 | 3.84ms | 2000 | 110ms | $or_self->{query}->start_transaction; # spent 108ms making 1000 calls to BenchmarkAnything::Storage::Backend::SQL::Query::start_transaction, avg 108µs/call
# spent 2.08ms making 1000 calls to DBI::common::STORE, avg 2µs/call |
| 416 | 1000 | 766µs | eval { | ||
| 417 | 1000 | 4.17ms | 1000 | 406ms | $ar_results = $or_self->{query}->select_raw_bench_bundle_for_lock; # spent 406ms making 1000 calls to BenchmarkAnything::Storage::Backend::SQL::Query::common::select_raw_bench_bundle_for_lock, avg 406µs/call |
| 418 | 1000 | 7.14ms | 2000 | 62.7ms | $or_result = $ar_results->fetchrow_hashref; # spent 33.1ms making 1000 calls to DBI::st::fetchrow_hashref, avg 33µs/call
# spent 29.6ms making 1000 calls to DBD::mysql::st::__ANON__[DBD/mysql.pm:870], avg 30µs/call |
| 419 | 1000 | 644µs | $i_id = $or_result->{raw_bench_bundle_id}; | ||
| 420 | 1000 | 407µs | if (!$i_id) { | ||
| 421 | $or_self->{query}->finish_transaction( $@ ); | ||||
| 422 | $or_self->{query}{dbh}->do("set transaction isolation level repeatable read") if $driver eq "mysql"; # reset to normal gap locking | ||||
| 423 | goto RETURN ; | ||||
| 424 | } | ||||
| 425 | 1000 | 2.96ms | 1000 | 335ms | $or_self->{query}->start_processing_raw_bench_bundle($i_id); # spent 335ms making 1000 calls to BenchmarkAnything::Storage::Backend::SQL::Query::common::start_processing_raw_bench_bundle, avg 335µs/call |
| 426 | }; | ||||
| 427 | 1000 | 2.62ms | 1000 | 11.8s | $or_self->{query}->finish_transaction( $@ ); # spent 11.8s making 1000 calls to BenchmarkAnything::Storage::Backend::SQL::Query::finish_transaction, avg 11.8ms/call |
| 428 | 1000 | 73.8ms | 1000 | 60.2ms | $or_self->{query}{dbh}->do("set transaction isolation level repeatable read") if $driver eq "mysql"; # reset to normal gap locking # spent 60.2ms making 1000 calls to DBI::db::do, avg 60µs/call |
| 429 | |||||
| 430 | # ===== process that single raw entry ===== | ||||
| 431 | 1000 | 4.27ms | 2000 | 106ms | $or_self->{query}->start_transaction; # spent 104ms making 1000 calls to BenchmarkAnything::Storage::Backend::SQL::Query::start_transaction, avg 104µs/call
# spent 2.12ms making 1000 calls to DBI::common::STORE, avg 2µs/call |
| 432 | 1000 | 885µs | eval { | ||
| 433 | 1000 | 1.68ms | require Sereal::Decoder; | ||
| 434 | |||||
| 435 | 1000 | 5.04ms | 1000 | 251ms | $ar_results = $or_self->{query}->select_raw_bench_bundle_for_processing($i_id); # spent 251ms making 1000 calls to BenchmarkAnything::Storage::Backend::SQL::Query::common::select_raw_bench_bundle_for_processing, avg 251µs/call |
| 436 | 1000 | 15.8ms | 2000 | 65.9ms | $s_serialized = $ar_results->fetchrow_hashref->{raw_bench_bundle_serialized}; # spent 37.6ms making 1000 calls to DBI::st::fetchrow_hashref, avg 38µs/call
# spent 28.3ms making 1000 calls to DBD::mysql::st::__ANON__[DBD/mysql.pm:870], avg 28µs/call |
| 437 | 1000 | 28.6ms | 1000 | 14.1ms | $ar_data_points = Sereal::Decoder::decode_sereal($s_serialized); # spent 14.1ms making 1000 calls to Sereal::Decoder::decode_sereal, avg 14µs/call |
| 438 | |||||
| 439 | # preserve order, otherwise add_multi_benchmark() would reorder to optimize insert | ||||
| 440 | 1000 | 6.77ms | 1000 | 28.0s | $or_self->add_multi_benchmark([$_], $hr_options) foreach @$ar_data_points; # spent 28.0s making 1000 calls to BenchmarkAnything::Storage::Backend::SQL::add_multi_benchmark, avg 28.0ms/call |
| 441 | 1000 | 6.00ms | 1000 | 673ms | $or_self->{query}->update_raw_bench_bundle_set_processed($i_id); # spent 673ms making 1000 calls to BenchmarkAnything::Storage::Backend::SQL::Query::common::update_raw_bench_bundle_set_processed, avg 673µs/call |
| 442 | }; | ||||
| 443 | 1000 | 3.54ms | 1000 | 11.0s | $or_self->{query}->finish_transaction( $@ ); # spent 11.0s making 1000 calls to BenchmarkAnything::Storage::Backend::SQL::Query::finish_transaction, avg 11.0ms/call |
| 444 | |||||
| 445 | 1000 | 11.6ms | RETURN: | ||
| 446 | return $@ ? undef : $i_id; | ||||
| 447 | |||||
| 448 | } | ||||
| 449 | |||||
| 450 | # garbage collect - initially raw_bench_bundles, but also other stuff. | ||||
| 451 | # spent 34.3ms (52µs+34.3) within BenchmarkAnything::Storage::Backend::SQL::gc which was called:
# once (52µs+34.3ms) by BenchmarkAnything::Storage::Frontend::Lib::gc at line 671 of BenchmarkAnything/Storage/Frontend/Lib.pm | ||||
| 452 | |||||
| 453 | 1 | 1µs | my ( $or_self, $hr_options ) = @_; | ||
| 454 | |||||
| 455 | 1 | 12µs | 1 | 23.5ms | $or_self->{query}->delete_processed_raw_bench_bundles; # spent 23.5ms making 1 call to BenchmarkAnything::Storage::Backend::SQL::Query::common::delete_processed_raw_bench_bundles |
| 456 | 1 | 8µs | if ($or_self->{searchengine}{elasticsearch}{enable_query}) { | ||
| 457 | 1 | 1µs | require BenchmarkAnything::Storage::Search::Elasticsearch; | ||
| 458 | my ($or_es, $s_index, $s_type) = BenchmarkAnything::Storage::Search::Elasticsearch::get_elasticsearch_client | ||||
| 459 | ( | ||||
| 460 | {searchengine => $or_self->{searchengine}} | ||||
| 461 | 1 | 6µs | 1 | 3.75ms | ); # spent 3.75ms making 1 call to BenchmarkAnything::Storage::Search::Elasticsearch::get_elasticsearch_client |
| 462 | 1 | 9µs | 2 | 3.81ms | $or_es->indices->clear_cache(index => $s_index); # spent 3.54ms making 1 call to Search::Elasticsearch::Role::Client::Direct::__ANON__[Search/Elasticsearch/Role/Client/Direct.pm:140]
# spent 273µs making 1 call to Search::Elasticsearch::Client::5_0::Direct::indices |
| 463 | } | ||||
| 464 | } | ||||
| 465 | |||||
| 466 | # spent 28.0s (34.7ms+27.9) within BenchmarkAnything::Storage::Backend::SQL::add_multi_benchmark which was called 1000 times, avg 28.0ms/call:
# 1000 times (34.7ms+27.9s) by BenchmarkAnything::Storage::Backend::SQL::process_queued_multi_benchmark at line 440, avg 28.0ms/call | ||||
| 467 | |||||
| 468 | 1000 | 706µs | my ( $or_self, $ar_data_points, $hr_options ) = @_; | ||
| 469 | |||||
| 470 | 1000 | 587µs | my $i_counter = 1; | ||
| 471 | 1000 | 1.37ms | my %h_benchmarks = (); | ||
| 472 | 1000 | 1.14ms | for my $hr_data_point ( @{$ar_data_points} ) { | ||
| 473 | |||||
| 474 | 1000 | 989µs | for my $s_param (qw/ NAME VALUE /) { | ||
| 475 | 2000 | 1.72ms | if ( not exists $hr_data_point->{$s_param} ) { | ||
| 476 | require Carp; | ||||
| 477 | if ( $hr_options->{force} ) { | ||||
| 478 | Carp::cluck("missing parameter '$s_param' in element $i_counter"); | ||||
| 479 | } | ||||
| 480 | else { | ||||
| 481 | Carp::confess("missing parameter '$s_param' in element $i_counter"); | ||||
| 482 | } | ||||
| 483 | } | ||||
| 484 | } | ||||
| 485 | |||||
| 486 | 1000 | 2.78ms | my ( $s_name, $s_unit ) = delete @{$hr_data_point}{qw/ NAME UNIT /}; | ||
| 487 | |||||
| 488 | 1000 | 4.58ms | if (! $h_benchmarks{$s_name} ) { | ||
| 489 | $h_benchmarks{$s_name} = { | ||||
| 490 | NAME => $s_name, | ||||
| 491 | UNIT => $s_unit, | ||||
| 492 | data => [], | ||||
| 493 | }; | ||||
| 494 | } | ||||
| 495 | else { | ||||
| 496 | $h_benchmarks{$s_name}{UNIT} ||= $s_unit; | ||||
| 497 | } | ||||
| 498 | |||||
| 499 | 1000 | 1.92ms | push @{$h_benchmarks{$s_name}{data}}, $hr_data_point; | ||
| 500 | |||||
| 501 | 1000 | 828µs | $i_counter++; | ||
| 502 | |||||
| 503 | } | ||||
| 504 | 1000 | 1.53ms | for my $hr_benchmark ( values %h_benchmarks ) { | ||
| 505 | 1000 | 3.79ms | 1000 | 27.9s | $or_self->add_single_benchmark( $hr_benchmark, $hr_options ); # spent 27.9s making 1000 calls to BenchmarkAnything::Storage::Backend::SQL::add_single_benchmark, avg 27.9ms/call |
| 506 | } | ||||
| 507 | |||||
| 508 | 1000 | 5.81ms | return 1; | ||
| 509 | |||||
| 510 | } | ||||
| 511 | |||||
| 512 | sub search { | ||||
| 513 | |||||
| 514 | my ( $or_self, $hr_search ) = @_; | ||||
| 515 | |||||
| 516 | return $or_self->{query}->select_benchmark_values( | ||||
| 517 | $hr_search | ||||
| 518 | ); | ||||
| 519 | |||||
| 520 | } | ||||
| 521 | |||||
| 522 | sub list_benchmark_names { | ||||
| 523 | |||||
| 524 | my ( $or_self, $s_pattern ) = @_; | ||||
| 525 | |||||
| 526 | my $ar_pattern = defined($s_pattern) ? [$s_pattern] : []; | ||||
| 527 | |||||
| 528 | my $s_key; | ||||
| 529 | if ( $or_self->{cache} ) { | ||||
| 530 | require JSON::XS; | ||||
| 531 | $s_key = JSON::XS::encode_json($ar_pattern); | ||||
| 532 | if ( my $ar_search_data = $or_self->{cache}->get("list_benchmark_names||$s_key") ) { | ||||
| 533 | return $ar_search_data; | ||||
| 534 | } | ||||
| 535 | } | ||||
| 536 | |||||
| 537 | my $ar_result = $or_self->{query} | ||||
| 538 | ->select_benchmark_names( @$ar_pattern ) | ||||
| 539 | ->fetchall_arrayref([0]); | ||||
| 540 | my $ar_benchmark_names = [ map { $_->[0] } @$ar_result ]; | ||||
| 541 | |||||
| 542 | if ( $or_self->{cache} ) { | ||||
| 543 | $or_self->{cache}->set( "list_benchmark_names||$s_key" => $ar_benchmark_names ); | ||||
| 544 | } | ||||
| 545 | |||||
| 546 | return $ar_benchmark_names; | ||||
| 547 | |||||
| 548 | } | ||||
| 549 | |||||
| 550 | sub list_additional_keys { | ||||
| 551 | |||||
| 552 | my ( $or_self, $s_pattern ) = @_; | ||||
| 553 | |||||
| 554 | my $ar_pattern = defined($s_pattern) ? [$s_pattern] : []; | ||||
| 555 | |||||
| 556 | my $s_key; | ||||
| 557 | if ( $or_self->{cache} ) { | ||||
| 558 | require JSON::XS; | ||||
| 559 | $s_key = JSON::XS::encode_json($ar_pattern); | ||||
| 560 | if ( my $ar_search_data = $or_self->{cache}->get("list_additional_keys||$s_key") ) { | ||||
| 561 | return $ar_search_data; | ||||
| 562 | } | ||||
| 563 | } | ||||
| 564 | |||||
| 565 | my $ar_result = $or_self->{query} | ||||
| 566 | ->select_additional_keys( @$ar_pattern ) | ||||
| 567 | ->fetchall_arrayref([0]); | ||||
| 568 | my $ar_key_names = [ map { $_->[0] } @$ar_result ]; | ||||
| 569 | |||||
| 570 | if ( $or_self->{cache} ) { | ||||
| 571 | $or_self->{cache}->set( "list_additional_keys||$s_key" => $ar_key_names ); | ||||
| 572 | } | ||||
| 573 | |||||
| 574 | return $ar_key_names; | ||||
| 575 | |||||
| 576 | } | ||||
| 577 | |||||
| 578 | sub get_stats { | ||||
| 579 | |||||
| 580 | my ( $or_self ) = @_; | ||||
| 581 | |||||
| 582 | my %h_searchengine_stats = (); | ||||
| 583 | my %h_flat_searchengine_stats = (); | ||||
| 584 | my %stats = (); | ||||
| 585 | |||||
| 586 | # Not strictly *stats* but useful information. | ||||
| 587 | if ( $or_self->{searchengine}{elasticsearch}{index} ) | ||||
| 588 | { | ||||
| 589 | require BenchmarkAnything::Storage::Search::Elasticsearch; | ||||
| 590 | my ($or_es, $s_index, $s_type) = BenchmarkAnything::Storage::Search::Elasticsearch::get_elasticsearch_client | ||||
| 591 | ( | ||||
| 592 | {searchengine => $or_self->{searchengine}} | ||||
| 593 | ); | ||||
| 594 | |||||
| 595 | $stats{count_datapoints} = (map {chomp; $_} split(qr/ +/, $or_es->cat->count))[2]; | ||||
| 596 | %h_searchengine_stats = | ||||
| 597 | ( | ||||
| 598 | index => $or_self->{searchengine}{elasticsearch}{index} || 'UNKNOWN', | ||||
| 599 | type => $or_self->{searchengine}{elasticsearch}{type} || 'UNKNOWN', | ||||
| 600 | enable_query => $or_self->{searchengine}{elasticsearch}{enable_query} || 0, | ||||
| 601 | cluster_health => $or_es->cluster->health, | ||||
| 602 | index_single_added_values_immediately => $or_self->{searchengine}{elasticsearch}{index_single_added_values_immediately} || 0, | ||||
| 603 | ); | ||||
| 604 | # boolean -> 0/1 | ||||
| 605 | for (values %{$h_searchengine_stats{elasticsearch}{cluster_health}}) { | ||||
| 606 | $_ = $_ ? 1 : 0 if ref eq 'JSON::XS::Boolean'; | ||||
| 607 | } | ||||
| 608 | $h_flat_searchengine_stats{"elasticsearch_$_"} = $h_searchengine_stats{$_} | ||||
| 609 | for qw(index type enable_query index_single_added_values_immediately); | ||||
| 610 | $h_flat_searchengine_stats{"elasticsearch_cluster_health_$_"} = $h_searchengine_stats{cluster_health}{$_} | ||||
| 611 | for qw(cluster_name active_shards_percent_as_number active_primary_shards number_of_nodes status); | ||||
| 612 | } | ||||
| 613 | |||||
| 614 | $stats{count_datapoints} ||= 0+$or_self->{query}->select_count_datapoints->fetch->[0]; | ||||
| 615 | $stats{count_datapointkeys} = 0+$or_self->{query}->select_count_datapointkeys->fetch->[0] if $or_self->{verbose}; | ||||
| 616 | $stats{count_metrics} = 0+$or_self->{query}->select_count_metrics->fetch->[0] if $or_self->{verbose}; | ||||
| 617 | $stats{count_keys} = 0+$or_self->{query}->select_count_keys->fetch->[0] if $or_self->{verbose}; | ||||
| 618 | |||||
| 619 | %stats = (%stats, %h_flat_searchengine_stats); | ||||
| 620 | |||||
| 621 | return \%stats; | ||||
| 622 | } | ||||
| 623 | |||||
| 624 | # spent 941ms (31.0+910) within BenchmarkAnything::Storage::Backend::SQL::get_single_benchmark_point which was called 1000 times, avg 941µs/call:
# 1000 times (31.0ms+910ms) by BenchmarkAnything::Storage::Backend::SQL::add_single_benchmark at line 376, avg 941µs/call | ||||
| 625 | |||||
| 626 | 1000 | 618µs | my ( $or_self, $i_bench_value_id ) = @_; | ||
| 627 | |||||
| 628 | 1000 | 359µs | return {} unless $i_bench_value_id; | ||
| 629 | |||||
| 630 | # cache? | ||||
| 631 | 1000 | 291µs | my $s_key; | ||
| 632 | 1000 | 705µs | if ( $or_self->{cache} ) { | ||
| 633 | require JSON::XS; | ||||
| 634 | $s_key = JSON::XS::encode_json({bench_value_id => $i_bench_value_id}); | ||||
| 635 | if ( my $hr_search_data = $or_self->{cache}->get("get_single_benchmark_point||$s_key") ) { | ||||
| 636 | return $hr_search_data; | ||||
| 637 | } | ||||
| 638 | } | ||||
| 639 | |||||
| 640 | # fetch all additional key/value fields | ||||
| 641 | my $ar_query_result = $or_self->{query} | ||||
| 642 | 1000 | 14.5ms | 3000 | 769ms | ->select_complete_benchmark_point( $i_bench_value_id ) # spent 534ms making 1000 calls to BenchmarkAnything::Storage::Backend::SQL::Query::common::select_complete_benchmark_point, avg 534µs/call
# spent 121ms making 1000 calls to DBI::st::fetchall_arrayref, avg 121µs/call
# spent 113ms making 1000 calls to DBD::_::st::fetchall_arrayref, avg 113µs/call |
| 643 | ->fetchall_arrayref({}); | ||||
| 644 | |||||
| 645 | # fetch essentials, like NAME, VALUE, UNIT | ||||
| 646 | my $hr_essentials = $or_self->{query} | ||||
| 647 | 1000 | 8.03ms | 3000 | 276ms | ->select_benchmark_point_essentials( $i_bench_value_id ) # spent 230ms making 1000 calls to BenchmarkAnything::Storage::Backend::SQL::Query::common::select_benchmark_point_essentials, avg 230µs/call
# spent 24.8ms making 1000 calls to DBI::st::fetchrow_hashref, avg 25µs/call
# spent 21.8ms making 1000 calls to DBD::mysql::st::__ANON__[DBD/mysql.pm:870], avg 22µs/call |
| 648 | ->fetchrow_hashref(); | ||||
| 649 | |||||
| 650 | # create complete BenchmarkAnything-like key/value entry | ||||
| 651 | 1000 | 334µs | my $hr_result; | ||
| 652 | 1000 | 6.23ms | $hr_result = { map { ($_->{bench_additional_type} => $_->{bench_additional_value} ) } @$ar_query_result }; | ||
| 653 | 1000 | 1.18ms | $hr_result->{NAME} = $hr_essentials->{bench}; | ||
| 654 | 1000 | 747µs | $hr_result->{VALUE} = $hr_essentials->{bench_value}; | ||
| 655 | 1000 | 986µs | $hr_result->{VALUE_ID} = $hr_essentials->{bench_value_id}; | ||
| 656 | 1000 | 890µs | $hr_result->{CREATED} = $hr_essentials->{created_at}; | ||
| 657 | 1000 | 408µs | $hr_result->{UNIT} = $hr_essentials->{bench_unit} if $hr_essentials->{bench_unit}; | ||
| 658 | |||||
| 659 | # cache! | ||||
| 660 | 1000 | 447µs | if ( $or_self->{cache} ) { | ||
| 661 | $or_self->{cache}->set( "get_single_benchmark_point||$s_key" => $hr_result ); | ||||
| 662 | } | ||||
| 663 | |||||
| 664 | 1000 | 4.73ms | return $hr_result; | ||
| 665 | } | ||||
| 666 | |||||
| 667 | sub get_full_benchmark_points { | ||||
| 668 | |||||
| 669 | my ( $or_self, $i_bench_value_id, $i_count ) = @_; | ||||
| 670 | |||||
| 671 | return [] unless $i_bench_value_id; | ||||
| 672 | |||||
| 673 | $i_count ||= 1; | ||||
| 674 | |||||
| 675 | # cache? | ||||
| 676 | my $s_key; | ||||
| 677 | if ( $or_self->{cache} ) { | ||||
| 678 | require JSON::XS; | ||||
| 679 | $s_key = JSON::XS::encode_json({bench_value_id => $i_bench_value_id}); | ||||
| 680 | if ( my $hr_search_data = $or_self->{cache}->get("get_full_benchmark_points||$s_key") ) { | ||||
| 681 | return $hr_search_data; | ||||
| 682 | } | ||||
| 683 | } | ||||
| 684 | |||||
| 685 | # fetch essentials, like NAME, VALUE, UNIT | ||||
| 686 | my $ar_essentials = $or_self->{query} | ||||
| 687 | ->select_multiple_benchmark_points_essentials($i_bench_value_id, $i_count) | ||||
| 688 | ->fetchall_arrayref({}); | ||||
| 689 | # additional key/value pairs | ||||
| 690 | my $ar_additional_values = $or_self->{query} | ||||
| 691 | ->select_multiple_benchmark_points_additionals($i_bench_value_id, $i_count) | ||||
| 692 | ->fetchall_arrayref({}); | ||||
| 693 | |||||
| 694 | # map columns into BenchmarkAnything schema | ||||
| 695 | my $hr_bmk; | ||||
| 696 | foreach my $k (keys %$hr_column_ba_mapping) | ||||
| 697 | { | ||||
| 698 | my $K = $hr_column_ba_mapping->{$k}; | ||||
| 699 | foreach my $e (@$ar_essentials) { | ||||
| 700 | $hr_bmk->{$e->{bench_value_id}}{$K} = $e->{$k} if $k ne 'bench_unit' or defined $e->{$k}; | ||||
| 701 | } | ||||
| 702 | } | ||||
| 703 | foreach (@$ar_additional_values) { | ||||
| 704 | $hr_bmk->{$_->{bench_value_id}}{$_->{bench_additional_type}} = $_->{bench_additional_value}; | ||||
| 705 | } | ||||
| 706 | # sorted (by VALUE_ID) array of BenchmarkAnything entries | ||||
| 707 | my @a_bmk = map { $hr_bmk->{$_} } sort keys %$hr_bmk; | ||||
| 708 | |||||
| 709 | # cache! | ||||
| 710 | if ( $or_self->{cache} ) { | ||||
| 711 | $or_self->{cache}->set( "get_full_benchmark_points||$s_key" => \@a_bmk ); | ||||
| 712 | } | ||||
| 713 | |||||
| 714 | return \@a_bmk; | ||||
| 715 | } | ||||
| 716 | |||||
| 717 | sub search_array { | ||||
| 718 | |||||
| 719 | my ( $or_self, $hr_search ) = @_; | ||||
| 720 | |||||
| 721 | my $debug = $or_self->{debug} || $or_self->{searchengine}{elasticsearch}{debug}; | ||||
| 722 | |||||
| 723 | my $s_key; | ||||
| 724 | if ( $or_self->{cache} ) { | ||||
| 725 | require JSON::XS; | ||||
| 726 | $s_key = JSON::XS::encode_json($hr_search); | ||||
| 727 | if ( my $ar_search_data = $or_self->{cache}->get("search_array||$s_key") ) { | ||||
| 728 | return $ar_search_data; | ||||
| 729 | } | ||||
| 730 | } | ||||
| 731 | |||||
| 732 | if ( $debug ) | ||||
| 733 | { | ||||
| 734 | require JSON::XS; | ||||
| 735 | require Data::Dumper; | ||||
| 736 | print STDERR ',-------------------'."\n"; | ||||
| 737 | print STDERR "benchmarkanything query:\n"; | ||||
| 738 | print STDERR "benchmarkanything-storage search -d '\n"; | ||||
| 739 | print STDERR JSON::XS->new->pretty->encode($hr_search); | ||||
| 740 | print STDERR "'\n"; | ||||
| 741 | print STDERR '`-------------------'."\n"; | ||||
| 742 | } | ||||
| 743 | |||||
| 744 | if ( $or_self->{searchengine}{elasticsearch}{enable_query} ) | ||||
| 745 | { | ||||
| 746 | # If anything goes wrong with Elasticsearch we just continue | ||||
| 747 | # below with relational backend query. | ||||
| 748 | |||||
| 749 | require BenchmarkAnything::Storage::Search::Elasticsearch; | ||||
| 750 | my $hr_es_query = BenchmarkAnything::Storage::Search::Elasticsearch::get_elasticsearch_query($hr_search); | ||||
| 751 | |||||
| 752 | if ($debug) | ||||
| 753 | { | ||||
| 754 | require JSON::XS; | ||||
| 755 | require Data::Dumper; | ||||
| 756 | print STDERR ',-------------------'."\n"; | ||||
| 757 | print STDERR "elasticsearch query:\n"; | ||||
| 758 | print STDERR "curl -s -XGET 'http://localhost:9200/tapper/benchmarkanything/_search?pretty' -d '\n"; | ||||
| 759 | print STDERR JSON::XS->new->pretty->encode($hr_es_query); | ||||
| 760 | print STDERR "'\n"; | ||||
| 761 | print STDERR '`-------------------'."\n"; | ||||
| 762 | } | ||||
| 763 | |||||
| 764 | # If we could transform the query then we run it against Elasticsearch and return its result. | ||||
| 765 | if (defined $hr_es_query) | ||||
| 766 | { | ||||
| 767 | # ===== client ===== | ||||
| 768 | |||||
| 769 | require BenchmarkAnything::Storage::Search::Elasticsearch; | ||||
| 770 | my ($or_es, $s_index, $s_type) = BenchmarkAnything::Storage::Search::Elasticsearch::get_elasticsearch_client | ||||
| 771 | ( | ||||
| 772 | {searchengine => $or_self->{searchengine}, ownjson => 1} | ||||
| 773 | ); | ||||
| 774 | |||||
| 775 | # ===== prepare ===== | ||||
| 776 | |||||
| 777 | # If sort fields are of type 'text' then those fields needs to | ||||
| 778 | # get their properties being declared as "fielddata":true. | ||||
| 779 | my $field_mapping = {}; | ||||
| 780 | my @sort_fields = map {keys %$_} @{$hr_es_query->{sort}||[]}; | ||||
| 781 | if (@sort_fields) { | ||||
| 782 | $field_mapping = $or_es->indices->get_mapping->{$s_index}{mappings}{$s_type}{properties}; | ||||
| 783 | } | ||||
| 784 | foreach my $sort_field (@sort_fields) | ||||
| 785 | { | ||||
| 786 | if ($field_mapping->{$sort_field}{type} and $field_mapping->{$sort_field}{type} eq 'text') | ||||
| 787 | { | ||||
| 788 | require BenchmarkAnything::Storage::Backend::SQL::Search; | ||||
| 789 | $or_es->indices->put_mapping | ||||
| 790 | ( | ||||
| 791 | index => $s_index, | ||||
| 792 | type => $s_type, | ||||
| 793 | body => { $s_type => { properties => { $sort_field => { type => 'text', | ||||
| 794 | fielddata => BenchmarkAnything::Storage::Backend::SQL::Search::json_true(), | ||||
| 795 | }}}} | ||||
| 796 | ); | ||||
| 797 | } | ||||
| 798 | } | ||||
| 799 | |||||
| 800 | # ===== search ===== | ||||
| 801 | my $hr_es_answer = $or_es->search(index => $s_index, type => $s_type, body => $hr_es_query); | ||||
| 802 | |||||
| 803 | if ( | ||||
| 804 | !$hr_es_answer->{timed_out} and | ||||
| 805 | !$hr_es_answer->{_shards}{failed} | ||||
| 806 | ) | ||||
| 807 | { | ||||
| 808 | my @ar_es_result = map { $_->{_source} } @{$hr_es_answer->{hits}{hits} || []}; | ||||
| 809 | return \@ar_es_result; | ||||
| 810 | } | ||||
| 811 | } | ||||
| 812 | |||||
| 813 | # Else no-op, continue with relational backend query. | ||||
| 814 | } | ||||
| 815 | |||||
| 816 | my $ar_result = $or_self | ||||
| 817 | ->search( $hr_search ) | ||||
| 818 | ->fetchall_arrayref({}) | ||||
| 819 | ; | ||||
| 820 | |||||
| 821 | if ( $or_self->{cache} ) { | ||||
| 822 | $or_self->{cache}->set( "search_array||$s_key" => $ar_result ); | ||||
| 823 | } | ||||
| 824 | |||||
| 825 | return $ar_result; | ||||
| 826 | |||||
| 827 | } | ||||
| 828 | |||||
| 829 | sub search_hash { | ||||
| 830 | |||||
| 831 | my ( $or_self, $hr_search ) = @_; | ||||
| 832 | |||||
| 833 | my $s_key; | ||||
| 834 | if ( $or_self->{cache} ) { | ||||
| 835 | require JSON::XS; | ||||
| 836 | $s_key = JSON::XS::encode_json($hr_search); | ||||
| 837 | if ( my $hr_search_data = $or_self->{cache}->get( "search_hash||$s_key" ) ) { | ||||
| 838 | return $hr_search_data; | ||||
| 839 | } | ||||
| 840 | } | ||||
| 841 | |||||
| 842 | if (! $hr_search->{keys} ) { | ||||
| 843 | require Carp; | ||||
| 844 | Carp::confess(q#cannot get hash search result without 'keys'#); | ||||
| 845 | return; | ||||
| 846 | } | ||||
| 847 | |||||
| 848 | my $hr_result = $or_self | ||||
| 849 | ->search( $hr_search ) | ||||
| 850 | ->fetchall_hashref($hr_search->{keys}) | ||||
| 851 | ; | ||||
| 852 | |||||
| 853 | if ( $or_self->{cache} ) { | ||||
| 854 | $or_self->{cache}->set( "search_hash||$s_key" => $hr_result ) | ||||
| 855 | } | ||||
| 856 | |||||
| 857 | return $hr_result; | ||||
| 858 | |||||
| 859 | } | ||||
| 860 | |||||
| 861 | sub subsume { | ||||
| 862 | |||||
| 863 | my ( $or_self, $hr_options ) = @_; | ||||
| 864 | |||||
| 865 | for my $s_parameter (qw/ subsume_type /) { | ||||
| 866 | if (! $hr_options->{$s_parameter}) { | ||||
| 867 | require Carp; | ||||
| 868 | Carp::confess("missing parameter '$s_parameter'"); | ||||
| 869 | return; | ||||
| 870 | } | ||||
| 871 | } | ||||
| 872 | |||||
| 873 | # check if subsume type exists | ||||
| 874 | my $hr_subsume_type = $or_self->{query} | ||||
| 875 | ->select_subsume_type( $hr_options->{subsume_type} ) | ||||
| 876 | ->fetchrow_hashref() | ||||
| 877 | ; | ||||
| 878 | if (! $hr_subsume_type ) { | ||||
| 879 | require Carp; | ||||
| 880 | Carp::confess("subsume type '$hr_options->{subsume_type}' not exists"); | ||||
| 881 | return; | ||||
| 882 | } | ||||
| 883 | if ( $hr_subsume_type->{bench_subsume_type_rank} == 1 ) { | ||||
| 884 | require Carp; | ||||
| 885 | Carp::confess("cannot subsume with type '$hr_options->{subsume_type}'"); | ||||
| 886 | return; | ||||
| 887 | } | ||||
| 888 | |||||
| 889 | # looking for values with with a higher rank subsume type | ||||
| 890 | if ( | ||||
| 891 | $or_self->{query} | ||||
| 892 | ->select_check_subsumed_values({ | ||||
| 893 | date_to => $hr_options->{date_to}, | ||||
| 894 | date_from => $hr_options->{date_from}, | ||||
| 895 | subsume_type_id => $hr_subsume_type->{bench_subsume_type_id}, | ||||
| 896 | }) | ||||
| 897 | ->rows() | ||||
| 898 | ) { | ||||
| 899 | require Carp; | ||||
| 900 | Carp::confess( | ||||
| 901 | "cannot use subsume type '$hr_options->{subsume_type}' " . | ||||
| 902 | 'because a higher rank subsume type is already used for this date period' | ||||
| 903 | ); | ||||
| 904 | return; | ||||
| 905 | } | ||||
| 906 | |||||
| 907 | # look if excluded additional types really exists | ||||
| 908 | my @a_excluded_adds; | ||||
| 909 | if ( $hr_options->{exclude_additionals} ) { | ||||
| 910 | for my $s_additional_type ( @{$hr_options->{exclude_additionals}} ) { | ||||
| 911 | if ( | ||||
| 912 | my $hr_addtype = $or_self->{query} | ||||
| 913 | ->select_addtype( $s_additional_type ) | ||||
| 914 | ->fetchrow_hashref() | ||||
| 915 | ) { | ||||
| 916 | push @a_excluded_adds, $hr_addtype->{bench_additional_type_id} | ||||
| 917 | } | ||||
| 918 | else { | ||||
| 919 | require Carp; | ||||
| 920 | Carp::confess( "additional type '$s_additional_type' not exists" ); | ||||
| 921 | return; | ||||
| 922 | } | ||||
| 923 | } | ||||
| 924 | } | ||||
| 925 | |||||
| 926 | # get all data points for subsume | ||||
| 927 | my $or_data_values = $or_self->{query}->select_data_values_for_subsume({ | ||||
| 928 | date_to => $hr_options->{date_to}, | ||||
| 929 | date_from => $hr_options->{date_from}, | ||||
| 930 | exclude_additionals => \@a_excluded_adds, | ||||
| 931 | subsume_type_id => $hr_subsume_type->{bench_subsume_type_id}, | ||||
| 932 | }); | ||||
| 933 | |||||
| 934 | require DateTime::Format::Strptime; | ||||
| 935 | my $or_strp = DateTime::Format::Strptime->new( pattern => '%F %T', ); | ||||
| 936 | |||||
| 937 | my @a_rows; | ||||
| 938 | my $i_counter = 0; | ||||
| 939 | my $i_sum_value = 0; | ||||
| 940 | my $b_backup = ((not exists $hr_options->{backup}) || $hr_options->{backup}) ? 1 : 0; | ||||
| 941 | my $s_last_key = q##; | ||||
| 942 | |||||
| 943 | while ( my $hr_values = $or_data_values->fetchrow_hashref() ) { | ||||
| 944 | |||||
| 945 | my $s_act_key = join '__', | ||||
| 946 | $hr_values->{bench_id}, | ||||
| 947 | $or_strp->parse_datetime( $hr_values->{created_at} )->strftime( $hr_subsume_type->{datetime_strftime_pattern} ), | ||||
| 948 | $hr_values->{additionals} || q##, | ||||
| 949 | ; | ||||
| 950 | |||||
| 951 | if ( $s_last_key ne $s_act_key ) { | ||||
| 952 | |||||
| 953 | if ( $i_counter ) { | ||||
| 954 | $or_self->$fn_add_subsumed_point({ | ||||
| 955 | rows => \@a_rows, | ||||
| 956 | VALUE => $i_sum_value / $i_counter, | ||||
| 957 | backup => $b_backup, | ||||
| 958 | type_id => $hr_subsume_type->{bench_subsume_type_id} | ||||
| 959 | }); | ||||
| 960 | } | ||||
| 961 | |||||
| 962 | @a_rows = (); | ||||
| 963 | $i_counter = 0; | ||||
| 964 | $i_sum_value = 0; | ||||
| 965 | $s_last_key = $s_act_key; | ||||
| 966 | |||||
| 967 | } | ||||
| 968 | |||||
| 969 | $i_counter += 1; | ||||
| 970 | $i_sum_value += $hr_values->{bench_value}; | ||||
| 971 | |||||
| 972 | push @a_rows, $hr_values; | ||||
| 973 | |||||
| 974 | } | ||||
| 975 | |||||
| 976 | if ( $i_counter ) { | ||||
| 977 | $or_self->$fn_add_subsumed_point({ | ||||
| 978 | rows => \@a_rows, | ||||
| 979 | VALUE => $i_sum_value / $i_counter, | ||||
| 980 | backup => $b_backup, | ||||
| 981 | type_id => $hr_subsume_type->{bench_subsume_type_id} | ||||
| 982 | }); | ||||
| 983 | } | ||||
| 984 | |||||
| 985 | return 1; | ||||
| 986 | |||||
| 987 | } | ||||
| 988 | |||||
| 989 | sub _get_additional_key_id { | ||||
| 990 | |||||
| 991 | my ( $or_self, $s_key ) = @_; | ||||
| 992 | |||||
| 993 | return $or_self->{query}->select_additional_key_id($s_key)->fetch->[0]; | ||||
| 994 | |||||
| 995 | } | ||||
| 996 | |||||
| 997 | sub default_columns { | ||||
| 998 | |||||
| 999 | my ( $or_self ) = @_; | ||||
| 1000 | |||||
| 1001 | return $or_self->{query}->default_columns; | ||||
| 1002 | |||||
| 1003 | } | ||||
| 1004 | |||||
| 1005 | sub benchmark_operators { | ||||
| 1006 | |||||
| 1007 | my ( $or_self ) = @_; | ||||
| 1008 | |||||
| 1009 | return $or_self->{query}->benchmark_operators; | ||||
| 1010 | |||||
| 1011 | } | ||||
| 1012 | |||||
| 1013 | sub init_search_engine | ||||
| 1014 | { | ||||
| 1015 | my ( $or_self, $b_force) = @_; | ||||
| 1016 | |||||
| 1017 | require BenchmarkAnything::Storage::Backend::SQL::Search; | ||||
| 1018 | BenchmarkAnything::Storage::Backend::SQL::Search::init_search_engine (@_); | ||||
| 1019 | } | ||||
| 1020 | |||||
| 1021 | sub sync_search_engine | ||||
| 1022 | { | ||||
| 1023 | require BenchmarkAnything::Storage::Backend::SQL::Search; | ||||
| 1024 | BenchmarkAnything::Storage::Backend::SQL::Search::sync_search_engine (@_); | ||||
| 1025 | } | ||||
| 1026 | |||||
| 1027 | 1 | 4µs | 1; | ||
| 1028 | |||||
| 1029 | __END__ |