Skip to content

Commit a35bfa9

Browse files
authored
Merge pull request #8232 from Turbo87/crate-downloads-table
Extract `crate_downloads` table
2 parents a5df2a7 + fdf1407 commit a35bfa9

File tree

7 files changed

+103
-10
lines changed

7 files changed

+103
-10
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
drop trigger insert_crate_downloads_row on crates;
2+
drop function insert_crate_downloads_row;
3+
drop table crate_downloads;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
-- Create the `crate_downloads` table.
2+
3+
create table crate_downloads
4+
(
5+
crate_id integer not null
6+
constraint crate_downloads_pk
7+
primary key
8+
constraint crate_downloads_crates_id_fk
9+
references crates
10+
on delete cascade,
11+
downloads bigint default 0 not null
12+
);
13+
14+
comment on table crate_downloads is 'Number of downloads per crate. This was extracted from the `crates` table for performance reasons.';
15+
comment on column crate_downloads.crate_id is 'Reference to the crate that this row belongs to.';
16+
comment on column crate_downloads.downloads is 'The total number of downloads for this crate.';
17+
18+
-- Create a trigger to automatically add a row to `crate_downloads` when a new
19+
-- crate is inserted into the `crates` table.
20+
21+
create or replace function insert_crate_downloads_row() returns trigger as $$
22+
begin
23+
insert into crate_downloads(crate_id) values (new.id);
24+
return new;
25+
end;
26+
$$ language plpgsql;
27+
28+
create trigger insert_crate_downloads_row
29+
after insert on crates
30+
for each row
31+
execute function insert_crate_downloads_row();
32+
33+
-- The following query can take a couple of seconds so it should be run manually
34+
-- outside of the migration to prevent the server from taking a long time to
35+
-- start up while waiting for the migration to complete.
36+
37+
-- insert into crate_downloads (crate_id, downloads)
38+
-- select id, downloads
39+
-- from crates;

src/schema.patch

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
--- src/schema.rs.orig 2024-03-04 10:34:35
2+
+++ src/schema.rs 2024-03-04 10:33:35
13
@@ -21,9 +21,7 @@
24
/// The `pg_catalog.tsvector` SQL type
35
///
@@ -7,7 +9,7 @@
79
- pub struct Tsvector;
810
+ pub use diesel_full_text_search::Tsvector;
911
}
10-
12+
1113
diesel::table! {
1214
@@ -74,9 +72,9 @@
1315
/// (Automatically generated by Diesel.)
@@ -33,8 +35,8 @@
3335
- path -> Ltree,
3436
}
3537
}
36-
37-
@@ -456,7 +448,7 @@
38+
39+
@@ -476,7 +468,7 @@
3840
/// Its SQL type is `Array<Nullable<Text>>`.
3941
///
4042
/// (Automatically generated by Diesel.)
@@ -43,9 +45,9 @@
4345
/// The `target` column of the `dependencies` table.
4446
///
4547
/// Its SQL type is `Nullable<Varchar>`.
46-
@@ -683,6 +675,24 @@
48+
@@ -703,6 +695,24 @@
4749
}
48-
50+
4951
diesel::table! {
5052
+ /// Representation of the `recent_crate_downloads` view.
5153
+ ///
@@ -68,8 +70,8 @@
6870
/// Representation of the `reserved_crate_names` table.
6971
///
7072
/// (Automatically generated by Diesel.)
71-
@@ -997,7 +1007,8 @@
72-
diesel::joinable!(api_tokens -> users (user_id));
73+
@@ -1018,7 +1028,8 @@
74+
diesel::joinable!(crate_downloads -> crates (crate_id));
7375
diesel::joinable!(crate_owner_invitations -> crates (crate_id));
7476
diesel::joinable!(crate_owners -> crates (crate_id));
7577
-diesel::joinable!(crate_owners -> users (created_by));
@@ -78,15 +80,15 @@
7880
diesel::joinable!(crates_categories -> categories (category_id));
7981
diesel::joinable!(crates_categories -> crates (crate_id));
8082
diesel::joinable!(crates_keywords -> crates (crate_id));
81-
@@ -1010,6 +1021,7 @@
83+
@@ -1031,6 +1042,7 @@
8284
diesel::joinable!(publish_limit_buckets -> users (user_id));
8385
diesel::joinable!(publish_rate_overrides -> users (user_id));
8486
diesel::joinable!(readme_renderings -> versions (version_id));
8587
+diesel::joinable!(recent_crate_downloads -> crates (crate_id));
8688
diesel::joinable!(version_downloads -> versions (version_id));
8789
diesel::joinable!(version_owner_actions -> api_tokens (api_token_id));
8890
diesel::joinable!(version_owner_actions -> users (user_id));
89-
@@ -1036,6 +1048,7 @@
91+
@@ -1058,6 +1070,7 @@
9092
publish_limit_buckets,
9193
publish_rate_overrides,
9294
readme_renderings,

src/schema.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,16 @@ diesel::table! {
181181
}
182182
}
183183

