From 838cd09aca877b1d3d0c0f7508ec984b7ac9bbb9 Mon Sep 17 00:00:00 2001 From: Rene Krenn Date: Tue, 15 Oct 2019 11:14:00 +0200 Subject: [PATCH] TT#68703 workaround slow contract_cnt queries of profiles Change-Id: Ieedb868b268890ebf16e6f036ef68601e73f3bcd --- lib/NGCP/Panel/Controller/Billing.pm | 4 ++- lib/NGCP/Panel/Controller/Network.pm | 4 ++- lib/NGCP/Panel/Role/API/BillingNetworks.pm | 17 +++++----- lib/NGCP/Panel/Role/API/BillingProfiles.pm | 1 + lib/NGCP/Panel/Utils/Billing.pm | 38 ++++++++++++++++++---- lib/NGCP/Panel/Utils/BillingNetworks.pm | 32 ++++++++++++++++-- 6 files changed, 77 insertions(+), 19 deletions(-) diff --git a/lib/NGCP/Panel/Controller/Billing.pm b/lib/NGCP/Panel/Controller/Billing.pm index b3cd79e236..b2e3aeef7a 100644 --- a/lib/NGCP/Panel/Controller/Billing.pm +++ b/lib/NGCP/Panel/Controller/Billing.pm @@ -49,6 +49,7 @@ sub _profile_resultset_admin { 'me.status' => { '!=' => 'terminated' }, }, { '+select' => [ { '' => \[ NGCP::Panel::Utils::Billing::get_contract_count_stmt() ] , -as => 'contract_cnt' }, + { '' => \[ NGCP::Panel::Utils::Billing::get_contract_exists_stmt() ] , -as => 'contract_exists' }, { '' => \[ NGCP::Panel::Utils::Billing::get_package_count_stmt() ] , -as => 'package_cnt' }, ], }); return $rs; @@ -62,6 +63,7 @@ sub _profile_resultset_reseller { 'me.status' => { '!=' => 'terminated' }, }, { '+select' => [ { '' => \[ NGCP::Panel::Utils::Billing::get_contract_count_stmt() ] , -as => 'contract_cnt' }, + { '' => \[ NGCP::Panel::Utils::Billing::get_contract_exists_stmt() ] , -as => 'contract_exists' }, { '' => \[ NGCP::Panel::Utils::Billing::get_package_count_stmt() ] , -as => 'package_cnt' }, ], }); return $rs; @@ -315,7 +317,7 @@ sub terminate :Chained('base') :PathPart('terminate') :Args(0) { try { #todo: putting the profile fetch into a transaction wouldn't help since the count columns a prone to phantom reads... - unless($profile->get_column('contract_cnt') == 0) { + if($profile->get_column('contract_exists')) { die(['Cannnot terminate billing profile that is still used in profile mappings', "showdetails"]); } unless($profile->get_column('package_cnt') == 0) { diff --git a/lib/NGCP/Panel/Controller/Network.pm b/lib/NGCP/Panel/Controller/Network.pm index 11b603a15d..ab548269c6 100644 --- a/lib/NGCP/Panel/Controller/Network.pm +++ b/lib/NGCP/Panel/Controller/Network.pm @@ -48,6 +48,7 @@ sub _network_resultset_admin { 'me.status' => { '!=' => 'terminated' }, }, { '+select' => [ { '' => \[ NGCP::Panel::Utils::BillingNetworks::get_contract_count_stmt() ] , -as => 'contract_cnt' }, + { '' => \[ NGCP::Panel::Utils::Billing::get_contract_exists_stmt() ] , -as => 'contract_exists' }, { '' => \[ NGCP::Panel::Utils::BillingNetworks::get_package_count_stmt() ] , -as => 'package_cnt' }, ], }); } @@ -65,6 +66,7 @@ sub _network_resultset_reseller { 'me.status' => { '!=' => 'terminated' }, }, { '+select' => [ { '' => \[ NGCP::Panel::Utils::BillingNetworks::get_contract_count_stmt() ] , -as => 'contract_cnt' }, + { '' => \[ NGCP::Panel::Utils::Billing::get_contract_exists_stmt() ] , -as => 'contract_exists' }, { '' => \[ NGCP::Panel::Utils::BillingNetworks::get_package_count_stmt() ] , -as => 'package_cnt' }, ], }); } @@ -223,7 +225,7 @@ sub terminate :Chained('base') :PathPart('terminate') :Args(0) { try { #todo: putting the network fetch into a transaction wouldn't help since the count columns a prone to phantom reads... - unless($network->get_column('contract_cnt') == 0) { + if($network->get_column('contract_exists')) { die(['Cannnot terminate billing network that is still used in profile mappings', "showdetails"]); } unless($network->get_column('package_cnt') == 0) { diff --git a/lib/NGCP/Panel/Role/API/BillingNetworks.pm b/lib/NGCP/Panel/Role/API/BillingNetworks.pm index 93b7258dbd..7e7c5b5f73 100644 --- a/lib/NGCP/Panel/Role/API/BillingNetworks.pm +++ b/lib/NGCP/Panel/Role/API/BillingNetworks.pm @@ -32,7 +32,7 @@ sub hal_from_item { delete $blockelem{_ipv4_net_from}; delete $blockelem{_ipv4_net_to}; delete $blockelem{_ipv6_net_from}; - delete $blockelem{_ipv6_net_to}; + delete $blockelem{_ipv6_net_to}; push @blocks, \%blockelem; } $resource{blocks} = \@blocks; @@ -71,11 +71,12 @@ sub _item_rs { my $item_rs = $c->model('DB')->resultset('billing_networks')->search_rs({ 'me.status' => { '!=' => 'terminated' } }); my $search_xtra = { '+select' => [ { '' => \[ NGCP::Panel::Utils::BillingNetworks::get_contract_count_stmt() ] , -as => 'contract_cnt' }, + { '' => \[ NGCP::Panel::Utils::BillingNetworks::get_contract_exists_stmt() ] , -as => 'contract_exists' }, { '' => \[ NGCP::Panel::Utils::BillingNetworks::get_package_count_stmt() ] , -as => 'package_cnt' }, ], - }; + }; if($c->user->roles eq "admin") { $item_rs = $item_rs->search(undef, - $search_xtra); + $search_xtra); } elsif($c->user->roles eq "reseller") { $item_rs = $item_rs->search({ reseller_id => $c->user->reseller_id }, $search_xtra); @@ -95,7 +96,7 @@ sub update_item { delete $resource->{id}; my $schema = $c->model('DB'); - + $form //= $self->get_form($c); # TODO: for some reason, formhandler lets missing reseller slip thru $resource->{reseller_id} //= undef; @@ -109,18 +110,18 @@ sub update_item { my ($err) = @_; $self->error($c, HTTP_UNPROCESSABLE_ENTITY, $err); }); - + return unless NGCP::Panel::Utils::BillingNetworks::check_network_update_item($c,$resource,$item,sub { my ($err) = @_; $self->error($c, HTTP_UNPROCESSABLE_ENTITY, $err); }); - + return unless $self->prepare_blocks_resource($c,$resource); my $blocks = delete $resource->{blocks}; - + try { $item->update($resource); - $item->billing_network_blocks->delete; + $item->billing_network_blocks->delete; for my $block (@$blocks) { $item->create_related("billing_network_blocks", $block); } diff --git a/lib/NGCP/Panel/Role/API/BillingProfiles.pm b/lib/NGCP/Panel/Role/API/BillingProfiles.pm index 6115fbbd4d..167649ad0f 100644 --- a/lib/NGCP/Panel/Role/API/BillingProfiles.pm +++ b/lib/NGCP/Panel/Role/API/BillingProfiles.pm @@ -21,6 +21,7 @@ sub _item_rs { my $item_rs = $c->model('DB')->resultset('billing_profiles'); my $search_xtra = { '+select' => [ { '' => \[ NGCP::Panel::Utils::Billing::get_contract_count_stmt() ] , -as => 'contract_cnt' }, + { '' => \[ NGCP::Panel::Utils::Billing::get_contract_exists_stmt() ] , -as => 'contract_exists' }, { '' => \[ NGCP::Panel::Utils::Billing::get_package_count_stmt() ] , -as => 'package_cnt' }, ], }; if($c->user->roles eq "admin") { diff --git a/lib/NGCP/Panel/Utils/Billing.pm b/lib/NGCP/Panel/Utils/Billing.pm index d7efe40d75..0ab432e20a 100644 --- a/lib/NGCP/Panel/Utils/Billing.pm +++ b/lib/NGCP/Panel/Utils/Billing.pm @@ -31,18 +31,19 @@ sub check_profile_update_item { # return 0 unless &{$err_code}("The prepaid rating library is mandatory for a prepaid profile.",'prepaid_library'); #} + my $contract_exists = $old_item->get_column('contract_exists'); my $contract_cnt = $old_item->get_column('contract_cnt'); #my $package_cnt = $old_item->get_column('package_cnt'); - if (($contract_cnt > 0) + if ($contract_exists && defined $new_resource->{interval_charge} && $old_item->interval_charge != $new_resource->{interval_charge}) { return 0 unless &{$err_code}("Interval charge cannot be changed (profile linked to $contract_cnt contracts).",'interval_charge'); } - if (($contract_cnt > 0) + if ($contract_exists && defined $new_resource->{interval_free_time} && $old_item->interval_free_time != $new_resource->{interval_free_time}) { return 0 unless &{$err_code}("Interval free time cannot be changed (profile linked to $contract_cnt contracts).",'interval_free_time'); } - if (($contract_cnt > 0) + if ($contract_exists && defined $new_resource->{interval_free_cash} && $old_item->interval_free_cash != $new_resource->{interval_free_cash}) { return 0 unless &{$err_code}("Interval free cash cannot be changed (profile linked to $contract_cnt contracts).",'interval_free_cash'); } @@ -447,11 +448,11 @@ sub switch_prepaid { } -sub get_contract_count_stmt { +sub get_contract_exists_stmt { return <= now()) EOS } + sub get_package_count_stmt { - return "select count(distinct pp.id) from `billing`.`package_profile_sets` pps join `billing`.`profile_packages` pp on pp.id = pps.package_id where pps.`profile_id` = `me`.`id`"; # and pp.status != 'terminated'"; + + return <= now()) +EOS + +} + sub get_package_count_stmt { - return "select count(distinct pp.id) from `billing`.`package_profile_sets` pps join `billing`.`profile_packages` pp on pp.id = pps.package_id where pps.`network_id` = `me`.`id`"; # and pp.status != 'terminated'"; + + return <