github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/integration/generate/writer.go (about) 1 // Copyright (c) 2016 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package generate 22 23 import ( 24 "time" 25 26 "github.com/m3db/m3/src/dbnode/encoding" 27 ns "github.com/m3db/m3/src/dbnode/namespace" 28 "github.com/m3db/m3/src/dbnode/persist" 29 "github.com/m3db/m3/src/dbnode/persist/fs" 30 "github.com/m3db/m3/src/dbnode/sharding" 31 "github.com/m3db/m3/src/x/checked" 32 "github.com/m3db/m3/src/x/context" 33 xtime "github.com/m3db/m3/src/x/time" 34 ) 35 36 type writer struct { 37 opts Options 38 } 39 40 // WriteAllPredicate writes all datapoints 41 func WriteAllPredicate(_ TestValue) bool { 42 return true 43 } 44 45 // NewWriter returns a new writer 46 func NewWriter(opts Options) Writer { 47 return &writer{ 48 opts: opts, 49 } 50 } 51 52 func (w *writer) WriteData( 53 nsCtx ns.Context, 54 shardSet sharding.ShardSet, 55 seriesMaps SeriesBlocksByStart, 56 volume int, 57 ) error { 58 return w.WriteDataWithPredicate(nsCtx, shardSet, seriesMaps, volume, WriteAllPredicate) 59 } 60 61 func (w *writer) WriteSnapshot( 62 nsCtx ns.Context, 63 shardSet sharding.ShardSet, 64 seriesMaps SeriesBlocksByStart, 65 volume int, 66 snapshotInterval time.Duration, 67 ) error { 68 return w.WriteSnapshotWithPredicate( 69 nsCtx, shardSet, seriesMaps, volume, WriteAllPredicate, snapshotInterval) 70 } 71 72 func (w *writer) WriteDataWithPredicate( 73 nsCtx ns.Context, 74 shardSet sharding.ShardSet, 75 seriesMaps SeriesBlocksByStart, 76 volume int, 77 pred WriteDatapointPredicate, 78 ) error { 79 return w.writeWithPredicate( 80 nsCtx, shardSet, seriesMaps, volume, pred, persist.FileSetFlushType, 0) 81 } 82 83 func (w *writer) WriteSnapshotWithPredicate( 84 nsCtx ns.Context, 85 shardSet sharding.ShardSet, 86 seriesMaps SeriesBlocksByStart, 87 volume int, 88 pred WriteDatapointPredicate, 89 snapshotInterval time.Duration, 90 ) error { 91 return w.writeWithPredicate( 92 nsCtx, shardSet, seriesMaps, volume, pred, persist.FileSetSnapshotType, snapshotInterval) 93 } 94 95 func (w *writer) writeWithPredicate( 96 nsCtx ns.Context, 97 shardSet sharding.ShardSet, 98 seriesMaps SeriesBlocksByStart, 99 volume int, 100 pred WriteDatapointPredicate, 101 fileSetType persist.FileSetType, 102 snapshotInterval time.Duration, 103 ) error { 104 var ( 105 gOpts = w.opts 106 blockSize = gOpts.BlockSize() 107 currStart = gOpts.ClockOptions().NowFn()().Truncate(blockSize) 108 retentionStart = currStart.Add(-gOpts.RetentionPeriod()) 109 isValidStart = func(start time.Time) bool { 110 return start.Equal(retentionStart) || start.After(retentionStart) 111 } 112 starts = make(map[xtime.UnixNano]struct{}) 113 ) 114 115 for start := currStart; isValidStart(start); start = start.Add(-blockSize) { 116 starts[xtime.ToUnixNano(start)] = struct{}{} 117 } 118 119 writer, err := fs.NewWriter(fs.NewOptions(). 120 SetFilePathPrefix(gOpts.FilePathPrefix()). 121 SetWriterBufferSize(gOpts.WriterBufferSize()). 122 SetNewFileMode(gOpts.NewFileMode()). 123 SetNewDirectoryMode(gOpts.NewDirectoryMode())) 124 if err != nil { 125 return err 126 } 127 encoder := gOpts.EncoderPool().Get() 128 encoder.SetSchema(nsCtx.Schema) 129 for start, data := range seriesMaps { 130 err := writeToDiskWithPredicate( 131 writer, shardSet, encoder, start, nsCtx, blockSize, 132 data, volume, pred, fileSetType, snapshotInterval) 133 if err != nil { 134 return err 135 } 136 delete(starts, start) 137 } 138 139 // Write remaining files even for empty start periods to avoid unfulfilled ranges 140 if w.opts.WriteEmptyShards() { 141 for start := range starts { 142 err := writeToDiskWithPredicate( 143 writer, shardSet, encoder, start, nsCtx, blockSize, 144 nil, volume, pred, fileSetType, snapshotInterval) 145 if err != nil { 146 return err 147 } 148 } 149 } 150 151 return nil 152 } 153 154 func writeToDiskWithPredicate( 155 writer fs.DataFileSetWriter, 156 shardSet sharding.ShardSet, 157 encoder encoding.Encoder, 158 start xtime.UnixNano, 159 nsCtx ns.Context, 160 blockSize time.Duration, 161 seriesList SeriesBlock, 162 volume int, 163 pred WriteDatapointPredicate, 164 fileSetType persist.FileSetType, 165 snapshotInterval time.Duration, 166 ) error { 167 seriesPerShard := make(map[uint32][]Series) 168 for _, shard := range shardSet.AllIDs() { 169 // Ensure we write out block files for each shard even if there's no data 170 seriesPerShard[shard] = make([]Series, 0) 171 } 172 for _, s := range seriesList { 173 shard := shardSet.Lookup(s.ID) 174 seriesPerShard[shard] = append(seriesPerShard[shard], s) 175 } 176 data := make([]checked.Bytes, 2) 177 for shard, seriesList := range seriesPerShard { 178 writerOpts := fs.DataWriterOpenOptions{ 179 BlockSize: blockSize, 180 Identifier: fs.FileSetFileIdentifier{ 181 Namespace: nsCtx.ID, 182 Shard: shard, 183 BlockStart: start, 184 VolumeIndex: volume, 185 }, 186 FileSetType: fileSetType, 187 Snapshot: fs.DataWriterSnapshotOptions{ 188 SnapshotTime: start.Add(snapshotInterval), 189 }, 190 } 191 192 if err := writer.Open(writerOpts); err != nil { 193 return err 194 } 195 196 ctx := context.NewBackground() 197 for _, series := range seriesList { 198 encoder.Reset(start, 0, nsCtx.Schema) 199 for _, dp := range series.Data { 200 if !pred(dp) { 201 continue 202 } 203 204 if err := encoder.Encode(dp.Datapoint, xtime.Second, dp.Annotation); err != nil { 205 return err 206 } 207 } 208 209 ctx.Reset() 210 stream, ok := encoder.Stream(ctx) 211 if !ok { 212 // None of the datapoints passed the predicate. 213 continue 214 } 215 segment, err := stream.Segment() 216 if err != nil { 217 return err 218 } 219 data[0] = segment.Head 220 data[1] = segment.Tail 221 checksum := segment.CalculateChecksum() 222 metadata := persist.NewMetadataFromIDAndTags(series.ID, series.Tags, 223 persist.MetadataOptions{}) 224 err = writer.WriteAll(metadata, data, checksum) 225 if err != nil { 226 return err 227 } 228 ctx.BlockingClose() 229 } 230 231 if err := writer.Close(); err != nil { 232 return err 233 } 234 } 235 236 return nil 237 }