@@ -1241,6 +1241,40 @@ impl<'a> Connection<'a> {
1241
1241
} )
1242
1242
}
1243
1243
1244
+ /// Return the shard that has the fewest deployments out of the given
1245
+ /// `shards`. If `shards` is empty, return `None`
1246
+ ///
1247
+ /// Usage of a shard is taken to be the number of assigned deployments
1248
+ /// that are stored in it. Unassigned deployments are ignored; in
1249
+ /// particular, that ignores deployments that are going to be removed
1250
+ /// soon.
1251
+ pub fn least_used_shard ( & self , shards : & [ Shard ] ) -> Result < Option < Shard > , StoreError > {
1252
+ use deployment_schemas as ds;
1253
+ use subgraph_deployment_assignment as a;
1254
+
1255
+ let used = ds:: table
1256
+ . inner_join ( a:: table. on ( a:: id. eq ( ds:: id) ) )
1257
+ . filter ( ds:: shard. eq ( any ( shards) ) )
1258
+ . select ( ( ds:: shard, sql ( "count(*)" ) ) )
1259
+ . group_by ( ds:: shard)
1260
+ . order_by ( sql :: < i64 > ( "count(*)" ) )
1261
+ . load :: < ( String , i64 ) > ( self . conn . as_ref ( ) ) ?;
1262
+
1263
+ let missing = shards
1264
+ . into_iter ( )
1265
+ . filter ( |shard| !used. iter ( ) . any ( |( s, _) | s == shard. as_str ( ) ) )
1266
+ . map ( |shard| ( shard. as_str ( ) , 0 ) ) ;
1267
+
1268
+ used. iter ( )
1269
+ . map ( |( shard, count) | ( shard. as_str ( ) , * count) )
1270
+ . chain ( missing)
1271
+ . min_by ( |( _, a) , ( _, b) | a. cmp ( b) )
1272
+ . map ( |( shard, _) | Shard :: new ( shard. to_string ( ) ) )
1273
+ . transpose ( )
1274
+ // This can't really happen since we filtered by valid shards
1275
+ . map_err ( |e| constraint_violation ! ( "database has illegal shard name: {}" , e) )
1276
+ }
1277
+
1244
1278
#[ cfg( debug_assertions) ]
1245
1279
pub fn versions_for_subgraph (
1246
1280
& self ,
0 commit comments