Commit Graph

5 Commits

Author SHA1 Message Date
zhenyi
5d32290cf1 feat(cache): split cache module into submodules and wire invalidation hooks
Two-part change:

1) Submodule split of src/cache/ (was a single ~150-line mod.rs)
   - src/cache/config.rs: CacheConfig, defaults, default_cache_dir(),
     DEFAULT_NAME / DEFAULT_MEMORY_CAPACITY_BYTES /
     DEFAULT_DISK_CAPACITY_BYTES constants
   - src/cache/key.rs: CacheKey / CacheValue type aliases and key
     derivation helpers (rev -> oid, blob-exists negatives)
   - src/cache/store.rs: CacheStore — the foyer HybridCache wrapper
     (open / close / get / insert / remove / contains), all errors
     mapped to BabyError::Cache with op-tagged source
   - src/cache/pools.rs: CachePools — multi-store aggregator used by
     the global singleton; replaces the old monolithic CacheStore
   - src/cache/stats.rs: CacheStats + counter accessors
   - src/cache/invalidation.rs: hook_ref_updated / hook_blob_written
     (single-process invalidation hooks, fired from usecase impls)
   - src/cache/mod.rs: now just submodule re-exports of
     (config, global, invalidation, key, pools, stats, store)
   - src/cache/global.rs: GITBABY_CACHE singleton + init / try /
     shutdown now operate on Arc<CachePools> instead of Arc<CacheStore>

2) Cache integration in usecase modules
   - src/blob/usecase.rs: read paths go through cache.get / insert;
     invalidation hooks fire on write
   - src/commit/usecase.rs: commit-list results cached, invalidated on
     commit write
   - src/branch/usecase.rs: branch-list results cached, invalidated on
     ref update
   - src/refs/usecase.rs: lookup-cache + ref-update hook
   - src/tags/usecase.rs: tag-info cache + write hook

3) AGENTS.md
   - Document that cached serde payloads (CommitInfo, Vec<TreeEntry>,
     etc.) follow additive-only compatibility: never rename/remove a
     field, every new field gets #[serde(default)] so stale payloads
     still deserialize.
   - Note that the foyer cache is single-process: fixed on-disk dir
     per config + in-process invalidation hooks. Sharing across
     processes on the same repo can leave stale metadata; do not do
     that.

CI: cargo build + cargo test (61 passed) green.

BREAKING: the public cache surface moved. Anyone reaching into
cache::CacheStore directly should switch to cache::CachePools (or
cache::global::try_global_cache). The high-level helpers in
cache::global keep the same name; only the inner type changed.
2026-08-14 18:38:18 +08:00
zhenyi
4d5e5ba5f9 feat(server): add streaming-first server scaffolding
Introduce src/server/ as a streaming-first git/lfs server surface. All
request and response bodies cross the API as
Box<dyn AsyncRead + Unpin + Send + Sync> (alias BoxedAsyncRead) — no
Vec<u8> bodies, no read_to_end convenience, no http crate.

What's in the box:
- src/server/mod.rs: re-exports + pipe re-export of
  command::cmd::DEFAULT_OUTPUT_CAP_BYTES as server::DEFAULT_OUTPUT_CAP_BYTES
- src/server/backend.rs: ServerBackend async-trait (authorize,
  list_refs_stream, produce_pack_stream, ingest_pack_stream,
  lfs_get_stream, lfs_put_stream). Every method currently returns
  ServerError::Unimplemented("...") placeholders — the wiring is
  real, the bodies are intentionally empty.
- src/server/request.rs: ServerRequest (not Clone, body is a boxed
  reader), HeaderMap = HashMap<String, String>, RefUpdateBatch
  (oid strings, no gix::ObjectId), BoxedAsyncRead alias
- src/server/stream.rs: StreamGuard (Clone + Send + Sync) carrying
  cancel() / handle() so spawned workers can race the request
