Skip to content

Commit 323304f

Browse files
committed
db: Add default_versions.num_versions column
1 parent 223ccc1 commit 323304f

File tree

8 files changed

+56
-11
lines changed

8 files changed

+56
-11
lines changed

crates/crates_io_database/src/schema.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,8 @@ diesel::table! {
408408
crate_id -> Int4,
409409
/// Reference to the version in the `versions` table.
410410
version_id -> Int4,
411+
/// The total number of versions.
412+
num_versions -> Int4,
411413
}
412414
}
413415

crates/crates_io_database_dump/src/dump-db.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ dependencies = ["crates", "versions"]
105105
[default_versions.columns]
106106
crate_id = "public"
107107
version_id = "public"
108+
num_versions = "public"
108109

109110
[deleted_crates]
110111
dependencies = ["users"]

crates/crates_io_database_dump/src/snapshots/[email protected]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ BEGIN ISOLATION LEVEL REPEATABLE READ, READ ONLY;
1818
\copy (SELECT "crate_id", "created_at", "created_by", "owner_id", "owner_kind" FROM "crate_owners" WHERE NOT deleted) TO 'data/crate_owners.csv' WITH CSV HEADER
1919

2020
\copy "versions" ("bin_names", "categories", "checksum", "crate_id", "crate_size", "created_at", "description", "documentation", "downloads", "edition", "features", "has_lib", "homepage", "id", "keywords", "license", "links", "num", "num_no_build", "published_by", "repository", "rust_version", "updated_at", "yanked") TO 'data/versions.csv' WITH CSV HEADER
21-
\copy "default_versions" ("crate_id", "version_id") TO 'data/default_versions.csv' WITH CSV HEADER
21+
\copy "default_versions" ("crate_id", "num_versions", "version_id") TO 'data/default_versions.csv' WITH CSV HEADER
2222
\copy "dependencies" ("crate_id", "default_features", "explicit_name", "features", "id", "kind", "optional", "req", "target", "version_id") TO 'data/dependencies.csv' WITH CSV HEADER
2323
\copy "version_downloads" ("date", "downloads", "version_id") TO 'data/version_downloads.csv' WITH CSV HEADER
2424
COMMIT;

crates/crates_io_database_dump/src/snapshots/[email protected]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ BEGIN;
6060
\copy "crates_keywords" ("crate_id", "keyword_id") FROM 'data/crates_keywords.csv' WITH CSV HEADER
6161
\copy "crate_owners" ("crate_id", "created_at", "created_by", "owner_id", "owner_kind") FROM 'data/crate_owners.csv' WITH CSV HEADER
6262
\copy "versions" ("bin_names", "categories", "checksum", "crate_id", "crate_size", "created_at", "description", "documentation", "downloads", "edition", "features", "has_lib", "homepage", "id", "keywords", "license", "links", "num", "num_no_build", "published_by", "repository", "rust_version", "updated_at", "yanked") FROM 'data/versions.csv' WITH CSV HEADER
63-
\copy "default_versions" ("crate_id", "version_id") FROM 'data/default_versions.csv' WITH CSV HEADER
63+
\copy "default_versions" ("crate_id", "num_versions", "version_id") FROM 'data/default_versions.csv' WITH CSV HEADER
6464
\copy "dependencies" ("crate_id", "default_features", "explicit_name", "features", "id", "kind", "optional", "req", "target", "version_id") FROM 'data/dependencies.csv' WITH CSV HEADER
6565
\copy "version_downloads" ("date", "downloads", "version_id") FROM 'data/version_downloads.csv' WITH CSV HEADER
6666

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
ALTER TABLE default_versions
2+
DROP COLUMN num_versions;
3+
4+
DROP FUNCTION IF EXISTS update_num_versions_from_versions CASCADE;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
ALTER TABLE default_versions
2+
ADD COLUMN num_versions INTEGER;
3+
4+
COMMENT ON COLUMN default_versions.num_versions IS 'The total number of versions.';
5+
6+
CREATE OR REPLACE FUNCTION update_num_versions_from_versions() RETURNS TRIGGER AS $$
7+
BEGIN
8+
IF (TG_OP = 'INSERT') THEN
9+
INSERT INTO default_versions (crate_id, version_id, num_versions)
10+
VALUES (NEW.crate_id, NEW.id, 1)
11+
ON CONFLICT (crate_id) DO UPDATE
12+
SET num_versions = EXCLUDED.num_versions + 1;
13+
RETURN NEW;
14+
ELSIF (TG_OP = 'DELETE') THEN
15+
UPDATE default_versions
16+
SET num_versions = num_versions - 1
17+
WHERE crate_id = OLD.crate_id;
18+
RETURN OLD;
19+
END IF;
20+
END
21+
$$ LANGUAGE plpgsql;
22+
23+
DROP TRIGGER IF EXISTS trigger_update_num_versions_from_versions ON versions;
24+
CREATE TRIGGER trigger_update_num_versions_from_versions
25+
AFTER INSERT OR DELETE ON versions
26+
FOR EACH ROW
27+
EXECUTE PROCEDURE update_num_versions_from_versions();

