Track scheduler affinity epochs and key sorting

This commit is contained in:
fawney19
2026-05-11 01:45:49 +08:00
parent 7b81c77424
commit e3574e1918
38 changed files with 656 additions and 23 deletions

View File

@@ -516,6 +516,10 @@ pub enum ProviderCatalogKeyListOrder {
#[default]
Name,
CreatedAt,
CreatedAtAsc,
CreatedAtDesc,
LastUsedAtAsc,
LastUsedAtDesc,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]

View File

@@ -2657,6 +2657,22 @@ CREATE INDEX IF NOT EXISTS idx_provider_api_keys_provider_active ON public.provi
--
-- Name: idx_provider_api_keys_provider_created_at_desc; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX IF NOT EXISTS idx_provider_api_keys_provider_created_at_desc ON public.provider_api_keys USING btree (provider_id, created_at DESC NULLS LAST, name, id);
--
-- Name: idx_provider_api_keys_provider_last_used_at_desc; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX IF NOT EXISTS idx_provider_api_keys_provider_last_used_at_desc ON public.provider_api_keys USING btree (provider_id, last_used_at DESC NULLS LAST, name, id);
--
-- Name: idx_provider_api_keys_provider_id; Type: INDEX; Schema: public; Owner: -
--

View File

@@ -0,0 +1,5 @@
CREATE INDEX IF NOT EXISTS idx_provider_api_keys_provider_created_at_desc
ON public.provider_api_keys USING btree (provider_id, created_at DESC NULLS LAST, name, id);
CREATE INDEX IF NOT EXISTS idx_provider_api_keys_provider_last_used_at_desc
ON public.provider_api_keys USING btree (provider_id, last_used_at DESC NULLS LAST, name, id);

View File

@@ -133,6 +133,22 @@ CREATE INDEX IF NOT EXISTS idx_provider_api_keys_provider_active ON public.provi
--
-- Name: idx_provider_api_keys_provider_created_at_desc; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX IF NOT EXISTS idx_provider_api_keys_provider_created_at_desc ON public.provider_api_keys USING btree (provider_id, created_at DESC NULLS LAST, name, id);
--
-- Name: idx_provider_api_keys_provider_last_used_at_desc; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX IF NOT EXISTS idx_provider_api_keys_provider_last_used_at_desc ON public.provider_api_keys USING btree (provider_id, last_used_at DESC NULLS LAST, name, id);
--
-- Name: idx_provider_api_keys_provider_id; Type: INDEX; Schema: public; Owner: -
--

View File

@@ -125,6 +125,22 @@ CREATE INDEX IF NOT EXISTS idx_provider_api_keys_provider_active ON public.provi
--
-- Name: idx_provider_api_keys_provider_created_at_desc; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX IF NOT EXISTS idx_provider_api_keys_provider_created_at_desc ON public.provider_api_keys USING btree (provider_id, created_at DESC NULLS LAST, name, id);
--
-- Name: idx_provider_api_keys_provider_last_used_at_desc; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX IF NOT EXISTS idx_provider_api_keys_provider_last_used_at_desc ON public.provider_api_keys USING btree (provider_id, last_used_at DESC NULLS LAST, name, id);
--
-- Name: idx_provider_api_keys_provider_id; Type: INDEX; Schema: public; Owner: -
--

View File

@@ -7,7 +7,7 @@ use tracing::info;
// Generated by build.rs from schema/bootstrap/postgres.
pub(crate) static EMPTY_DATABASE_SNAPSHOT_SQL: &str =
include_str!(concat!(env!("OUT_DIR"), "/empty_database_snapshot.sql"));
pub(crate) const EMPTY_DATABASE_SNAPSHOT_CUTOFF_VERSION: i64 = 20260510120000;
pub(crate) const EMPTY_DATABASE_SNAPSHOT_CUTOFF_VERSION: i64 = 20260511000000;
const PUBLIC_BASE_TABLE_COUNT_SQL: &str = r#"
SELECT COUNT(*)::BIGINT

View File

@@ -298,6 +298,7 @@ fn empty_database_snapshot_covers_current_cutoff_versions() {
20260509120000,
20260510000000,
20260510120000,
20260511000000,
]
);
}
@@ -1082,6 +1083,7 @@ fn pending_migrations_from_applied_skips_versions_already_applied() {
20260509120000,
20260510000000,
20260510120000,
20260511000000,
]
);
}

View File

