github.com/ethereum/go-ethereum@v1.16.1/core/rawdb/freezer_meta.go (about) 1 // Copyright 2022 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package rawdb 18 19 import ( 20 "errors" 21 "io" 22 "math" 23 "os" 24 25 "github.com/ethereum/go-ethereum/log" 26 "github.com/ethereum/go-ethereum/rlp" 27 ) 28 29 const ( 30 freezerTableV1 = 1 // Initial version of metadata struct 31 freezerTableV2 = 2 // Add field: 'flushOffset' 32 freezerVersion = freezerTableV2 // The current used version 33 ) 34 35 // freezerTableMeta is a collection of additional properties that describe the 36 // freezer table. These properties are designed with error resilience, allowing 37 // them to be automatically corrected after an error occurs without significantly 38 // impacting overall correctness. 39 type freezerTableMeta struct { 40 file *os.File // file handler of metadata 41 version uint16 // version descriptor of the freezer table 42 43 // virtualTail represents the number of items marked as deleted. It is 44 // calculated as the sum of items removed from the table and the items 45 // hidden within the table, and should never be less than the "actual 46 // tail". 47 // 48 // If lost due to a crash or other reasons, it will be reset to the number 49 // of items deleted from the table, causing the previously hidden items 50 // to become visible, which is an acceptable consequence. 51 virtualTail uint64 52 53 // flushOffset represents the offset in the index file up to which the index 54 // items along with the corresponding data items in data files has been flushed 55 // (fsync’d) to disk. Beyond this offset, data integrity is not guaranteed, 56 // the extra index items along with the associated data items should be removed 57 // during the startup. 58 // 59 // The principle is that all data items above the flush offset are considered 60 // volatile and should be recoverable if they are discarded after the unclean 61 // shutdown. If data integrity is required, manually force a sync of the 62 // freezer before proceeding with further operations (e.g. do freezer.Sync() 63 // first and then write data to key value store in some circumstances). 64 // 65 // The offset could be moved forward by applying sync operation, or be moved 66 // backward in cases of head/tail truncation, etc. 67 flushOffset int64 68 } 69 70 // decodeV1 attempts to decode the metadata structure in v1 format. If fails or 71 // the result is incompatible, nil is returned. 72 func decodeV1(file *os.File) *freezerTableMeta { 73 _, err := file.Seek(0, io.SeekStart) 74 if err != nil { 75 return nil 76 } 77 type obj struct { 78 Version uint16 79 Tail uint64 80 } 81 var o obj 82 if err := rlp.Decode(file, &o); err != nil { 83 return nil 84 } 85 if o.Version != freezerTableV1 { 86 return nil 87 } 88 return &freezerTableMeta{ 89 file: file, 90 version: o.Version, 91 virtualTail: o.Tail, 92 } 93 } 94 95 // decodeV2 attempts to decode the metadata structure in v2 format. If fails or 96 // the result is incompatible, nil is returned. 97 func decodeV2(file *os.File) *freezerTableMeta { 98 _, err := file.Seek(0, io.SeekStart) 99 if err != nil { 100 return nil 101 } 102 type obj struct { 103 Version uint16 104 Tail uint64 105 Offset uint64 106 } 107 var o obj 108 if err := rlp.Decode(file, &o); err != nil { 109 return nil 110 } 111 if o.Version != freezerTableV2 { 112 return nil 113 } 114 if o.Offset > math.MaxInt64 { 115 log.Error("Invalid flushOffset %d in freezer metadata", o.Offset, "file", file.Name()) 116 return nil 117 } 118 return &freezerTableMeta{ 119 file: file, 120 version: freezerTableV2, 121 virtualTail: o.Tail, 122 flushOffset: int64(o.Offset), 123 } 124 } 125 126 // newMetadata initializes the metadata object, either by loading it from the file 127 // or by constructing a new one from scratch. 128 func newMetadata(file *os.File) (*freezerTableMeta, error) { 129 stat, err := file.Stat() 130 if err != nil { 131 return nil, err 132 } 133 if stat.Size() == 0 { 134 m := &freezerTableMeta{ 135 file: file, 136 version: freezerTableV2, 137 virtualTail: 0, 138 flushOffset: 0, 139 } 140 if err := m.write(true); err != nil { 141 return nil, err 142 } 143 return m, nil 144 } 145 if m := decodeV2(file); m != nil { 146 return m, nil 147 } 148 if m := decodeV1(file); m != nil { 149 return m, nil // legacy metadata 150 } 151 return nil, errors.New("failed to decode metadata") 152 } 153 154 // setVirtualTail sets the virtual tail and flushes the metadata if sync is true. 155 func (m *freezerTableMeta) setVirtualTail(tail uint64, sync bool) error { 156 m.virtualTail = tail 157 return m.write(sync) 158 } 159 160 // setFlushOffset sets the flush offset and flushes the metadata if sync is true. 161 func (m *freezerTableMeta) setFlushOffset(offset int64, sync bool) error { 162 m.flushOffset = offset 163 return m.write(sync) 164 } 165 166 // write flushes the content of metadata into file and performs a fsync if required. 167 func (m *freezerTableMeta) write(sync bool) error { 168 type obj struct { 169 Version uint16 170 Tail uint64 171 Offset uint64 172 } 173 var o obj 174 o.Version = freezerVersion // forcibly use the current version 175 o.Tail = m.virtualTail 176 o.Offset = uint64(m.flushOffset) 177 178 _, err := m.file.Seek(0, io.SeekStart) 179 if err != nil { 180 return err 181 } 182 if err := rlp.Encode(m.file, &o); err != nil { 183 return err 184 } 185 if !sync { 186 return nil 187 } 188 return m.file.Sync() 189 }