Line data Source code
1 : // Copyright 2024 The LevelDB-Go and Pebble Authors. All rights reserved. Use
2 : // of this source code is governed by a BSD-style license that can be found in
3 : // the LICENSE file.
4 :
5 : package sstable
6 :
7 : import (
8 : "github.com/cockroachdb/errors"
9 : "github.com/cockroachdb/pebble/internal/base"
10 : )
11 :
12 : // obsoleteKeyBlockPropertyCollector is a block property collector used to
13 : // implement obsoleteKeyBlockPropertyFilter - a filter that excludes blocks
14 : // which contain only obsolete keys.
15 : //
16 : // For an explanation of obsolete keys, see the comment for TableFormatPebblev4
17 : // which explains obsolete keys.
18 : type obsoleteKeyBlockPropertyCollector struct {
19 : blockIsNonObsolete bool
20 : indexIsNonObsolete bool
21 : tableIsNonObsolete bool
22 : }
23 :
24 : var _ BlockPropertyCollector = (*obsoleteKeyBlockPropertyCollector)(nil)
25 :
26 : // Name is part of the BlockPropertyCollector interface.
27 1 : func (o *obsoleteKeyBlockPropertyCollector) Name() string {
28 1 : return "obsolete-key"
29 1 : }
30 :
31 : // AddPointKey is part of the BlockPropertyCollector interface.
32 1 : func (o *obsoleteKeyBlockPropertyCollector) AddPointKey(key InternalKey, value []byte) error {
33 1 : // Ignore.
34 1 : return nil
35 1 : }
36 :
37 : // AddRangeKeys is part of the BlockPropertyCollector interface.
38 1 : func (o *obsoleteKeyBlockPropertyCollector) AddRangeKeys(span Span) error {
39 1 : // Ignore.
40 1 : return nil
41 1 : }
42 :
43 : // AddPoint is an out-of-band method that is specific to this collector.
44 1 : func (o *obsoleteKeyBlockPropertyCollector) AddPoint(isObsolete bool) {
45 1 : o.blockIsNonObsolete = o.blockIsNonObsolete || !isObsolete
46 1 : }
47 :
48 : // FinishDataBlock is part of the BlockPropertyCollector interface.
49 1 : func (o *obsoleteKeyBlockPropertyCollector) FinishDataBlock(buf []byte) ([]byte, error) {
50 1 : o.tableIsNonObsolete = o.tableIsNonObsolete || o.blockIsNonObsolete
51 1 : return obsoleteKeyBlockPropertyEncode(!o.blockIsNonObsolete, buf), nil
52 1 : }
53 :
54 : // AddPrevDataBlockToIndexBlock is part of the BlockPropertyCollector interface.
55 1 : func (o *obsoleteKeyBlockPropertyCollector) AddPrevDataBlockToIndexBlock() {
56 1 : o.indexIsNonObsolete = o.indexIsNonObsolete || o.blockIsNonObsolete
57 1 : o.blockIsNonObsolete = false
58 1 : }
59 :
60 : // FinishIndexBlock is part of the BlockPropertyCollector interface.
61 1 : func (o *obsoleteKeyBlockPropertyCollector) FinishIndexBlock(buf []byte) ([]byte, error) {
62 1 : buf = obsoleteKeyBlockPropertyEncode(!o.indexIsNonObsolete, buf)
63 1 : o.indexIsNonObsolete = false
64 1 : return buf, nil
65 1 : }
66 :
67 : // FinishTable is part of the BlockPropertyCollector interface.
68 1 : func (o *obsoleteKeyBlockPropertyCollector) FinishTable(buf []byte) ([]byte, error) {
69 1 : return obsoleteKeyBlockPropertyEncode(!o.tableIsNonObsolete, buf), nil
70 1 : }
71 :
72 : // AddCollectedWithSuffixReplacement is part of the BlockPropertyCollector interface.
73 : func (o *obsoleteKeyBlockPropertyCollector) AddCollectedWithSuffixReplacement(
74 : oldProp []byte, oldSuffix, newSuffix []byte,
75 0 : ) error {
76 0 : // Verify the property is valid.
77 0 : _, err := obsoleteKeyBlockPropertyDecode(oldProp)
78 0 : if err != nil {
79 0 : return err
80 0 : }
81 : // Suffix rewriting currently loses the obsolete bit.
82 0 : o.blockIsNonObsolete = true
83 0 : return nil
84 : }
85 :
86 : // SupportsSuffixReplacement is part of the BlockPropertyCollector interface.
87 0 : func (o *obsoleteKeyBlockPropertyCollector) SupportsSuffixReplacement() bool {
88 0 : return true
89 0 : }
90 :
91 : // obsoleteKeyBlockPropertyFilter implements the filter that excludes blocks
92 : // that only contain obsolete keys. It pairs with
93 : // obsoleteKeyBlockPropertyCollector.
94 : //
95 : // Note that we filter data blocks as well as first-level index blocks.
96 : //
97 : // For an explanation of obsolete keys, see the comment for TableFormatPebblev4
98 : // which explains obsolete keys.
99 : //
100 : // NB: obsoleteKeyBlockPropertyFilter is stateless. This aspect of the filter
101 : // is used in table_cache.go for in-place modification of a filters slice.
102 : type obsoleteKeyBlockPropertyFilter struct{}
103 :
104 : var _ BlockPropertyFilter = obsoleteKeyBlockPropertyFilter{}
105 :
106 : // Name is part of the BlockPropertyFilter interface. It must match
107 : // obsoleteKeyBlockPropertyCollector.Name.
108 1 : func (o obsoleteKeyBlockPropertyFilter) Name() string {
109 1 : return "obsolete-key"
110 1 : }
111 :
112 : // Intersects is part of the BlockPropertyFilter interface. It returns true
113 : // if the block may contain non-obsolete keys.
114 1 : func (o obsoleteKeyBlockPropertyFilter) Intersects(prop []byte) (bool, error) {
115 1 : isObsolete, err := obsoleteKeyBlockPropertyDecode(prop)
116 1 : if err != nil {
117 0 : return false, err
118 0 : }
119 1 : return !isObsolete, nil
120 : }
121 :
122 : // SyntheticSuffixIntersects is part of the BlockPropertyFilter interface. It
123 : // expects that synthetic suffix is never used with tables that contain obsolete
124 : // keys.
125 : func (o obsoleteKeyBlockPropertyFilter) SyntheticSuffixIntersects(
126 : prop []byte, suffix []byte,
127 1 : ) (bool, error) {
128 1 : isObsolete, err := obsoleteKeyBlockPropertyDecode(prop)
129 1 : if err != nil {
130 0 : return false, err
131 0 : }
132 : // A block with suffix replacement should never be obsolete.
133 1 : if isObsolete {
134 0 : return false, base.AssertionFailedf("block with synthetic suffix is obsolete")
135 0 : }
136 1 : return true, nil
137 : }
138 :
139 : // Encodes the information of whether the block contains only obsolete keys. We
140 : // use the empty encoding for the common case of a block not being obsolete.
141 1 : func obsoleteKeyBlockPropertyEncode(isObsolete bool, buf []byte) []byte {
142 1 : if isObsolete {
143 1 : return append(buf, 't')
144 1 : }
145 1 : return buf
146 : }
147 :
148 : // Decodes the information of whether the block contains only obsolete keys (the
149 : // inverse of obsoleteKeyBlockPropertyEncode).
150 1 : func obsoleteKeyBlockPropertyDecode(prop []byte) (isObsolete bool, _ error) {
151 1 : switch {
152 1 : case len(prop) == 0:
153 1 : return false, nil
154 1 : case len(prop) == 1 && prop[0] == 't':
155 1 : return true, nil
156 0 : default:
157 0 : return false, errors.Errorf("invalid obsolete block property %x", prop)
158 : }
159 : }
|