Skip to main content

shardline_server/
lib.rs

1#![deny(unsafe_code)]
2#![allow(
3    clippy::indexing_slicing,
4    clippy::missing_errors_doc,
5    clippy::missing_panics_doc,
6    clippy::let_underscore_must_use
7)]
8#![cfg_attr(
9    test,
10    allow(
11        clippy::unwrap_used,
12        clippy::expect_used,
13        clippy::arithmetic_side_effects,
14        clippy::shadow_unrelated,
15        clippy::format_push_string
16    )
17)]
18
19//! HTTP server runtime and operator workflows for Shardline.
20//!
21//! This crate owns the deployable server boundary: HTTP routing, configuration,
22//! local and Postgres metadata backends, provider token issuance, integrity
23//! checks, repair flows, garbage collection, backup manifests, and storage
24//! migration.
25//!
26//! Most embedders only need [`ServerConfig`] and [`serve`]. Operational tooling
27//! can use the same library entry points exposed by the `shardline` CLI, such as
28//! [`run_fsck`], [`run_lifecycle_repair`], [`run_gc`], and
29//! [`run_storage_migration`].
30//!
31//! # Example
32//!
33//! ```no_run
34//! use shardline_server::{ServerConfig, serve};
35//!
36//! #[tokio::main]
37//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
38//!     let config = ServerConfig::from_env()?;
39//!     serve(config).await?;
40//!     Ok(())
41//! }
42//! ```
43
44pub mod app;
45mod auth;
46mod backend;
47mod backup;
48mod chunk_store;
49mod clock;
50mod config;
51mod database_migration;
52pub mod download_stream;
53mod error;
54mod fsck;
55mod fuzz;
56mod ingest_bench;
57mod jwks_provider;
58mod lifecycle_repair;
59mod local_backend;
60mod local_fs;
61mod local_path;
62pub mod metrics;
63mod model;
64mod object_store;
65mod oidc_provider;
66mod ops_record_store;
67mod overflow;
68mod postgres_backend;
69mod protocol_support;
70mod provider;
71mod provider_events;
72mod rebuild;
73mod reconstruction_cache;
74mod record_store;
75mod repository_scope_path;
76mod runtime_check;
77mod server_frontend;
78mod server_role;
79mod storage_migration;
80pub mod test_fixtures;
81pub mod test_invariant_error;
82
83pub use app::ProtocolMetrics;
84pub use app::{
85    AppState, MAX_PROVIDER_NAME_BYTES, MAX_PROVIDER_SUBJECT_BYTES,
86    MAX_PROVIDER_TOKEN_REQUEST_BODY_BYTES, MAX_PROVIDER_WEBHOOK_BODY_BYTES,
87    acquire_chunk_transfer_permit, full_byte_stream_response,
88};
89pub use backend::{
90    BenchmarkBackend, ServerBackend, clear_repository_reference_probe_filter,
91    lock_repository_reference_probe_test, repository_reference_probe_count,
92    reset_repository_reference_probe_count_for_hash,
93};
94pub use download_stream::{STREAM_READ_BUFFER_BYTES, ServerByteStream};
95pub use local_backend::chunk_hash;
96pub use object_store::ServerObjectStore;
97pub use oci_adapter::{oci_blob_key, oci_manifest_key, oci_manifest_media_type_key};
98pub use protocol_support::shared_sha256_object_key;
99pub use reconstruction_cache::ReconstructionCacheService;
100pub(crate) use shardline_oci_adapter as oci_adapter;
101pub use shardline_protocol_adapters::{BazelCacheKind, bazel_cache_object_key, lfs_object_key};
102pub use transfer_limiter::TransferLimiter;
103#[cfg(test)]
104mod gc_tests;
105mod transfer_limiter;
106mod upload_ingest;
107mod validation;
108pub(crate) use shardline_xet_adapter as xet_adapter;
109
110pub use app::{serve, serve_with_listener};
111pub use backup::{BackupManifestReport, write_backup_manifest};
112pub use config::{
113    AuthProviderKind, ObjectStorageAdapter, ServerConfig, ServerConfigError, ShardMetadataLimits,
114};
115pub use database_migration::{
116    DatabaseMigration, DatabaseMigrationCommand, DatabaseMigrationError, DatabaseMigrationOptions,
117    DatabaseMigrationReport, DatabaseMigrationStatusEntry, apply_database_migrations,
118    bundled_database_migrations, run_database_migration,
119};
120pub use error::ServerError;
121pub(crate) use error::{InvalidReconstructionResponseError, InvalidSerializedShardError};
122pub use fsck::{
123    FsckIssueDetail, FsckIssueKind, FsckReconstructionPlanDetail, LocalFsckIssue,
124    LocalFsckIssueKind, LocalFsckReport, ProviderRepositoryStateTimestampField, run_fsck,
125    run_local_fsck,
126};
127pub use fuzz::{
128    FuzzBazelHttpFrontendSummary, FuzzLfsFrontendSummary, FuzzLifecycleRepairSummary,
129    FuzzOciFrontendSummary, FuzzProtocolFrontendSummary, FuzzReconstructionResponseSummary,
130    FuzzRetainedShardSummary, FuzzValidatedXorbSummary, fuzz_bazel_http_frontend_summary,
131    fuzz_lfs_frontend_summary, fuzz_lifecycle_repair_summary, fuzz_normalize_and_validate_xorb,
132    fuzz_oci_frontend_summary, fuzz_protocol_frontend_summary,
133    fuzz_reconstruction_response_summary, fuzz_retained_shard_chunk_hashes,
134};
135pub use gc::{
136    DEFAULT_LOCAL_GC_RETENTION_SECONDS, LocalGcDiagnostics, LocalGcOptions, LocalGcReport,
137};
138pub use ingest_bench::ingest_without_storage_with_parallelism;
139pub use lifecycle_repair::{
140    DEFAULT_WEBHOOK_DELIVERY_RETENTION_SECONDS, LifecycleRepairOptions, LifecycleRepairReport,
141    run_lifecycle_repair, run_local_lifecycle_repair,
142};
143pub use local_backend::LocalBackend;
144pub use model::{
145    GitLfsAuthenticateResponse, HealthResponse, ProviderTokenIssueRequest,
146    ProviderTokenIssueResponse, ProviderWebhookResponse, ReadyResponse, ServerStatsResponse,
147    XetCasTokenResponse,
148};
149pub(crate) use postgres_backend::PostgresBackend;
150pub use rebuild::{
151    IndexRebuildIssueDetail, IndexRebuildReconstructionPlanDetail, LocalIndexRebuildIssue,
152    LocalIndexRebuildIssueKind, LocalIndexRebuildReport, run_index_rebuild,
153    run_local_index_rebuild,
154};
155pub use reconstruction_cache::{
156    ReconstructionCacheBenchReport, benchmark_memory_reconstruction_cache,
157};
158pub use runtime_check::{ConfigCheckReport, run_config_check};
159pub use server_frontend::{ServerFrontend, ServerFrontendParseError};
160pub use server_role::{ServerRole, ServerRoleParseError};
161pub(crate) use shardline_gc as gc;
162pub(crate) use shardline_protocol_adapters::{
163    LFS_CONTENT_TYPE, LfsBatchRequest, LfsBatchResponse, LfsObjectError, LfsObjectResponse,
164};
165pub(crate) use shardline_xet_adapter::ShardUploadResponse;
166pub use storage_migration::{
167    StorageMigrationEndpoint, StorageMigrationOptions, StorageMigrationReport,
168    run_storage_migration,
169};
170pub use xet_adapter::{
171    BatchReconstructionResponse, FileReconstructionResponse, FileReconstructionV2Response,
172    XorbUploadResponse, decode_serialized_xorb_chunks, try_for_each_serialized_xorb_chunk,
173    validate_serialized_xorb,
174};
175
176use object_store::object_store_from_config;
177use postgres_backend::connect_postgres_metadata_pool;
178use shardline_index::{LocalIndexStore, LocalRecordStore, PostgresIndexStore, PostgresRecordStore};
179
180/// Runs garbage collection against the configured metadata backend and local chunk storage.
181///
182/// # Errors
183///
184/// Returns [`ServerError`] when metadata cannot be read, quarantine state cannot be
185/// updated, or deletion fails.
186pub async fn run_gc(
187    config: ServerConfig,
188    options: gc::LocalGcOptions,
189) -> Result<gc::LocalGcReport, error::ServerError> {
190    Ok(run_gc_diagnostics(config, options).await?.report)
191}
192
193/// Runs garbage collection and returns operator diagnostics.
194///
195/// # Errors
196///
197/// Returns [`ServerError`] when metadata cannot be read, quarantine state cannot be
198/// updated, or deletion fails.
199pub async fn run_gc_diagnostics(
200    config: ServerConfig,
201    options: gc::LocalGcOptions,
202) -> Result<gc::LocalGcDiagnostics, error::ServerError> {
203    let object_store = object_store_from_config(&config)?;
204    if let Some(index_postgres_url) = config.index_postgres_url() {
205        let pool = connect_postgres_metadata_pool(index_postgres_url, 4)?;
206        let index_store = PostgresIndexStore::new(pool.clone());
207        let record_store = PostgresRecordStore::new(pool);
208        return gc::run_gc_with_stores(
209            &record_store,
210            &index_store,
211            &object_store,
212            config.server_frontends(),
213            options,
214        )
215        .await
216        .map_err(Into::into);
217    }
218
219    let index_store = LocalIndexStore::open(config.root_dir().to_path_buf());
220    let record_store = LocalRecordStore::open(config.root_dir().to_path_buf());
221    gc::run_gc_with_stores(
222        &record_store,
223        &index_store,
224        &object_store,
225        config.server_frontends(),
226        options,
227    )
228    .await
229    .map_err(Into::into)
230}