* feat: cloud Mount() per HIP-0106
Adds pkg/vfs.Mount(*zip.App, cloud.Deps) error so the unified Hanzo
Cloud binary can blank-import and register VFS alongside every
other Hanzo subsystem.
VFS has no public HTTP surface today — it's an S3-backed virtual
block filesystem driven through FUSE/CLI/sidecar. Mount() exposes
liveness/readiness probes plus a minimal block-CRUD HTTP API so
co-resident subsystems can talk to VFS without falling back to the
CLI:
- GET /v1/vfs/health — always 200
- GET /v1/vfs/readyz — 200 only when an instance is
attached via vfs.SetInstance
- PUT /v1/vfs/blocks — payload upload
- GET /v1/vfs/blocks/:id — payload download
- DELETE /v1/vfs/blocks/:id — backend delete
- GET /v1/vfs/stats — cache/backend counters
cmd/vfsd is a HIP-0106 thin shim that wires backend + age keys from
env (VFSD_BACKEND, VFSD_AGE_KEY) and listens on cloud.Config.ListenAddr.
The existing cmd/vfs CLI (put/get/stats/mount) is untouched.
init() registers with cloud.Register("vfs", 20, …). schema/vfs.zap
adds the minimal Health + Block ZAP interface; full multi-block
File surface lands in follow-up PRs.
Tests:
- pkg/vfs: TestMount_HealthReadyz + TestMount_PutGetRoundtrip green
- existing TestRoundTrip + TestStats unchanged
* refactor: collapse pkg/vfs/ → root for clean import path
Subsystem code lives in package vfs at repo root. Importers now use
`github.com/hanzoai/vfs` (no /pkg/vfs/ nesting). Standalone shim moved
to cmd/vfs/ where applicable.
Per Hanzo Go-stdlib pattern: repo IS the package.
* refactor: flatten import path — github.com/hanzoai/cloud (drop /pkg/cloud)
98 lines
2.2 KiB
Go
98 lines
2.2 KiB
Go
package vfs
|
|
|
|
import (
|
|
"container/list"
|
|
"sync"
|
|
)
|
|
|
|
// Cache is an in-memory LRU bound by total bytes. Used as the hot tier
|
|
// in front of the backend. The on-disk cache layer (NVMe write-back)
|
|
// is a future addition (0.2.0); v0.1.0 keeps everything resident in RAM
|
|
// up to MaxBytes.
|
|
type Cache struct {
|
|
mu sync.Mutex
|
|
items map[BlockID]*list.Element
|
|
order *list.List
|
|
bytesIn int64
|
|
maxBytes int64
|
|
}
|
|
|
|
type cacheEntry struct {
|
|
id BlockID
|
|
val []byte
|
|
}
|
|
|
|
// NewCache creates an LRU cache holding up to maxBytes worth of blocks.
|
|
// maxBytes <= 0 disables eviction (unlimited; for tests).
|
|
func NewCache(maxBytes int64) *Cache {
|
|
return &Cache{
|
|
items: make(map[BlockID]*list.Element),
|
|
order: list.New(),
|
|
maxBytes: maxBytes,
|
|
}
|
|
}
|
|
|
|
// Get returns the cached block (or nil, false on miss).
|
|
func (c *Cache) Get(id BlockID) ([]byte, bool) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
el, ok := c.items[id]
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
c.order.MoveToFront(el)
|
|
return el.Value.(*cacheEntry).val, true
|
|
}
|
|
|
|
// Put inserts or updates a block. Older blocks are evicted to keep the
|
|
// total under maxBytes.
|
|
func (c *Cache) Put(id BlockID, val []byte) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
if el, ok := c.items[id]; ok {
|
|
entry := el.Value.(*cacheEntry)
|
|
c.bytesIn += int64(len(val) - len(entry.val))
|
|
entry.val = val
|
|
c.order.MoveToFront(el)
|
|
return
|
|
}
|
|
el := c.order.PushFront(&cacheEntry{id: id, val: val})
|
|
c.items[id] = el
|
|
c.bytesIn += int64(len(val))
|
|
c.evictIfOver()
|
|
}
|
|
|
|
// Delete removes a single entry.
|
|
func (c *Cache) Delete(id BlockID) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
if el, ok := c.items[id]; ok {
|
|
c.bytesIn -= int64(len(el.Value.(*cacheEntry).val))
|
|
c.order.Remove(el)
|
|
delete(c.items, id)
|
|
}
|
|
}
|
|
|
|
// Stats returns (entries, bytes-in-use, max-bytes).
|
|
func (c *Cache) Stats() (entries int, bytesInUse int64, maxBytes int64) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
return len(c.items), c.bytesIn, c.maxBytes
|
|
}
|
|
|
|
func (c *Cache) evictIfOver() {
|
|
if c.maxBytes <= 0 {
|
|
return
|
|
}
|
|
for c.bytesIn > c.maxBytes {
|
|
back := c.order.Back()
|
|
if back == nil {
|
|
return
|
|
}
|
|
entry := back.Value.(*cacheEntry)
|
|
c.order.Remove(back)
|
|
delete(c.items, entry.id)
|
|
c.bytesIn -= int64(len(entry.val))
|
|
}
|
|
}
|