@@ -1,3 +1,4 @@
use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::sync::RwLock;
@@ -454,6 +455,20 @@ impl ProviderCatalogReadRepository for InMemoryProviderCatalogReadRepository {
})
.cloned()
.collect::<Vec<_>>();
fn compare_optional_u64_null_last(
left: Option<u64>,
right: Option<u64>,
descending: bool,
) -> Ordering {
match (left, right) {
(Some(left), Some(right)) if descending => right.cmp(&left),
(Some(left), Some(right)) => left.cmp(&right),
(Some(_), None) => Ordering::Less,
(None, Some(_)) => Ordering::Greater,
(None, None) => Ordering::Equal,
}
}
match query.order {
ProviderCatalogKeyListOrder::Name => {
keys.sort_by(|left, right| {
@@ -475,6 +490,50 @@ impl ProviderCatalogReadRepository for InMemoryProviderCatalogReadRepository {
.then(left.id.cmp(&right.id))
});
}
ProviderCatalogKeyListOrder::CreatedAtAsc => {
keys.sort_by(|left, right| {
compare_optional_u64_null_last(
left.created_at_unix_ms,
right.created_at_unix_ms,
false,
)
.then(left.name.cmp(&right.name))
.then(left.id.cmp(&right.id))
});
}
ProviderCatalogKeyListOrder::CreatedAtDesc => {
keys.sort_by(|left, right| {
compare_optional_u64_null_last(
left.created_at_unix_ms,
right.created_at_unix_ms,
true,
)
.then(left.name.cmp(&right.name))
.then(left.id.cmp(&right.id))
});
}
ProviderCatalogKeyListOrder::LastUsedAtAsc => {
keys.sort_by(|left, right| {
compare_optional_u64_null_last(
left.last_used_at_unix_secs,
right.last_used_at_unix_secs,
false,
)
.then(left.name.cmp(&right.name))
.then(left.id.cmp(&right.id))
});
}
ProviderCatalogKeyListOrder::LastUsedAtDesc => {
keys.sort_by(|left, right| {
compare_optional_u64_null_last(
left.last_used_at_unix_secs,
right.last_used_at_unix_secs,
true,
)
.then(left.name.cmp(&right.name))
.then(left.id.cmp(&right.id))
});
}
}
let total = keys.len();
let items = keys
@@ -1028,6 +1087,70 @@ mod tests {
);
}
#[tokio::test]
async fn paginates_provider_keys_by_pool_sort_fields() {
let mut old = sample_key("key-1", "provider-1");
old.name = "old".to_string();
old.created_at_unix_ms = Some(10);
old.last_used_at_unix_secs = Some(30);
let mut fresh = sample_key("key-2", "provider-1");
fresh.name = "fresh".to_string();
fresh.created_at_unix_ms = Some(20);
fresh.last_used_at_unix_secs = Some(10);
let mut unused = sample_key("key-3", "provider-1");
unused.name = "unused".to_string();
unused.created_at_unix_ms = None;
unused.last_used_at_unix_secs = None;
let repository = InMemoryProviderCatalogReadRepository::seed(
vec![sample_provider("provider-1")],
vec![],
vec![old, fresh, unused],
);
let imported = repository
.list_keys_page(&ProviderCatalogKeyListQuery {
provider_id: "provider-1".to_string(),
search: None,
is_active: None,
offset: 0,
limit: 2,
order: ProviderCatalogKeyListOrder::CreatedAtDesc,
})
.await
.expect("keys should page");
assert_eq!(imported.total, 3);
assert_eq!(
imported
.items
.iter()
.map(|item| item.id.as_str())
.collect::<Vec<_>>(),
vec!["key-2", "key-1"]
);
let last_used = repository
.list_keys_page(&ProviderCatalogKeyListQuery {
provider_id: "provider-1".to_string(),
search: None,
is_active: None,
offset: 0,
limit: 3,
order: ProviderCatalogKeyListOrder::LastUsedAtDesc,
})
.await
.expect("keys should page");
assert_eq!(
last_used
.items
.iter()
.map(|item| item.id.as_str())
.collect::<Vec<_>>(),
vec!["key-1", "key-2", "key-3"]
);
}
#[tokio::test]
async fn summarizes_provider_key_stats() {
let mut inactive = sample_key("key-2", "provider-1");

View File

@@ -571,6 +571,18 @@ ORDER BY provider_priority ASC, name ASC
ProviderCatalogKeyListOrder::CreatedAt => {
"internal_priority ASC, COALESCE(created_at, TO_TIMESTAMP(0)) ASC, id ASC"
}
ProviderCatalogKeyListOrder::CreatedAtAsc => {
"created_at ASC NULLS LAST, name ASC, id ASC"
}
ProviderCatalogKeyListOrder::CreatedAtDesc => {
"created_at DESC NULLS LAST, name ASC, id ASC"
}
ProviderCatalogKeyListOrder::LastUsedAtAsc => {
"last_used_at ASC NULLS LAST, name ASC, id ASC"
}
ProviderCatalogKeyListOrder::LastUsedAtDesc => {
"last_used_at DESC NULLS LAST, name ASC, id ASC"
}
};
let count_row = sqlx::query(