1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//! Registry-schema pre-resolution for the sync schema-fallback tier (issue #1692).
//!
//! The sync `get_table_schema` must never `block_on` the async schema registry on
//! a tokio worker thread. Instead the registry schema is resolved ONCE — properly
//! awaited — at an async wiring point, and cached in `self.registry_schema` for
//! the sync path to read.
use std::sync::Arc;
use super::parsing::extract_keyspace_table_from_path;
use super::types::SSTableReader;
impl SSTableReader {
/// Pre-resolve the registry schema into the sync cache (issue #1692, AG3).
///
/// Call this from an async wiring point (e.g. `open_reader_with_schema`, or a
/// DIRECT caller's own async context), AFTER
/// [`set_schema_registry`](SSTableReader::set_schema_registry) — or use the
/// combined [`attach_schema_registry`](SSTableReader::attach_schema_registry).
/// It resolves the table schema from the async registry a single time —
/// properly awaited — and caches it in `self.registry_schema`, so the SYNC
/// schema-fallback tier of `get_table_schema` reads a plain field instead of
/// `block_on`-ing the async registry lock on a tokio worker thread.
///
/// Cold path only: when the SSTable header already carried a schema
/// (`self.schema.is_some()`) the sync path short-circuits before the registry
/// tier, so no resolution is performed. A registry miss leaves the cache
/// `None`; the sync path then falls through to header-column construction,
/// exactly as the previous `block_on` path did on a miss.
pub async fn resolve_registry_schema(&mut self) {
// Header schema present -> the registry tier is never reached; skip.
if self.schema.is_some() {
return;
}
let Some(registry_rwlock) = self.schema_registry.clone() else {
return;
};
// Keyspace/table from the SSTable path (authoritative), with the header
// names as the documented fallback — same resolution the old block_on
// path used.
let file_path = self.file_path();
let (keyspace, table_name) = match extract_keyspace_table_from_path(&file_path) {
Ok(names) => names,
Err(e) => {
tracing::debug!(
"resolve_registry_schema: path parse failed for {}: {}. Using header names.",
file_path.display(),
e
);
(self.header.keyspace.clone(), self.header.table_name.clone())
}
};
// Invalidate any previously-cached schema BEFORE the lookup so a miss
// ALWAYS leaves the cache `None` (never serves a stale schema after the
// table is removed/unresolvable). The hit path re-populates it below.
self.registry_schema = None;
let registry = registry_rwlock.read().await;
match registry.get_schema(&keyspace, &table_name).await {
Ok(schema) => {
tracing::debug!(
"resolve_registry_schema: cached registry schema for {}.{}",
keyspace,
table_name
);
self.registry_schema = Some(Arc::new(schema));
}
Err(e) => {
tracing::debug!(
"resolve_registry_schema: no registry schema for {}.{}: {}",
keyspace,
table_name,
e
);
// Cache already cleared above; a miss leaves it `None`.
}
}
}
}