184+
diesel::table! {
185+
/// Number of downloads per crate. This was extracted from the `crates` table for performance reasons.
186+
crate_downloads (crate_id) {
187+
/// Reference to the crate that this row belongs to.
188+
crate_id -> Int4,
189+
/// The total number of downloads for this crate.
190+
downloads -> Int8,
191+
}
192+
}
193+
184194
diesel::table! {
185195
/// Representation of the `crate_owner_invitations` table.
186196
///
@@ -1005,6 +1015,7 @@ diesel::table! {
10051015
}
10061016

10071017
diesel::joinable!(api_tokens -> users (user_id));
1018+
diesel::joinable!(crate_downloads -> crates (crate_id));
10081019
diesel::joinable!(crate_owner_invitations -> crates (crate_id));
10091020
diesel::joinable!(crate_owners -> crates (crate_id));
10101021
diesel::joinable!(crate_owners -> teams (owner_id));
@@ -1034,6 +1045,7 @@ diesel::allow_tables_to_appear_in_same_query!(
10341045
api_tokens,
10351046
background_jobs,
10361047
categories,
1048+
crate_downloads,
10371049
crate_owner_invitations,
10381050
crate_owners,
10391051
crates,

src/tests/builders/krate.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crates_io::{
55
};
66

77
use chrono::NaiveDateTime;
8+
use crates_io::schema::crate_downloads;
89
use diesel::prelude::*;
910

1011
use super::VersionBuilder;
@@ -125,6 +126,10 @@ impl<'a> CrateBuilder<'a> {
125126
// crate properties in a single DB call.
126127

127128
if let Some(downloads) = self.downloads {
129+
update(crate_downloads::table.filter(crate_downloads::crate_id.eq(krate.id)))
130+
.set(crate_downloads::downloads.eq(downloads as i64))
131+
.execute(connection)?;
132+
128133
krate = update(&krate)
129134
.set(crates::downloads.eq(downloads))
130135
.returning(Crate::as_returning())

src/worker/jobs/downloads/update_metadata.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ fn batch_update(batch_size: i64, conn: &mut PgConnection) -> QueryResult<i64> {
126126
SET downloads = crates.downloads + crate_downloads_batch.downloads
127127
FROM crate_downloads_batch
128128
WHERE crates.id = crate_downloads_batch.crate_id
129+
), updated_crate_downloads AS (
130+
-- Update the `downloads` count for each crate in the `crate_downloads` table.
131+
UPDATE crate_downloads
132+
SET downloads = crate_downloads.downloads + crate_downloads_batch.downloads
133+
FROM crate_downloads_batch
134+
WHERE crate_downloads.crate_id = crate_downloads_batch.crate_id
129135
), updated_metadata AS (
130136
-- Update the `total_downloads` count in the `metadata` table.
131137
UPDATE metadata
@@ -170,7 +176,7 @@ mod tests {
170176
use super::*;
171177
use crate::email::Emails;
172178
use crate::models::{Crate, NewCrate, NewUser, NewVersion, User, Version};
173-
use crate::schema::{crates, versions};
179+
use crate::schema::{crate_downloads, crates, versions};
174180
use crate::test_util::test_db_connection;
175181
use std::collections::BTreeMap;
176182

@@ -224,17 +230,27 @@ mod tests {
224230
.unwrap();
225231

226232
super::update(conn).unwrap();
233+
227234
let version_downloads = versions::table
228235
.find(version.id)
229236
.select(versions::downloads)
230237
.first(conn);
231238
assert_eq!(version_downloads, Ok(1));
239+
232240
let crate_downloads = crates::table
233241
.find(krate.id)
234242
.select(crates::downloads)
235243
.first(conn);
236244
assert_eq!(crate_downloads, Ok(1));
245+
246+
let crate_downloads = crate_downloads::table
247+
.find(krate.id)
248+
.select(crate_downloads::downloads)
249+
.first(conn);
250+
assert_eq!(crate_downloads, Ok(1));
251+
237252
super::update(conn).unwrap();
253+
238254
let version_downloads = versions::table
239255
.find(version.id)
240256
.select(versions::downloads)
@@ -330,17 +346,29 @@ mod tests {
330346
.filter(crates::id.eq(krate.id))
331347
.first(conn)
332348
.unwrap();
349+
333350
super::update(conn).unwrap();
351+
334352
let version2: Version = versions::table.find(version.id).first(conn).unwrap();
335353
assert_eq!(version2.downloads, 2);
336354
assert_eq!(version2.updated_at, version_before.updated_at);
355+
337356
let krate2: Crate = Crate::all()
338357
.filter(crates::id.eq(krate.id))
339358
.first(conn)
340359
.unwrap();
341360
assert_eq!(krate2.downloads, 2);
342361
assert_eq!(krate2.updated_at, krate_before.updated_at);
362+
363+
let krate2_downloads: i64 = crate_downloads::table
364+
.find(krate.id)
365+
.select(crate_downloads::downloads)
366+
.first(conn)
367+
.unwrap();
368+
assert_eq!(krate2_downloads, 2);
369+
343370
super::update(conn).unwrap();
371+
344372
let version3: Version = versions::table.find(version.id).first(conn).unwrap();
345373
assert_eq!(version3.downloads, 2);
346374
}

src/worker/jobs/dump_db/dump-db.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ crates_cnt = "public"
4848
created_at = "public"
4949
path = "public"
5050

51+
[crate_downloads.columns]
52+
crate_id = "public"
53+
downloads = "public"
54+
5155
[crate_owner_invitations.columns]
5256
invited_user_id = "private"
5357
invited_by_user_id = "private"

0 commit comments

Comments
 (0)