Merge branch 'tc-geo-prune-events-correctly-ce' into 'master'
Add Gitlab::Database::Subquery.self_join to delete_all with limit See merge request gitlab-org/gitlab-ce!21839
This commit is contained in:
commit
c2da94187d
4 changed files with 97 additions and 0 deletions
|
@ -94,6 +94,7 @@ description: 'Learn how to contribute to GitLab.'
|
||||||
- [Verifying database capabilities](verifying_database_capabilities.md)
|
- [Verifying database capabilities](verifying_database_capabilities.md)
|
||||||
- [Database Debugging and Troubleshooting](database_debugging.md)
|
- [Database Debugging and Troubleshooting](database_debugging.md)
|
||||||
- [Query Count Limits](query_count_limits.md)
|
- [Query Count Limits](query_count_limits.md)
|
||||||
|
- [Database helper modules](database_helpers.md)
|
||||||
|
|
||||||
## Testing guides
|
## Testing guides
|
||||||
|
|
||||||
|
|
63
doc/development/database_helpers.md
Normal file
63
doc/development/database_helpers.md
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
# Database helpers
|
||||||
|
|
||||||
|
There are a number of useful helper modules defined in `/lib/gitlab/database/`.
|
||||||
|
|
||||||
|
## Subquery
|
||||||
|
|
||||||
|
In some cases it is not possible to perform an operation on a query.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Geo::EventLog.where('id < 100').limit(10).delete_all
|
||||||
|
```
|
||||||
|
|
||||||
|
Will give this error:
|
||||||
|
|
||||||
|
> ActiveRecord::ActiveRecordError: delete_all doesn't support limit
|
||||||
|
|
||||||
|
One solution would be to wrap it in another `where`:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Geo::EventLog.where(id: Geo::EventLog.where('id < 100').limit(10)).delete_all
|
||||||
|
```
|
||||||
|
|
||||||
|
This works with PostgreSQL, but with MySQL it gives this error:
|
||||||
|
|
||||||
|
> ActiveRecord::StatementInvalid: Mysql2::Error: This version of MySQL
|
||||||
|
> doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
|
||||||
|
|
||||||
|
Also, that query doesn't have very good performance. Using a
|
||||||
|
`INNER JOIN` with itself is better.
|
||||||
|
|
||||||
|
So instead of this query:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT geo_event_log.*
|
||||||
|
FROM geo_event_log
|
||||||
|
WHERE geo_event_log.id IN
|
||||||
|
(SELECT geo_event_log.id
|
||||||
|
FROM geo_event_log
|
||||||
|
WHERE (id < 100)
|
||||||
|
LIMIT 10)
|
||||||
|
```
|
||||||
|
|
||||||
|
It's better to write:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT geo_event_log.*
|
||||||
|
FROM geo_event_log
|
||||||
|
INNER JOIN
|
||||||
|
(SELECT geo_event_log.*
|
||||||
|
FROM geo_event_log
|
||||||
|
WHERE (id < 100)
|
||||||
|
LIMIT 10) t2 ON geo_event_log.id = t2.id
|
||||||
|
```
|
||||||
|
|
||||||
|
And this is where `Gitlab::Database::Subquery.self_join` can help
|
||||||
|
you. So you can rewrite the above statement as:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Gitlab::Database::Subquery.self_join(Geo::EventLog.where('id < 100').limit(10)).delete_all
|
||||||
|
```
|
||||||
|
|
||||||
|
And this also works with MySQL, so you don't need to worry about that.
|
16
lib/gitlab/database/subquery.rb
Normal file
16
lib/gitlab/database/subquery.rb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Gitlab
|
||||||
|
module Database
|
||||||
|
module Subquery
|
||||||
|
class << self
|
||||||
|
def self_join(relation)
|
||||||
|
t = relation.arel_table
|
||||||
|
t2 = relation.arel.as('t2')
|
||||||
|
|
||||||
|
relation.unscoped.joins(t.join(t2).on(t[:id].eq(t2[:id])).join_sources.first)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
17
spec/lib/gitlab/database/subquery_spec.rb
Normal file
17
spec/lib/gitlab/database/subquery_spec.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe Gitlab::Database::Subquery do
|
||||||
|
describe '.self_join' do
|
||||||
|
set(:project) { create(:project) }
|
||||||
|
|
||||||
|
it 'allows you to delete_all rows with WHERE and LIMIT' do
|
||||||
|
events = create_list(:event, 8, project: project)
|
||||||
|
|
||||||
|
expect do
|
||||||
|
described_class.self_join(Event.where('id < ?', events[5]).recent.limit(2)).delete_all
|
||||||
|
end.to change { Event.count }.by(-2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue