Skip to content

Commit 3c7cb19

Browse files
committed
replace etag, version with hash
1 parent c414d87 commit 3c7cb19

10 files changed

Lines changed: 124 additions & 176 deletions

File tree

‎src/handlers/http/mod.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ pub async fn fetch_schema(
144144
.get_global_schema(stream_name, tenant_id)
145145
.await?
146146
{
147-
return Ok(serde_json::from_slice(&global.schema)?);
147+
return Ok(global.schema);
148148
}
149149
let res: Vec<Schema> = PARSEABLE
150150
.metastore

‎src/metastore/metastore_traits.rs‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ use bytes::Bytes;
2323
use chrono::{DateTime, Utc};
2424
use dashmap::DashMap;
2525
use erased_serde::Serialize as ErasedSerialize;
26-
use object_store::UpdateVersion;
2726
use tonic::async_trait;
2827
use ulid::Ulid;
2928

@@ -304,10 +303,10 @@ pub trait Metastore: std::fmt::Debug + Send + Sync {
304303
) -> Result<Option<GlobalSchema>, MetastoreError>;
305304
async fn put_global_schema(
306305
&self,
307-
obj: Schema,
306+
obj: GlobalSchema,
308307
stream_name: &str,
309308
tenant_id: &Option<String>,
310-
expected: Option<UpdateVersion>,
309+
expected: Option<u64>,
311310
) -> Result<bool, MetastoreError>;
312311

313312
/// parseable metadata

‎src/metastore/metastores/object_store_metastore.rs‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1184,7 +1184,8 @@ impl Metastore for ObjectStoreMetastore {
11841184
tenant_id: &Option<String>,
11851185
) -> Result<Bytes, MetastoreError> {
11861186
if let Some(global_schema) = self.get_global_schema(stream_name, tenant_id).await? {
1187-
return Ok(global_schema.schema);
1187+
let schema = to_bytes(&global_schema.schema);
1188+
return Ok(schema);
11881189
}
11891190

11901191
Ok(self
@@ -1214,24 +1215,23 @@ impl Metastore for ObjectStoreMetastore {
12141215
) -> Result<Option<GlobalSchema>, MetastoreError> {
12151216
// LocalFS does not participate in the shared registry. Only bypass
12161217
// explicitly unsupported backends; propagate errors from cloud stores.
1217-
if !self.storage.supports_conditional_writes() {
1218-
return Ok(None);
1219-
}
12201218
let path = global_schema_path(stream_name, tenant_id);
1219+
let global_schema =
1220+
serde_json::from_slice(&self.storage.get_object(&path, tenant_id).await?)?;
12211221
let Some((schema, version)) = self.storage.get_object_versioned(&path, tenant_id).await?
12221222
else {
12231223
return Ok(None);
12241224
};
1225-
Ok(Some(GlobalSchema::new(schema, version)))
1225+
Ok(Some(global_schema))
12261226
}
12271227

12281228
/// Atomically create a missing global schema or compare-and-swap an existing one.
12291229
async fn put_global_schema(
12301230
&self,
1231-
obj: Schema,
1231+
obj: GlobalSchema,
12321232
stream_name: &str,
12331233
tenant_id: &Option<String>,
1234-
expected: Option<UpdateVersion>,
1234+
expected: Option<u64>,
12351235
) -> Result<bool, MetastoreError> {
12361236
let path = global_schema_path(stream_name, tenant_id);
12371237
Ok(self

‎src/parseable/streams.rs‎

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ use arrow::{
2323
};
2424
use arrow_array::{ArrayRef, RecordBatch, UInt32Array};
2525
use arrow_schema::{ArrowError, Field, Fields, Schema, SortOptions};
26-
use bytes::Bytes;
2726
use chrono::{NaiveDate, NaiveDateTime, Timelike, Utc};
2827
use derive_more::derive::{Deref, DerefMut};
2928
use itertools::Itertools;
@@ -52,7 +51,8 @@ use rayon::{
5251
slice::ParallelSliceMut,
5352
};
5453
use relative_path::RelativePathBuf;
55-
use std::sync::PoisonError;
54+
use rustc_hash::FxHasher;
55+
use serde::{Deserialize, Serialize};
5656
use std::{
5757
collections::{HashMap, HashSet, VecDeque},
5858
fs::{self, File, OpenOptions, remove_file, write},
@@ -62,6 +62,10 @@ use std::{
6262
sync::{Arc, Mutex, RwLock},
6363
time::{Duration, Instant, SystemTime, UNIX_EPOCH},
6464
};
65+
use std::{
66+
hash::{Hash, Hasher},
67+
sync::PoisonError,
68+
};
6569
use tokio::task::JoinSet;
6670
use tracing::{error, info, info_span, instrument, trace, warn};
6771
use ulid::Ulid;
@@ -80,7 +84,7 @@ use crate::{
8084
option::Mode,
8185
parseable::{DEFAULT_TENANT, PARSEABLE},
8286
schema_registry::Resolution,
83-
storage::{StreamType, object_storage::to_bytes, retention::Retention},
87+
storage::{ComparableBytes, StreamType, object_storage::to_bytes, retention::Retention},
8488
sync::FLUSH_AND_CONVERT_RUNTIME,
8589
utils::{
8690
arrow::adapt_batch,
@@ -374,15 +378,24 @@ fn minute_from_system_time(time: SystemTime) -> u128 {
374378
/ 60000
375379
}
376380

377-
#[derive(Debug, Clone)]
381+
#[derive(Debug, Clone, Deserialize, Serialize)]
378382
pub struct GlobalSchema {
379-
pub schema: Bytes,
380-
pub version: object_store::UpdateVersion,
383+
pub schema: Schema,
384+
pub hash: u64,
381385
}
382386

383387
impl GlobalSchema {
384-
pub fn new(schema: Bytes, version: object_store::UpdateVersion) -> Self {
385-
Self { schema, version }
388+
pub fn from_schema(schema: Schema) -> Self {
389+
let mut hasher = FxHasher::with_seed(0);
390+
schema.hash(&mut hasher);
391+
let hash = hasher.finish();
392+
Self { schema, hash }
393+
}
394+
}
395+
396+
impl ComparableBytes for GlobalSchema {
397+
fn get_hash(&self) -> u64 {
398+
self.hash
386399
}
387400
}
388401

@@ -1179,7 +1192,7 @@ impl Stream {
11791192
{
11801193
// The registry is authoritative. Raw schemas staged before the
11811194
// first coordinated conversion may contain the losing field type.
1182-
schema = global.as_ref().clone();
1195+
schema = global.as_ref().to_owned();
11831196
} else {
11841197
let staging_schemas = self.get_schemas_if_present()?;
11851198
if !staging_schemas.is_empty() {
@@ -1876,12 +1889,11 @@ impl Stream {
18761889
}
18771890

18781891
fn can_use_global_schema(&self) -> bool {
1879-
self.options.mode == Mode::Ingest
1880-
&& !self.get_static_schema_flag()
1881-
&& PARSEABLE
1882-
.storage
1883-
.get_object_store()
1884-
.supports_conditional_writes()
1892+
self.options.mode == Mode::Ingest && !self.get_static_schema_flag()
1893+
// && PARSEABLE
1894+
// .storage
1895+
// .get_object_store()
1896+
// .supports_conditional_writes()
18851897
}
18861898

18871899
fn uses_global_schema(&self) -> bool {
@@ -1973,7 +1985,7 @@ impl Stream {
19731985
"global schema disappeared after resolution".to_owned(),
19741986
)
19751987
})?;
1976-
let canonical = serde_json::from_slice::<Schema>(&global.schema)?;
1988+
let canonical = global.schema;
19771989
Ok::<_, StagingError>((resolution, Arc::new(canonical)))
19781990
})?;
19791991
resolution.include_identity_schema(&canonical);
@@ -2146,6 +2158,7 @@ impl Stream {
21462158

21472159
/// Converts one Parquet output group and removes its Arrow sources only
21482160
/// after the final Parquet rename succeeds.
2161+
#[allow(clippy::too_many_arguments)]
21492162
fn convert_arrow_group(
21502163
&self,
21512164
parquet_path: PathBuf,
@@ -2225,6 +2238,7 @@ impl Stream {
22252238

22262239
#[cfg_attr(feature = "hotpath", hotpath::measure)]
22272240
/// Writes one conversion group to a temporary Parquet file.
2241+
#[allow(clippy::too_many_arguments)]
22282242
fn write_parquet_part_file(
22292243
&self,
22302244
part_path: &Path,
@@ -2287,6 +2301,7 @@ impl Stream {
22872301

22882302
/// Writes a metric conversion group with pipelined row-group preparation
22892303
/// and encoding while preserving row-group output order.
2304+
#[allow(clippy::too_many_arguments)]
22902305
fn write_metric_parquet_part_file(
22912306
&self,
22922307
part_path: &Path,
@@ -2632,7 +2647,7 @@ impl Stream {
26322647
.ok_or_else(|| {
26332648
StagingError::GlobalSchema("global schema disappeared during startup".to_owned())
26342649
})?;
2635-
let canonical = serde_json::from_slice::<Schema>(&global.schema)?;
2650+
let canonical = global.schema;
26362651
resolution.include_identity_schema(&canonical);
26372652
*self.global_schema.write().expect(LOCK_EXPECT) =
26382653
Some((Arc::new(canonical.clone()), Arc::new(resolution)));

‎src/schema_registry.rs‎

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ use std::{
3030

3131
use arrow_array::{RecordBatch, RecordBatchOptions};
3232
use arrow_schema::{ArrowError, DataType, Field, FieldRef, Schema};
33-
use bytes::Bytes;
3433
use tonic::async_trait;
3534

3635
use crate::{
@@ -49,8 +48,8 @@ trait RegistryBackend: Send + Sync {
4948
async fn read_legacy_schemas(&self) -> Result<Vec<Schema>, MetastoreError>;
5049
async fn compare_and_swap(
5150
&self,
52-
schema: Schema,
53-
expected: Option<object_store::UpdateVersion>,
51+
global_schema: GlobalSchema,
52+
expected: Option<u64>,
5453
) -> Result<bool, MetastoreError>;
5554
}
5655

@@ -76,11 +75,11 @@ impl RegistryBackend for MetastoreRegistryBackend<'_> {
7675

7776
async fn compare_and_swap(
7877
&self,
79-
schema: Schema,
80-
expected: Option<object_store::UpdateVersion>,
78+
global_schema: GlobalSchema,
79+
expected: Option<u64>,
8180
) -> Result<bool, MetastoreError> {
8281
self.metastore
83-
.put_global_schema(schema, self.stream_name, self.tenant_id, expected)
82+
.put_global_schema(global_schema, self.stream_name, self.tenant_id, expected)
8483
.await
8584
}
8685
}
@@ -264,7 +263,7 @@ async fn resolve_backend(
264263
let global = backend.read_global().await?;
265264
let (canonical, original_schema) = match global.as_ref() {
266265
Some(global) => {
267-
let original = parse_global_schema(&global.schema)?;
266+
let original = global.schema.to_owned();
268267
let normalized = normalize_registry(&original).map_err(schema_error)?;
269268
(normalized, Some(original))
270269
}
@@ -290,8 +289,11 @@ async fn resolve_backend(
290289

291290
// `None` is explicitly create-only; a snapshot version is an
292291
// update-only CAS. A failed CAS invalidates this whole resolution.
293-
let expected = global.as_ref().map(|snapshot| snapshot.version.clone());
294-
if backend.compare_and_swap(proposed, expected).await? {
292+
let expected = global.as_ref().map(|snapshot| snapshot.hash.clone());
293+
if backend
294+
.compare_and_swap(GlobalSchema::from_schema(proposed), expected)
295+
.await?
296+
{
295297
return Ok(resolution);
296298
}
297299

@@ -306,10 +308,6 @@ async fn resolve_backend(
306308
)))
307309
}
308310

309-
fn parse_global_schema(bytes: &Bytes) -> Result<Schema, MetastoreError> {
310-
serde_json::from_slice(bytes).map_err(MetastoreError::from)
311-
}
312-
313311
fn schema_error(message: impl Into<String>) -> MetastoreError {
314312
MetastoreError::JsonSchemaError {
315313
message: message.into(),

‎src/storage/azure_blob.rs‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -623,10 +623,6 @@ impl BlobStore {
623623

624624
#[async_trait]
625625
impl ObjectStorage for BlobStore {
626-
fn supports_conditional_writes(&self) -> bool {
627-
true
628-
}
629-
630626
async fn parallel_chunked_download(
631627
&self,
632628
path: &RelativePath,
@@ -850,12 +846,12 @@ impl ObjectStorage for BlobStore {
850846
&self,
851847
path: &RelativePath,
852848
data: Bytes,
853-
expected: Option<object_store::UpdateVersion>,
849+
expected: Option<u64>,
854850
tenant_id: &Option<String>,
855851
) -> Result<bool, ObjectStorageError> {
856852
let tenant = tenant_id.as_deref().unwrap_or(DEFAULT_TENANT);
857853
let date = Utc::now().date_naive().to_string();
858-
let result = crate::storage::put_object_if(
854+
let result = crate::storage::put_schema_if(
859855
&*self.client,
860856
&to_object_store_path(path),
861857
data,

‎src/storage/gcs.rs‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -590,10 +590,6 @@ impl Gcs {
590590

591591
#[async_trait]
592592
impl ObjectStorage for Gcs {
593-
fn supports_conditional_writes(&self) -> bool {
594-
true
595-
}
596-
597593
async fn parallel_chunked_download(
598594
&self,
599595
path: &RelativePath,
@@ -832,12 +828,12 @@ impl ObjectStorage for Gcs {
832828
&self,
833829
path: &RelativePath,
834830
data: Bytes,
835-
expected: Option<object_store::UpdateVersion>,
831+
expected: Option<u64>,
836832
tenant_id: &Option<String>,
837833
) -> Result<bool, ObjectStorageError> {
838834
let tenant = tenant_id.as_deref().unwrap_or(DEFAULT_TENANT);
839835
let date = Utc::now().date_naive().to_string();
840-
let result = crate::storage::put_object_if(
836+
let result = crate::storage::put_schema_if(
841837
&*self.client,
842838
&to_object_store_path(path),
843839
data,

0 commit comments

Comments
 (0)