src/controllers/krate/publish.rs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ pub async fn publish(app: AppState, req: Parts, body: Body) -> AppResult<Json<Go
445445
// Upsert the `default_value` determined by the existing `default_value` and the
446446
// published version. Note that this could potentially write an outdated version
447447
// (although this should not happen regularly), as we might be comparing to an
448-
// outdated value.
448+
// outdated value. The initial record will be handled by the trigger function.
449449
//
450450
// Compared to only using a background job, this prevents us from getting into a
451451
// situation where a crate exists in the `crates` table but doesn't have a default
@@ -470,14 +470,6 @@ pub async fn publish(app: AppState, req: Parts, body: Body) -> AppResult<Json<Go
470470
// Update the default version asynchronously in a background job
471471
// to ensure correctness and eventual consistency.
472472
UpdateDefaultVersion::new(krate.id).enqueue(conn).await?;
473-
} else {
474-
diesel::insert_into(default_versions::table)
475-
.values((
476-
default_versions::crate_id.eq(krate.id),
477-
default_versions::version_id.eq(version.id),
478-
))
479-
.execute(conn)
480-
.await?;
481473
}
482474

483475
// Update all keywords for this crate

src/tests/krate/publish/basics.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,23 @@ async fn new_krate_twice() {
116116
// The primary purpose is to verify that the `default_version` we provide is as expected.
117117
#[tokio::test(flavor = "multi_thread")]
118118
async fn new_krate_twice_alt() {
119+
use crate::schema::default_versions;
120+
119121
let (app, _, _, token) = TestApp::full().with_token().await;
122+
let mut conn = app.db_conn().await;
120123

121124
let crate_to_publish =
122125
PublishBuilder::new("foo_twice", "2.0.0").description("2.0.0 description");
123126
token.publish_crate(crate_to_publish).await.good();
124127

128+
let num_versions = default_versions::table
129+
.select(default_versions::num_versions)
130+
.load::<i32>(&mut conn)
131+
.await
132+
.unwrap();
133+
assert_eq!(num_versions.len(), 1);
134+
assert_eq!(num_versions[0], 1);
135+
125136
let crate_to_publish = PublishBuilder::new("foo_twice", "0.99.0");
126137
let response = token.publish_crate(crate_to_publish).await;
127138
assert_eq!(response.status(), StatusCode::OK);
@@ -130,6 +141,14 @@ async fn new_krate_twice_alt() {
130141
".crate.updated_at" => "[datetime]",
131142
});
132143

144+
let num_versions = default_versions::table
145+
.select(default_versions::num_versions)
146+
.load::<i32>(&mut conn)
147+
.await
148+
.unwrap();
149+
assert_eq!(num_versions.len(), 1);
150+
assert_eq!(num_versions[0], 2);
151+
133152
let crates = app.crates_from_index_head("foo_twice");
134153
assert_json_snapshot!(crates);
135154

0 commit comments

Comments
 (0)