Skip to content

Commit bd41b5e

Browse files
committed
crates-admin: Add backfill tool for generating OG images for existing crates
1 parent e398e76 commit bd41b5e

File tree

2 files changed

+116
-0
lines changed

2 files changed

+116
-0
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use anyhow::Result;
2+
use crates_io::db;
3+
use crates_io::schema::crates;
4+
use crates_io::worker::jobs::GenerateOgImage;
5+
use crates_io_worker::BackgroundJob;
6+
use diesel::prelude::*;
7+
use diesel_async::RunQueryDsl;
8+
use tracing::{info, warn};
9+
10+
#[derive(clap::Parser, Debug)]
11+
#[command(
12+
name = "backfill-og-images",
13+
about = "Enqueue OG image generation jobs for existing crates"
14+
)]
15+
pub struct Opts {
16+
#[arg(long, default_value = "1000")]
17+
/// Batch size for enqueueing crates (default: 1000)
18+
batch_size: usize,
19+
20+
#[arg(long)]
21+
/// Only generate OG images for crates with names starting with this prefix
22+
prefix: Option<String>,
23+
24+
#[arg(long)]
25+
/// Offset to start enqueueing from (useful for resuming)
26+
offset: Option<i64>,
27+
}
28+
29+
pub async fn run(opts: Opts) -> Result<()> {
30+
let mut conn = db::oneoff_connection().await?;
31+
32+
info!("Starting OG image backfill with options: {opts:?}");
33+
34+
// Helper function to build query
35+
let build_query = |offset: i64| {
36+
let mut query = crates::table
37+
.select(crates::name)
38+
.order(crates::name)
39+
.into_boxed();
40+
41+
if let Some(prefix) = &opts.prefix {
42+
query = query.filter(crates::name.like(format!("{prefix}%")));
43+
}
44+
45+
query.offset(offset)
46+
};
47+
48+
// Count total crates to process
49+
let mut count_query = crates::table.into_boxed();
50+
if let Some(prefix) = &opts.prefix {
51+
count_query = count_query.filter(crates::name.like(format!("{prefix}%")));
52+
}
53+
let total_crates: i64 = count_query.count().get_result(&mut conn).await?;
54+
55+
info!("Total crates to enqueue: {total_crates}");
56+
57+
let mut offset = opts.offset.unwrap_or(0);
58+
let mut enqueued = 0;
59+
let mut errors = 0;
60+
61+
loop {
62+
// Fetch batch of crate names
63+
let crate_names: Vec<String> = build_query(offset)
64+
.limit(opts.batch_size as i64)
65+
.load(&mut conn)
66+
.await?;
67+
68+
if crate_names.is_empty() {
69+
break;
70+
}
71+
72+
let batch_size = crate_names.len();
73+
info!(
74+
"Enqueueing batch {}-{} of {total_crates}",
75+
offset + 1,
76+
offset + batch_size as i64
77+
);
78+
79+
for crate_name in crate_names {
80+
match GenerateOgImage::new(crate_name.clone())
81+
.enqueue(&mut conn)
82+
.await
83+
{
84+
Ok(_) => {
85+
enqueued += 1;
86+
if enqueued % 100 == 0 {
87+
info!("Enqueued {enqueued} jobs so far...");
88+
}
89+
}
90+
Err(e) => {
91+
warn!("Failed to enqueue OG image job for {crate_name}: {e}");
92+
errors += 1;
93+
}
94+
}
95+
}
96+
97+
// Break if we've processed fewer than batch_size (last batch)
98+
if batch_size < opts.batch_size {
99+
break;
100+
}
101+
102+
offset += opts.batch_size as i64;
103+
}
104+
105+
info!("Jobs enqueued: {enqueued}");
106+
info!("Errors: {errors}");
107+
108+
if errors > 0 {
109+
warn!("Some jobs failed to enqueue. Check logs above for details.");
110+
}
111+
112+
Ok(())
113+
}

src/bin/crates-admin/main.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#[macro_use]
22
extern crate tracing;
33

4+
mod backfill_og_images;
45
mod default_versions;
56
mod delete_crate;
67
mod delete_version;
@@ -17,6 +18,7 @@ mod yank_version;
1718
#[derive(clap::Parser, Debug)]
1819
#[command(name = "crates-admin")]
1920
enum Command {
21+
BackfillOgImages(backfill_og_images::Opts),
2022
DeleteCrate(delete_crate::Opts),
2123
DeleteVersion(delete_version::Opts),
2224
Populate(populate::Opts),
@@ -46,6 +48,7 @@ async fn main() -> anyhow::Result<()> {
4648
span.record("command", tracing::field::debug(&command));
4749

4850
match command {
51+
Command::BackfillOgImages(opts) => backfill_og_images::run(opts).await,
4952
Command::DeleteCrate(opts) => delete_crate::run(opts).await,
5053
Command::DeleteVersion(opts) => delete_version::run(opts).await,
5154
Command::Populate(opts) => populate::run(opts).await,

0 commit comments

Comments
 (0)