Files
wehub-resource-sync 5357c39144
Fuzzer / Run Fuzzer (push) Has been cancelled
Race tests / Go race tests (ubuntu-22.04) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:01:40 +08:00

164 lines
4.6 KiB
Go

// Copyright 2025 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package remotestorage
import (
"errors"
"sync"
"github.com/dolthub/dolt/go/store/hash"
"github.com/dolthub/dolt/go/store/nbs"
)
type WriteBuffer interface {
// Add a compressed chunk to the write buffer. It will be
// returned from future calls to |GetAllForWrite| until a
// write is successful.
Put(nbs.CompressedChunk) error
// Returns the current set of written chunks. After this
// returns, concurrent calls to other methods may block until
// |WriteCompleted| is called. Calls to |GetAllForWrite| must
// be followed by a call to |WriteCompleted| once the write
// attempt is finished.
GetAllForWrite() map[hash.Hash]nbs.CompressedChunk
// Called after a call to |GetAllForWrite|, this records
// success or failure of the write operation. If the write
// operation was successful, then the written chunks are now
// in the upstream and they can be cleared from the write
// buffer. Otherwise, the written chunks are retained in the
// write buffer so that the write can be retried.
WriteCompleted(success bool)
// ChunkStore clients expect to read their own writes before a
// commit. On the get path, remotestorage should add buffered
// chunks matching a given |query| to its |result|. On the
// HasMany path, remotestorage should remove present chunks
// from its absent set on the HasMany response.
AddBufferedChunks(query hash.HashSet, result map[hash.Hash]nbs.ToChunker)
// Removes the addresses of any buffered chunks from |hashes|.
// Used to filter the |absent| response of a HasMany call so
// that buffered chunks are not considered absent.
RemovePresentChunks(hashes hash.HashSet)
}
type noopWriteBuffer struct {
}
var _ WriteBuffer = noopWriteBuffer{}
func (noopWriteBuffer) Put(nbs.CompressedChunk) error {
return errors.New("unsupported operation: write on a read-only remotestorage chunk store")
}
func (noopWriteBuffer) GetAllForWrite() map[hash.Hash]nbs.CompressedChunk {
panic("attempt to upload chunks on a read-only remotestorage chunk store")
}
func (noopWriteBuffer) WriteCompleted(success bool) {
panic("call to WriteCompleted on a noopWriteBuffer")
}
func (noopWriteBuffer) AddBufferedChunks(hash.HashSet, map[hash.Hash]nbs.ToChunker) {
}
func (noopWriteBuffer) RemovePresentChunks(hash.HashSet) {
}
// A simple WriteBuffer which buffers unlimited data in memory and
// waits to flush it.
type mapWriteBuffer struct {
mu sync.Mutex
cond sync.Cond
// Set when an outstanding write is in progress, |Put| will
// block against this. Reset by |WriteCompleted| after the
// appropriate updates to |chunks| have been made.
writing bool
chunks map[hash.Hash]nbs.CompressedChunk
}
func newMapWriteBuffer() *mapWriteBuffer {
ret := &mapWriteBuffer{
chunks: make(map[hash.Hash]nbs.CompressedChunk),
}
ret.cond.L = &ret.mu
return ret
}
func (b *mapWriteBuffer) Put(cc nbs.CompressedChunk) error {
b.mu.Lock()
defer b.mu.Unlock()
for b.writing {
b.cond.Wait()
}
b.chunks[cc.H] = cc
return nil
}
func (b *mapWriteBuffer) GetAllForWrite() map[hash.Hash]nbs.CompressedChunk {
b.mu.Lock()
defer b.mu.Unlock()
for b.writing {
b.cond.Wait()
}
b.writing = true
return b.chunks
}
func (b *mapWriteBuffer) WriteCompleted(success bool) {
b.mu.Lock()
defer b.mu.Unlock()
if !b.writing {
panic("mapWriteBuffer got WriteCompleted while no write was in progress")
}
b.writing = false
if success {
b.chunks = make(map[hash.Hash]nbs.CompressedChunk)
}
b.cond.Broadcast()
}
func (b *mapWriteBuffer) AddBufferedChunks(hs hash.HashSet, res map[hash.Hash]nbs.ToChunker) {
b.mu.Lock()
defer b.mu.Unlock()
for h := range hs {
cc, ok := b.chunks[h]
if ok {
res[h] = cc
}
}
}
func (b *mapWriteBuffer) RemovePresentChunks(hashes hash.HashSet) {
b.mu.Lock()
defer b.mu.Unlock()
if len(b.chunks) < len(hashes) {
for h := range b.chunks {
hashes.Remove(h)
}
} else {
var toRemove []hash.Hash
for h := range hashes {
if _, ok := b.chunks[h]; ok {
toRemove = append(toRemove, h)
}
}
for _, h := range toRemove {
hashes.Remove(h)
}
}
}