- src/server/endpoint.rs, error.rs, limits.rs: EndpointKind enum,
  ServerError, DEFAULT_LFS_OBJECT_CAP_BYTES (5 GiB cap, separate from
  the 64 MiB output cap)
- tests/server.rs: PlaceholderBackend skeleton + ENV_LOCK +
  200 ms cancel convention reused from tests/env.rs and tests/pipe.rs

Public API impact on GitBaby:
- Drop pipe from GitBaby::new — the server owns its own stream
  lifecycle, so the Pipe parameter is no longer required at
  construction time. Existing callers must be updated:
    - before: GitBaby::new(facade, pipe)
    - after:  GitBaby::new(facade)

Documentation:
- AGENTS.md gains a "Planned but unimplemented (streaming-first
  server)" section documenting the deliberately-empty contracts so
  future contributors don't "fill them in" without a concrete
  git/lfs protocol task.

CI: cargo build + cargo test (61 total, 23 new from server suite)
green.
2026-08-14 18:13:39 +08:00
zhenyi
66045dd971 feat(cache): add hybrid memory/disk cache layer on foyer
Introduce a two-tier cache for blob payloads backed by the foyer crate
(memory + disk). The cache is configured via CacheConfig and exposed
through src/cache/{mod,global}.rs.

Why foyer:
- HybridCache gives us bounded RAM plus disk overflow without bringing
  in our own eviction / spill logic.
- The runtime-tokio feature plays cleanly with the existing tokio stack.

Touches:
- src/cache/mod.rs: CacheConfig, CacheStore, CacheKey/Value aliases,
  default-into-disk fallback (CacheConfig::default)
- src/cache/global.rs: process-wide singleton accessor for the default
  cache
- src/error.rs: new BabyError::Cache { op, source } variant wrapping
  foyer::Error so cache failures share the existing error pipeline
- Cargo.toml: add foyer = "0.22.3" with runtime-tokio feature
- src/lib.rs: pub mod cache; (wiring only — no public API on GitBaby
  changes yet)
- src/tree/types.rs: drive-by import reorder (serde before std::path)
  to match rustfmt

CI: cargo build + cargo test (38 passed) green.

Skipped intentionally: no git/lfs protocol logic is added here; this
commit is purely the storage substrate.
2026-08-14 18:12:56 +08:00
zhenyi
916b5f3176 feat(types): add Serialize/Deserialize derives to all types.rs
Add the serde 'twins' (Serialize + Deserialize) to every public struct
and enum in src/*/types.rs so data types can be serialized via JSON,
bincode, postcard, etc.

Dependency changes:
- enable 'serde' feature on gix (for gix::ObjectId)
- enable 'serde' feature on time (for time::OffsetDateTime)

Skipped:
- src/archive/types.rs::ChildArchiveReader<R> — contains a
  tokio::process::Child field, semantically not serializable

Coverage (18 modules):
archive, blame, blob, branch, cleanup, commit, compare, config,
conflict, diff, merge, refs, remote, setup, submodule, tags, tree

Notes:
- field names kept snake_case (no rename_all remap)
- existing PartialEq / Eq / Default / Copy derives preserved
- cargo build + cargo test (38 passed) green
2026-08-14 18:10:04 +08:00
zhenyi
680411c4fb chore: initialize gitbaby repository
gitbaby is an asynchronous Rust library wrapping git operations, built on
gix + tokio + async-trait.

Module layout:
- archive, blame, blob, branch, cleanup, commit, compare, config,
  conflict, diff, merge, refs, remote, setup, share, submodule, tags,
  tree (feature modules)
- command: command scheduler (cmd/env/pipe/context/error)
- repo: RepositoryFacade trait (consumers implement, exposing a
  gix::Repository)

Features:
- async API on tokio 1.53
- order-preserving Env (Vec<(String, String)>)
- hand-written BabyError / CmdError, not using thiserror derive
- integration tests covering cmd_run / context / env / pipe (env keys
  prefixed for isolation)

CI: none, standard cargo build / test / clippy / fmt only
2026-08-14 18:10:04 +08:00