@@ -2,13 +2,23 @@ use crate::schema::crates;
2
2
use crate :: storage:: FeedId ;
3
3
use crate :: worker:: Environment ;
4
4
use anyhow:: anyhow;
5
+ use chrono:: Duration ;
5
6
use crates_io_worker:: BackgroundJob ;
6
7
use diesel:: prelude:: * ;
7
8
use std:: sync:: Arc ;
8
9
9
10
#[ derive( Serialize , Deserialize ) ]
10
11
pub struct SyncCratesFeed ;
11
12
13
+ /// Items younger than this will always be included in the feed.
14
+ const ALWAYS_INCLUDE_AGE : Duration = Duration :: minutes ( 60 ) ;
15
+
16
+ /// The number of items to include in the feed.
17
+ ///
18
+ /// If there are less than this number of items in the database, the feed will
19
+ /// contain fewer items. If there are more items in the database that are
20
+ /// younger than [`ALWAYS_INCLUDE_AGE`], all of them will be included in
21
+ /// the feed.
12
22
const NUM_ITEMS : i64 = 50 ;
13
23
14
24
impl BackgroundJob for SyncCratesFeed {
@@ -70,7 +80,26 @@ impl BackgroundJob for SyncCratesFeed {
70
80
}
71
81
}
72
82
83
+ /// Load the latest crates from the database.
84
+ ///
85
+ /// This function will load all crates from the database that are younger
86
+ /// than [`ALWAYS_INCLUDE_AGE`]. If there are less than [`NUM_ITEMS`] crates
87
+ /// then the list will be padded with older crates until [`NUM_ITEMS`] are
88
+ /// returned.
73
89
fn load_new_crates ( conn : & mut PgConnection ) -> QueryResult < Vec < NewCrate > > {
90
+ let threshold_dt = chrono:: Utc :: now ( ) . naive_utc ( ) - ALWAYS_INCLUDE_AGE ;
91
+
92
+ let new_crates = crates:: table
93
+ . filter ( crates:: created_at. gt ( threshold_dt) )
94
+ . order ( crates:: created_at. desc ( ) )
95
+ . select ( NewCrate :: as_select ( ) )
96
+ . load ( conn) ?;
97
+
98
+ let num_new_crates = new_crates. len ( ) ;
99
+ if num_new_crates as i64 >= NUM_ITEMS {
100
+ return Ok ( new_crates) ;
101
+ }
102
+
74
103
crates:: table
75
104
. order ( crates:: created_at. desc ( ) )
76
105
. select ( NewCrate :: as_select ( ) )
@@ -126,3 +155,68 @@ impl NewCrate {
126
155
}
127
156
}
128
157
}
158
+
159
+ #[ cfg( test) ]
160
+ mod tests {
161
+ use super :: * ;
162
+ use chrono:: NaiveDateTime ;
163
+ use crates_io_test_db:: TestDatabase ;
164
+ use insta:: assert_debug_snapshot;
165
+
166
+ #[ test]
167
+ fn test_load_version_updates ( ) {
168
+ crate :: util:: tracing:: init_for_test ( ) ;
169
+
170
+ let db = TestDatabase :: new ( ) ;
171
+ let mut conn = db. connect ( ) ;
172
+
173
+ let now = chrono:: Utc :: now ( ) . naive_utc ( ) ;
174
+
175
+ let new_crates = assert_ok ! ( load_new_crates( & mut conn) ) ;
176
+ assert_eq ! ( new_crates. len( ) , 0 ) ;
177
+
178
+ // If there are less than NUM_ITEMS crates, they should all be returned
179
+ create_crate ( & mut conn, "foo" , now - Duration :: days ( 123 ) ) ;
180
+ create_crate ( & mut conn, "bar" , now - Duration :: days ( 110 ) ) ;
181
+ create_crate ( & mut conn, "baz" , now - Duration :: days ( 100 ) ) ;
182
+ create_crate ( & mut conn, "qux" , now - Duration :: days ( 90 ) ) ;
183
+
184
+ let new_crates = assert_ok ! ( load_new_crates( & mut conn) ) ;
185
+ assert_eq ! ( new_crates. len( ) , 4 ) ;
186
+ assert_debug_snapshot ! ( new_crates. iter( ) . map( |u| & u. name) . collect:: <Vec <_>>( ) ) ;
187
+
188
+ // If there are more than NUM_ITEMS crates, only the most recent NUM_ITEMS should be returned
189
+ for i in 1 ..=NUM_ITEMS {
190
+ let name = format ! ( "crate-{i}" ) ;
191
+ let publish_time = now - Duration :: days ( 90 ) + Duration :: hours ( i) ;
192
+ create_crate ( & mut conn, & name, publish_time) ;
193
+ }
194
+
195
+ let new_crates = assert_ok ! ( load_new_crates( & mut conn) ) ;
196
+ assert_eq ! ( new_crates. len( ) as i64 , NUM_ITEMS ) ;
197
+ assert_debug_snapshot ! ( new_crates. iter( ) . map( |u| & u. name) . collect:: <Vec <_>>( ) ) ;
198
+
199
+ // But if there are more than NUM_ITEMS crates that are younger than ALWAYS_INCLUDE_AGE, all of them should be returned
200
+ for i in 1 ..=( NUM_ITEMS + 10 ) {
201
+ let name = format ! ( "other-crate-{i}" ) ;
202
+ let publish_time = now - Duration :: minutes ( 30 ) + Duration :: seconds ( i) ;
203
+ create_crate ( & mut conn, & name, publish_time) ;
204
+ }
205
+
206
+ let new_crates = assert_ok ! ( load_new_crates( & mut conn) ) ;
207
+ assert_eq ! ( new_crates. len( ) as i64 , NUM_ITEMS + 10 ) ;
208
+ assert_debug_snapshot ! ( new_crates. iter( ) . map( |u| & u. name) . collect:: <Vec <_>>( ) ) ;
209
+ }
210
+
211
+ fn create_crate ( conn : & mut PgConnection , name : & str , publish_time : NaiveDateTime ) -> i32 {
212
+ diesel:: insert_into ( crates:: table)
213
+ . values ( (
214
+ crates:: name. eq ( name) ,
215
+ crates:: created_at. eq ( publish_time) ,
216
+ crates:: updated_at. eq ( publish_time) ,
217
+ ) )
218
+ . returning ( crates:: id)
219
+ . get_result ( conn)
220
+ . unwrap ( )
221
+ }
222
+ }
0 commit comments