diff --git a/NEWS.md b/NEWS.md index 002440acc0..8a0d8c3360 100644 --- a/NEWS.md +++ b/NEWS.md @@ -81,6 +81,8 @@ Development - Fix Auth URL generation while establishing a connection with Google Drive [#16357](https://github.com/CartoDB/cartodb/pull/16357) - Fix adding license metadata to a dataset [#16356](https://github.com/CartoDB/cartodb/pull/16356) - Fix notifications when organization seats limit is reached [#16359](https://github.com/CartoDB/cartodb/pull/16359) +- Notify Support when a user is reaching the named maps limit [#16368](https://github.com/CartoDB/cartodb/pull/16368) +- Remove old named maps when a user is reaching the named maps limit [#16368](https://github.com/CartoDB/cartodb/pull/16368) - Fix privacy dropdown when user is editing a map [#16367](https://github.com/CartoDB/cartodb/pull/16367) - Add a new rake to update a user username [#16370](https://github.com/CartoDB/cartodb/pull/16370) - Add a check before destroying user tables in order to avoid deleting dependent maps [#16381](https://github.com/CartoDB/cartodb/pull/16381) diff --git a/app/mailers/reporter_mailer.rb b/app/mailers/reporter_mailer.rb index dc97053704..33e63fdaf8 100644 --- a/app/mailers/reporter_mailer.rb +++ b/app/mailers/reporter_mailer.rb @@ -8,4 +8,11 @@ class ReporterMailer < ActionMailer::Base mail to: mail_to, subject: @subject end + + def named_maps_near_the_limit(message) + mail_to = Cartodb.get_config(:mailer, 'support_email') + mail_from = Cartodb.get_config(:mailer, 'internal_notifications_email') + + mail from: mail_from, to: mail_to, subject: message if mail_to && mail_from + end end diff --git a/app/views/reporter_mailer/named_maps_near_the_limit.html.erb b/app/views/reporter_mailer/named_maps_near_the_limit.html.erb new file mode 100644 index 0000000000..c6ca18dc19 --- /dev/null +++ b/app/views/reporter_mailer/named_maps_near_the_limit.html.erb @@ -0,0 +1,8 @@ + + +

+ There is one username that is reaching the named maps limit. Response Team should check if it's possible to + remove a few named maps! +

+ + diff --git a/lib/carto/named_maps/api.rb b/lib/carto/named_maps/api.rb index 3e8edf5c9b..4867216450 100644 --- a/lib/carto/named_maps/api.rb +++ b/lib/carto/named_maps/api.rb @@ -12,6 +12,7 @@ module Carto HTTP_REQUEST_TIMEOUT_SECONDS = 60 RETRY_TIME_SECONDS = 2 MAX_RETRY_ATTEMPTS = 3 + MAX_NAMED_MAPS_TO_CLEAN = 100 attr_accessor :user, :visualization @@ -44,7 +45,21 @@ module Carto response_code = response.code if response_code.to_s.match?(/^2/) - ::JSON.parse(response.response_body).deep_symbolize_keys + response_body = ::JSON.parse(response.response_body).deep_symbolize_keys + if response_body.key?(:limit_message) + # Send message to support and clean some named_maps + ReporterMailer.named_maps_near_the_limit(response_body[:limit_message]).deliver_now + tables = Carto::UserTable.where(user_id: user.id, privacy: 0) + .limit(MAX_NAMED_MAPS_TO_CLEAN) + .order('updated_at') + named_maps_ids = tables.map { |t| "tpl_#{t.visualization.id.tr('-', '_')}" } + urls = named_maps_ids.map { |id| url(template_name: id) } + ::Resque.enqueue( + ::Resque::UserJobs::NamedMapsLimitsJobs::CleanNamedMaps, + { urls: urls, request_params: request_params } + ) + end + response_body elsif response_code != 409 # Ignore when max number of templates is reached log_response(response, 'create') end @@ -90,7 +105,6 @@ module Carto def destroy(retries: 0) stats_aggregator.timing('carto-named-maps-api.destroy') do url = url(template_name: @named_map_template.name) - response = http_client.delete(url, request_params) response_code_string = response.code.to_s diff --git a/lib/resque/user_jobs.rb b/lib/resque/user_jobs.rb index 81f63ce207..4b13073ff3 100644 --- a/lib/resque/user_jobs.rb +++ b/lib/resque/user_jobs.rb @@ -58,6 +58,36 @@ module Resque end end + module NamedMapsLimitsJobs + class CleanNamedMaps < BaseJob + + RETRY_TIME_SECONDS = 2 + MAX_RETRY_ATTEMPTS = 3 + + @queue = :users + + def self.perform(options = {}) + run_action(options, @queue, lambda { |job_options| + job_options.symbolize_keys[:urls].each { |url| delete(url, job_options.symbolize_keys[:request_params]) } + }) + end + + def self.delete(url, request_params, retries: 0) + http_client = Carto::Http::Client.get('named_maps') + response = http_client.delete(url, request_params.symbolize_keys) + + response_code_string = response.code.to_s + if response_code_string.start_with?('2') + response.response_body + elsif (response_code_string.start_with?('5') || response.code == 429) && retries < MAX_RETRY_ATTEMPTS + sleep(RETRY_TIME_SECONDS**retries) + delete(url, request_params, retries: retries + 1) + end + end + + end + end + module RateLimitsJobs module SyncRedis extend ::Resque::Metrics