package dbdoor import ( "errors" "fmt" "sync" ) const HandleHeader = "X-Carlos-DB-Handle" type dbHandleState struct { listMu sync.Mutex mu sync.Mutex plan []LTXFile pinned bool files map[string]LTXFile } func (c *Client) rememberFiles(level int, list ListResponse) ([]LTXFile, error) { c.handles.mu.Lock() defer c.handles.mu.Unlock() if list.Protocol != 0 && list.Protocol != 2 { return nil, errors.New("dbdoor: unsupported listing protocol") } if c.handles.pinned && list.Protocol != 2 { return nil, errors.New("dbdoor: pinned listing protocol disappeared") } if list.Plan && list.Protocol != 2 { return nil, errors.New("dbdoor: invalid pinned listing") } if list.Protocol == 0 { return list.Files, nil } files := make(map[string]LTXFile, len(c.handles.files)+len(list.Files)) for k, v := range c.handles.files { files[k] = v } seen := map[string]bool{} out := make([]LTXFile, 0, len(list.Files)) for _, f := range list.Files { k := GetTail(f.Level, f.MinTXID, f.MaxTXID) if (!list.Plan && f.Level != level) || f.Level < 0 || f.Level > MaxLevel || f.MinTXID == 0 || f.MaxTXID < f.MinTXID || f.Handle == "" || f.Size < 0 || seen[k] { return nil, errors.New("dbdoor: invalid pinned listing") } seen[k] = true if old, ok := files[k]; ok { if old.Size != f.Size { return nil, errors.New("dbdoor: pinned file size changed; retry the database pull") } f = old // Preserve first handle and its metadata through retries/resumes. } files[k] = f out = append(out, f) } if list.Plan { if c.handles.plan == nil { c.handles.plan = out } out = filesAtLevel(c.handles.plan, level) } c.handles.files = files c.handles.pinned = true return out, nil } func (c *Client) fileHandle(level int, lo, hi uint64) (string, error) { c.handles.mu.Lock() defer c.handles.mu.Unlock() if !c.handles.pinned { return "", nil } if f, ok := c.handles.files[GetTail(level, lo, hi)]; ok { return f.Handle, nil } return "", fmt.Errorf("dbdoor: coordinate has no pinned handle: %d %s", level, Span(lo, hi)) } func filesAtLevel(files []LTXFile, level int) []LTXFile { out := make([]LTXFile, 0) for _, f := range files { if f.Level == level { out = append(out, f) } } return out } func (c *Client) planFiles(level int) ([]LTXFile, bool) { c.handles.mu.Lock() defer c.handles.mu.Unlock() return filesAtLevel(c.handles.plan, level), c.handles.plan != nil }