mirror of
https://github.com/dolthub/dolt.git
synced 2026-01-28 18:59:00 -06:00
189 lines
4.6 KiB
Go
189 lines
4.6 KiB
Go
// Copyright 2022 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 nbs
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
|
|
"golang.org/x/sync/errgroup"
|
|
|
|
"github.com/dolthub/dolt/go/store/chunks"
|
|
"github.com/dolthub/dolt/go/store/hash"
|
|
)
|
|
|
|
// journalChunkSource is a chunkSource that reads chunks
|
|
// from a chunkJournal. Unlike other NBS chunkSources,
|
|
// it is not immutable and its set of chunks grows as
|
|
// more commits are made to the chunkJournal.
|
|
type journalChunkSource struct {
|
|
journal *journalWriter
|
|
}
|
|
|
|
var _ chunkSource = journalChunkSource{}
|
|
|
|
func (s journalChunkSource) has(h addr) (bool, error) {
|
|
return s.journal.hasAddr(h), nil
|
|
}
|
|
|
|
func (s journalChunkSource) hasMany(addrs []hasRecord) (missing bool, err error) {
|
|
for i := range addrs {
|
|
ok := s.journal.hasAddr(*addrs[i].a)
|
|
if ok {
|
|
addrs[i].has = true
|
|
} else {
|
|
missing = true
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (s journalChunkSource) getCompressed(_ context.Context, h addr, _ *Stats) (CompressedChunk, error) {
|
|
return s.journal.getCompressedChunk(h)
|
|
}
|
|
|
|
func (s journalChunkSource) get(_ context.Context, h addr, _ *Stats) ([]byte, error) {
|
|
cc, err := s.journal.getCompressedChunk(h)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if cc.IsEmpty() {
|
|
return nil, nil
|
|
}
|
|
ch, err := cc.ToChunk()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return ch.Data(), nil
|
|
}
|
|
|
|
func (s journalChunkSource) getMany(ctx context.Context, _ *errgroup.Group, reqs []getRecord, found func(context.Context, *chunks.Chunk), stats *Stats) (bool, error) {
|
|
var remaining bool
|
|
// todo: read planning
|
|
for i := range reqs {
|
|
if reqs[i].found {
|
|
continue
|
|
}
|
|
data, err := s.get(ctx, *reqs[i].a, stats)
|
|
if err != nil {
|
|
return false, err
|
|
} else if data != nil {
|
|
reqs[i].found = true
|
|
ch := chunks.NewChunkWithHash(hash.Hash(*reqs[i].a), data)
|
|
found(ctx, &ch)
|
|
} else {
|
|
remaining = true
|
|
}
|
|
}
|
|
return remaining, nil
|
|
}
|
|
|
|
func (s journalChunkSource) getManyCompressed(ctx context.Context, _ *errgroup.Group, reqs []getRecord, found func(context.Context, CompressedChunk), stats *Stats) (bool, error) {
|
|
var remaining bool
|
|
// todo: read planning
|
|
for i := range reqs {
|
|
if reqs[i].found {
|
|
continue
|
|
}
|
|
cc, err := s.getCompressed(ctx, *reqs[i].a, stats)
|
|
if err != nil {
|
|
return false, err
|
|
} else if cc.IsEmpty() {
|
|
remaining = true
|
|
} else {
|
|
reqs[i].found = true
|
|
found(ctx, cc)
|
|
}
|
|
}
|
|
return remaining, nil
|
|
}
|
|
|
|
func (s journalChunkSource) count() (uint32, error) {
|
|
return s.journal.recordCount(), nil
|
|
}
|
|
|
|
func (s journalChunkSource) uncompressedLen() (uint64, error) {
|
|
return s.journal.uncompressedSize(), nil
|
|
}
|
|
|
|
func (s journalChunkSource) hash() addr {
|
|
return journalAddr
|
|
}
|
|
|
|
// reader implements chunkSource.
|
|
func (s journalChunkSource) reader(context.Context) (io.ReadCloser, uint64, error) {
|
|
rdr, sz, err := s.journal.snapshot()
|
|
return rdr, uint64(sz), err
|
|
}
|
|
|
|
func (s journalChunkSource) getRecordRanges(requests []getRecord) (map[hash.Hash]Range, error) {
|
|
ranges := make(map[hash.Hash]Range, len(requests))
|
|
for _, req := range requests {
|
|
if req.found {
|
|
continue
|
|
}
|
|
rng, ok, err := s.journal.getRange(*req.a)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if !ok {
|
|
continue
|
|
}
|
|
req.found = true // update |requests|
|
|
ranges[hash.Hash(*req.a)] = rng
|
|
}
|
|
return ranges, nil
|
|
}
|
|
|
|
// size implements chunkSource.
|
|
// size returns the total size of the chunkSource: chunks, index, and footer
|
|
func (s journalChunkSource) currentSize() uint64 {
|
|
return uint64(s.journal.currentSize())
|
|
}
|
|
|
|
// index implements chunkSource.
|
|
func (s journalChunkSource) index() (tableIndex, error) {
|
|
return nil, fmt.Errorf("journalChunkSource cannot be conjoined")
|
|
}
|
|
|
|
func (s journalChunkSource) clone() (chunkSource, error) {
|
|
return s, nil
|
|
}
|
|
|
|
func (s journalChunkSource) close() error {
|
|
// |s.journal| closed via chunkJournal
|
|
return nil
|
|
}
|
|
|
|
func equalSpecs(left, right []tableSpec) bool {
|
|
if len(left) != len(right) {
|
|
return false
|
|
}
|
|
l := make(map[addr]struct{}, len(left))
|
|
for _, s := range left {
|
|
l[s.name] = struct{}{}
|
|
}
|
|
for _, s := range right {
|
|
if _, ok := l[s.name]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func emptyAddr(a addr) bool {
|
|
var b addr
|
|
return a == b
|
|
}
|