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