summaryrefslogtreecommitdiff
path: root/lib/gitlab
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gitlab')
-rw-r--r--lib/gitlab/action_rate_limiter.rb47
-rw-r--r--lib/gitlab/git/commit.rb13
-rw-r--r--lib/gitlab/gitaly_client/commit_service.rb9
3 files changed, 69 insertions, 0 deletions
diff --git a/lib/gitlab/action_rate_limiter.rb b/lib/gitlab/action_rate_limiter.rb
new file mode 100644
index 00000000000..4cd3bdefda3
--- /dev/null
+++ b/lib/gitlab/action_rate_limiter.rb
@@ -0,0 +1,47 @@
+module Gitlab
+ # This class implements a simple rate limiter that can be used to throttle
+ # certain actions. Unlike Rack Attack and Rack::Throttle, which operate at
+ # the middleware level, this can be used at the controller level.
+ class ActionRateLimiter
+ TIME_TO_EXPIRE = 60 # 1 min
+
+ attr_accessor :action, :expiry_time
+
+ def initialize(action:, expiry_time: TIME_TO_EXPIRE)
+ @action = action
+ @expiry_time = expiry_time
+ end
+
+ # Increments the given cache key and increments the value by 1 with the
+ # given expiration time. Returns the incremented value.
+ #
+ # key - An array of ActiveRecord instances
+ def increment(key)
+ value = 0
+
+ Gitlab::Redis::Cache.with do |redis|
+ cache_key = action_key(key)
+ value = redis.incr(cache_key)
+ redis.expire(cache_key, expiry_time) if value == 1
+ end
+
+ value
+ end
+
+ # Increments the given key and returns true if the action should
+ # be throttled.
+ #
+ # key - An array of ActiveRecord instances
+ # threshold_value - The maximum number of times this action should occur in the given time interval
+ def throttled?(key, threshold_value)
+ self.increment(key) > threshold_value
+ end
+
+ private
+
+ def action_key(key)
+ serialized = key.map { |obj| "#{obj.class.model_name.to_s.underscore}:#{obj.id}" }.join(":")
+ "action_rate_limiter:#{action}:#{serialized}"
+ end
+ end
+end
diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb
index e90b158fb34..145721dea76 100644
--- a/lib/gitlab/git/commit.rb
+++ b/lib/gitlab/git/commit.rb
@@ -228,6 +228,19 @@ module Gitlab
end
end
end
+
+ # Only to be used when the object ids will not necessarily have a
+ # relation to each other. The last 10 commits for a branch for example,
+ # should go through .where
+ def batch_by_oid(repo, oids)
+ repo.gitaly_migrate(:list_commits_by_oid) do |is_enabled|
+ if is_enabled
+ repo.gitaly_commit_client.list_commits_by_oid(oids)
+ else
+ oids.map { |oid| find(repo, oid) }.compact
+ end
+ end
+ end
end
def initialize(repository, raw_commit, head = nil)
diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb
index 7985f5b5457..fb3e27770b4 100644
--- a/lib/gitlab/gitaly_client/commit_service.rb
+++ b/lib/gitlab/gitaly_client/commit_service.rb
@@ -169,6 +169,15 @@ module Gitlab
consume_commits_response(response)
end
+ def list_commits_by_oid(oids)
+ request = Gitaly::ListCommitsByOidRequest.new(repository: @gitaly_repo, oid: oids)
+
+ response = GitalyClient.call(@repository.storage, :commit_service, :list_commits_by_oid, request, timeout: GitalyClient.medium_timeout)
+ consume_commits_response(response)
+ rescue GRPC::Unknown # If no repository is found, happens mainly during testing
+ []
+ end
+
def commits_by_message(query, revision: '', path: '', limit: 1000, offset: 0)
request = Gitaly::CommitsByMessageRequest.new(
repository: @gitaly_repo,