Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude
7 changes: 5 additions & 2 deletions inmem/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,17 @@ type MemStore struct {
blobRefs map[claimKey]registry.BlobClaim
intents map[string]registry.UploadIntent // keyed by string(digest)
locations map[locKey]registry.BlobLocation // keyed by (space, digest)
encParams map[locKey]registry.BlobEncryptionParams // keyed by (space, digest)
inclusions map[locKey]registry.BlobInclusion // keyed by (space, digest)
parks map[string]registry.BlobPark // keyed by string(digest)
sessions map[string]registry.MultipartSession // keyed by uploadID
parts map[string]map[int]registry.MultipartPart // uploadID -> partNumber -> part
gcCands map[string]struct{} // keyed by string(cid)
}

// claimKey / locKey are the composite map keys for the blob_refs and
// blob_locations tables (digest bytes carried as a string for comparability).
// claimKey / locKey are the composite map keys for the blob_refs and the
// (space, digest)-keyed tables — blob_locations, blob_encryption_params and
// shard_inclusions (digest bytes carried as a string for comparability).
type claimKey struct {
digest, bucket, objectKey, versionID string
}
Expand All @@ -86,6 +88,7 @@ func NewMemStore() *MemStore {
blobRefs: map[claimKey]registry.BlobClaim{},
intents: map[string]registry.UploadIntent{},
locations: map[locKey]registry.BlobLocation{},
encParams: map[locKey]registry.BlobEncryptionParams{},
inclusions: map[locKey]registry.BlobInclusion{},
parks: map[string]registry.BlobPark{},
sessions: map[string]registry.MultipartSession{},
Expand Down
92 changes: 78 additions & 14 deletions inmem/stores.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,19 @@ import (
)

// In-memory implementations of the architecture's relational stores
// (registry.BlobRefStore / IntentStore / LocationStore / MultipartStore /
// GCStore), mirroring the Postgres tables so the in-process suite and
// standalone mode exercise the same write/read/delete code paths.
// (registry.BlobRefStore / IntentStore / LocationStore / EncryptionParamsStore /
// MultipartStore / GCStore), mirroring the Postgres tables so the in-process
// suite and standalone mode exercise the same write/read/delete code paths.

// Compile-time assertions: *MemStore satisfies every store interface.
var (
_ registry.BlobRefStore = (*MemStore)(nil)
_ registry.IntentStore = (*MemStore)(nil)
_ registry.LocationStore = (*MemStore)(nil)
_ registry.InclusionStore = (*MemStore)(nil)
_ registry.MultipartStore = (*MemStore)(nil)
_ registry.GCStore = (*MemStore)(nil)
_ registry.BlobRefStore = (*MemStore)(nil)
_ registry.IntentStore = (*MemStore)(nil)
_ registry.LocationStore = (*MemStore)(nil)
_ registry.EncryptionParamsStore = (*MemStore)(nil)
_ registry.InclusionStore = (*MemStore)(nil)
_ registry.MultipartStore = (*MemStore)(nil)
_ registry.GCStore = (*MemStore)(nil)
)

func cloneBytes(b []byte) []byte {
Expand Down Expand Up @@ -125,9 +126,7 @@ func (m *MemStore) DeleteIntent(_ context.Context, digest []byte) error {
func (m *MemStore) PutLocation(_ context.Context, loc registry.BlobLocation) error {
m.mu.Lock()
defer m.mu.Unlock()
cp := loc
cp.Digest = cloneBytes(loc.Digest)
m.locations[locKey{loc.Space, string(loc.Digest)}] = cp
m.locations[locKey{loc.Space, string(loc.Digest)}] = cloneLocation(loc)
return nil
}

Expand All @@ -138,8 +137,7 @@ func (m *MemStore) GetLocation(_ context.Context, space did.DID, digest []byte)
if !ok {
return nil, registry.ErrNotFound
}
cp := loc
cp.Digest = cloneBytes(loc.Digest)
cp := cloneLocation(loc)
return &cp, nil
}

Expand All @@ -150,6 +148,54 @@ func (m *MemStore) DeleteLocation(_ context.Context, space did.DID, digest []byt
return nil
}

// EncryptionParamsStore ======================================================

func (m *MemStore) PutEncryptionParams(_ context.Context, params registry.BlobEncryptionParams) error {
// Match the Postgres store, whose columns are all NOT NULL.
if err := params.Validate(); err != nil {
return err
}
m.mu.Lock()
defer m.mu.Unlock()
m.encParams[locKey{params.Space, string(params.Digest)}] = cloneEncryptionParams(params)
return nil
}

func (m *MemStore) GetEncryptionParams(_ context.Context, space did.DID, digest []byte) (*registry.BlobEncryptionParams, error) {
m.mu.Lock()
defer m.mu.Unlock()
params, ok := m.encParams[locKey{space, string(digest)}]
if !ok {
return nil, registry.ErrNotFound
}
cp := cloneEncryptionParams(params)
return &cp, nil
}

func (m *MemStore) RewrapEncryptionParams(_ context.Context, space did.DID, digest, wrappedCEK []byte, keyVersion string) error {
if err := registry.ValidateRewrap(wrappedCEK, keyVersion); err != nil {
return err
}
m.mu.Lock()
defer m.mu.Unlock()
key := locKey{space, string(digest)}
params, ok := m.encParams[key]
if !ok {
return registry.ErrNotFound
}
params.RegionWrappedCEK = cloneBytes(wrappedCEK)
params.RegionKeyVersion = keyVersion
m.encParams[key] = params
return nil
}

func (m *MemStore) DeleteEncryptionParams(_ context.Context, space did.DID, digest []byte) error {
m.mu.Lock()
defer m.mu.Unlock()
delete(m.encParams, locKey{space, string(digest)})
return nil
}

// ParkStore ==================================================================

func (m *MemStore) PutPark(_ context.Context, p registry.BlobPark) error {
Expand Down Expand Up @@ -386,6 +432,24 @@ func cloneSession(s registry.MultipartSession) registry.MultipartSession {
return s
}

// cloneLocation deep-copies a BlobLocation's digest so the stored copy and any
// returned copy never alias the caller's slice.
func cloneLocation(loc registry.BlobLocation) registry.BlobLocation {
loc.Digest = cloneBytes(loc.Digest)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just use bytes.Clone?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Written by Claude.

Done, and removed the local cloneBytes helper in favour of bytes.Clone everywhere in the file.

return loc
}

// cloneEncryptionParams deep-copies a BlobEncryptionParams' byte-slice fields —
// the digest and the key material — so the stored copy and any returned copy
// never alias the caller's slices.
func cloneEncryptionParams(p registry.BlobEncryptionParams) registry.BlobEncryptionParams {
p.Digest = cloneBytes(p.Digest)
p.RegionWrappedCEK = cloneBytes(p.RegionWrappedCEK)
p.BaseNonce = cloneBytes(p.BaseNonce)
p.AAD = cloneBytes(p.AAD)
return p
}

func clonePart(p registry.MultipartPart) registry.MultipartPart {
p.ETagMD5 = cloneBytes(p.ETagMD5)
if p.BlobDigests != nil {
Expand Down
Loading
Loading