github.com/bcskill/bcschain/v3@v3.4.9-beta2/ethdb/table.go (about) 1 package ethdb 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "os" 8 "path/filepath" 9 "sort" 10 "sync" 11 "time" 12 13 "github.com/bcskill/bcschain/v3/common" 14 "github.com/bcskill/bcschain/v3/log" 15 ) 16 17 // Table represents key/value storage for a particular data type. 18 // Contains zero or more segments that are separated by partitioner. 19 type Table struct { 20 mu sync.RWMutex 21 active string // active segment name 22 ldbSegments map[string]*LDBSegment // writable segments 23 segments *SegmentSet // all segments 24 25 Name string 26 Path string 27 Partitioner Partitioner 28 29 MinMutableSegmentCount int 30 MinCompactionAge time.Duration 31 32 // Maximum number of segments that can be opened at once. 33 MaxOpenSegmentCount int 34 35 SegmentOpener SegmentOpener 36 SegmentCompactor SegmentCompactor 37 } 38 39 // NewTable returns a new instance of Table. 40 func NewTable(name, path string, partitioner Partitioner) *Table { 41 return &Table{ 42 Name: name, 43 Path: path, 44 Partitioner: partitioner, 45 46 MinMutableSegmentCount: DefaultMinMutableSegmentCount, 47 MinCompactionAge: DefaultMinCompactionAge, 48 MaxOpenSegmentCount: DefaultMaxOpenSegmentCount, 49 50 SegmentOpener: NewFileSegmentOpener(), 51 SegmentCompactor: NewFileSegmentCompactor(), 52 } 53 } 54 55 // Open initializes the table and all existing segments. 56 func (t *Table) Open() error { 57 t.ldbSegments = make(map[string]*LDBSegment) 58 t.segments = NewSegmentSet(t.MaxOpenSegmentCount) 59 60 if err := os.MkdirAll(t.Path, 0777); err != nil { 61 return err 62 } 63 64 names, err := t.SegmentOpener.ListSegmentNames(t.Path, t.Name) 65 if err != nil { 66 log.Error("Cannot list segment names", "path", t.Path, "name", t.Name, "err", err) 67 return err 68 } 69 70 for _, name := range names { 71 path := filepath.Join(t.Path, name) 72 73 // Determine the segment file type. 74 typ, err := SegmentFileType(path) 75 if err == ErrInvalidSegmentType { 76 log.Warn("Invalid segment type, skipping", "path", path, "name", name, "err", err) 77 continue 78 } else if err != nil && !os.IsNotExist(err) { 79 return err 80 } 81 82 // Open appropriate segment type. 83 switch typ { 84 case SegmentLDB1: 85 ldbSegment := NewLDBSegment(name, path) 86 if err := ldbSegment.Open(); err != nil { 87 log.Error("Cannot open ldb segment", "path", path, "name", name, "err", err) 88 t.Close() 89 return err 90 } 91 t.ldbSegments[name] = ldbSegment 92 93 default: 94 segment, err := t.SegmentOpener.OpenSegment(t.Name, name, path) 95 if err == ErrSegmentTypeUnknown { 96 log.Info("unknown segment type, skipping", "filename", name) 97 continue 98 } else if err != nil { 99 log.Error("Cannot open ethdb segment", "path", path, "table", t.Name, "name", name, "err", err) 100 return err 101 } 102 t.segments.Add(segment) 103 } 104 105 // Set as active if it has the highest lexicographical name. 106 if name > t.active { 107 t.active = name 108 } 109 } 110 return nil 111 } 112 113 // Close closes all segments within the table. 114 func (t *Table) Close() error { 115 for _, segment := range t.ldbSegments { 116 if err := segment.Close(); err != nil { 117 log.Error("Failed to close leveldb segment", "path", segment.Path(), "name", segment.Name(), "error", err) 118 } 119 } 120 if t.segments != nil { 121 t.segments.Close() 122 } 123 return nil 124 } 125 126 // ActiveSegmentName the name of the current active segment. 127 func (t *Table) ActiveSegmentName() string { 128 t.mu.RLock() 129 defer t.mu.RUnlock() 130 return t.active 131 } 132 133 // ActiveSegment returns the active segment. 134 func (t *Table) ActiveSegment() MutableSegment { 135 t.mu.RLock() 136 defer t.mu.RUnlock() 137 return t.ldbSegments[t.active] 138 } 139 140 // SegmentPath returns the path of the named segment. 141 func (t *Table) SegmentPath(name string) string { 142 return filepath.Join(t.Path, name) 143 } 144 145 // AcquireSegment returns a segment by name. Returns nil if segment does not exist. 146 // Must call ReleaseSegment when finished with the segment. 147 func (t *Table) AcquireSegment(name string) (Segment, error) { 148 t.mu.RLock() 149 defer t.mu.RUnlock() 150 151 if s := t.ldbSegments[name]; s != nil { 152 return s, nil 153 } 154 return t.segments.Acquire(name) 155 } 156 157 // ReleaseSegment releases a given segment. 158 func (t *Table) ReleaseSegment(s Segment) { 159 switch s.(type) { 160 case *LDBSegment: 161 return 162 default: 163 t.segments.Release() 164 } 165 } 166 167 // SegmentNames a sorted list of all segments names. 168 func (t *Table) SegmentNames() []string { 169 t.mu.RLock() 170 defer t.mu.RUnlock() 171 172 a := make([]string, 0, len(t.ldbSegments)+t.segments.Len()) 173 for _, s := range t.ldbSegments { 174 a = append(a, s.Name()) 175 } 176 for _, s := range t.segments.Slice() { 177 a = append(a, s.Name()) 178 } 179 sort.Strings(a) 180 return a 181 } 182 183 // SegmentsSlice returns a sorted slice of all segments. 184 func (t *Table) SegmentSlice() []Segment { 185 t.mu.RLock() 186 defer t.mu.RUnlock() 187 return t.segmentSlice() 188 } 189 190 func (t *Table) segmentSlice() []Segment { 191 a := make([]Segment, 0, len(t.ldbSegments)+t.segments.Len()) 192 for _, s := range t.ldbSegments { 193 a = append(a, s) 194 } 195 for _, s := range t.segments.Slice() { 196 a = append(a, s) 197 } 198 SortSegments(a) 199 return a 200 } 201 202 func (t *Table) ldbSegmentSlice() []*LDBSegment { 203 a := make([]*LDBSegment, 0, len(t.ldbSegments)) 204 for _, s := range t.ldbSegments { 205 a = append(a, s) 206 } 207 sort.Slice(a, func(i, j int) bool { return a[i].Name() < a[j].Name() }) 208 return a 209 } 210 211 // CreateSegmentIfNotExists returns a mutable segment by name. 212 // Creates a new segment if it does not exist. 213 func (t *Table) CreateSegmentIfNotExists(ctx context.Context, name string) (*LDBSegment, error) { 214 t.mu.RLock() 215 if s := t.ldbSegments[name]; s != nil { 216 t.mu.RUnlock() 217 return s, nil 218 } 219 t.mu.RUnlock() 220 221 t.mu.Lock() 222 defer t.mu.Unlock() 223 224 // Recheck under write lock. 225 if s := t.ldbSegments[name]; s != nil { 226 return s, nil 227 } 228 229 // Uncompact segment if it has already become compacted. 230 if t.segments.Contains(name) { 231 return t.uncompact(ctx, name) 232 } 233 234 // Ensure segment name can become active. 235 if name < t.active { 236 log.Error("cannot new non-active create segment", "name", name, "active", t.active) 237 return nil, ErrImmutableSegment 238 } 239 240 // Create new mutable segment. 241 ldbSegment := NewLDBSegment(name, t.SegmentPath(name)) 242 if err := ldbSegment.Open(); err != nil { 243 return nil, err 244 } 245 t.ldbSegments[name] = ldbSegment 246 247 // Set as active segment. 248 t.active = name 249 250 // Compact under lock. 251 // TODO(benbjohnson): Run compaction in background if too slow. 252 if err := t.compact(ctx); err != nil { 253 return nil, err 254 } 255 256 return ldbSegment, nil 257 } 258 259 // Has returns true if key exists in the table. 260 func (t *Table) Has(key []byte) (bool, error) { 261 name := t.Partitioner.Partition(key) 262 s, err := t.AcquireSegment(name) 263 if err != nil { 264 return false, err 265 } else if s == nil { 266 return false, common.ErrNotFound 267 } 268 defer t.ReleaseSegment(s) 269 return s.Has(key) 270 } 271 272 // Get returns the value associated with key. 273 func (t *Table) Get(key []byte) ([]byte, error) { 274 name := t.Partitioner.Partition(key) 275 s, err := t.AcquireSegment(name) 276 if err != nil { 277 return nil, err 278 } else if s == nil { 279 return nil, nil 280 } 281 defer t.ReleaseSegment(s) 282 return s.Get(key) 283 } 284 285 // Put associates a value with key. 286 func (t *Table) Put(key, value []byte) error { 287 // Ignore if value is the same. 288 if v, err := t.Get(key); err != nil && err != common.ErrNotFound { 289 return err 290 } else if bytes.Equal(v, value) { 291 return nil 292 } 293 294 s, err := t.CreateSegmentIfNotExists(context.TODO(), t.Partitioner.Partition(key)) 295 if err != nil { 296 return err 297 } 298 return s.Put(key, value) 299 } 300 301 // Delete removes key from the database. 302 func (t *Table) Delete(key []byte) error { 303 s, err := t.AcquireSegment(t.Partitioner.Partition(key)) 304 if err != nil { 305 return err 306 } else if s == nil { 307 return nil 308 } 309 defer t.ReleaseSegment(s) 310 311 switch s := s.(type) { 312 case MutableSegment: 313 return s.Delete(key) 314 default: 315 return ErrImmutableSegment 316 } 317 } 318 319 func (t *Table) NewBatch() common.Batch { 320 return &tableBatch{table: t, batches: make(map[string]*ldbSegmentBatch)} 321 } 322 323 // Compact converts LDB segments into immutable file segments. 324 func (t *Table) Compact(ctx context.Context) error { 325 t.mu.Lock() 326 defer t.mu.Unlock() 327 return t.compact(ctx) 328 } 329 330 func (t *Table) compact(ctx context.Context) error { 331 // Retrieve LDB segments. Exit if too few mutable segments. 332 ldbSegmentSlice := t.ldbSegmentSlice() 333 if len(ldbSegmentSlice) < t.MinMutableSegmentCount { 334 return nil 335 } 336 337 for _, ldbSegment := range ldbSegmentSlice[:len(ldbSegmentSlice)-t.MinMutableSegmentCount] { 338 startTime := time.Now() 339 340 if fi, err := os.Stat(ldbSegment.Path()); err != nil { 341 return err 342 } else if time.Since(fi.ModTime()) < t.MinCompactionAge { 343 log.Debug("LDB segment too young, skipping compaction", "table", t.Name, "name", ldbSegment.Name(), "elapsed", time.Since(startTime)) 344 continue 345 } 346 347 newSegment, err := t.SegmentCompactor.CompactSegment(ctx, t.Name, ldbSegment) 348 if err != nil { 349 return err 350 } 351 t.segments.Add(newSegment) 352 delete(t.ldbSegments, ldbSegment.Name()) 353 354 log.Info("Compacted segment", "table", t.Name, "name", ldbSegment.Name(), "elapsed", time.Since(startTime)) 355 } 356 return nil 357 } 358 359 // uncompact converts an immutable segment to a mutable segment. 360 func (t *Table) uncompact(ctx context.Context, name string) (*LDBSegment, error) { 361 startTime := time.Now() 362 363 s, err := t.segments.Acquire(name) 364 if err != nil { 365 return nil, err 366 } 367 defer t.segments.Release() 368 369 ldbSegment, err := t.SegmentCompactor.UncompactSegment(ctx, t.Name, s) 370 if err != nil { 371 return nil, err 372 } 373 t.ldbSegments[name] = ldbSegment 374 t.segments.Remove(ctx, name) 375 376 log.Info("Uncompacted segment", "table", t.Name, "name", name, "elapsed", time.Since(startTime)) 377 378 return ldbSegment, nil 379 } 380 381 type tableBatch struct { 382 table *Table 383 batches map[string]*ldbSegmentBatch 384 size int 385 } 386 387 func (b *tableBatch) Put(key, value []byte) error { 388 // Ignore if value is the same. 389 if v, err := b.table.Get(key); err != nil && err != common.ErrNotFound { 390 return err 391 } else if bytes.Equal(v, value) { 392 return nil 393 } 394 395 name := b.table.Partitioner.Partition(key) 396 ldbSegment, err := b.table.CreateSegmentIfNotExists(context.TODO(), name) 397 if err != nil { 398 log.Error("tableBatch.Put: error", "table", b.table.Name, "segment", name, "key", fmt.Sprintf("%x", key)) 399 return err 400 } 401 402 sb := b.batches[name] 403 if sb == nil { 404 sb = ldbSegment.newBatch() 405 b.batches[name] = sb 406 } 407 if err := sb.Put(key, value); err != nil { 408 return err 409 } 410 b.size += len(value) 411 return nil 412 } 413 414 func (b *tableBatch) Delete(key []byte) error { 415 // Ignore if value doesn't exist. 416 if _, err := b.table.Get(key); err == common.ErrNotFound { 417 return nil 418 } else if err != nil { 419 return err 420 } 421 422 name := b.table.Partitioner.Partition(key) 423 ldbSegment, err := b.table.CreateSegmentIfNotExists(context.TODO(), name) 424 if err != nil { 425 log.Error("tableBatch.Delete: error", "table", b.table.Name, "segment", name, "key", fmt.Sprintf("%x", key)) 426 return err 427 } 428 429 sb := b.batches[name] 430 if sb == nil { 431 sb = ldbSegment.newBatch() 432 b.batches[name] = sb 433 } 434 if err := sb.Delete(key); err != nil { 435 return err 436 } 437 return nil 438 } 439 440 func (b *tableBatch) Write() error { 441 for _, sb := range b.batches { 442 if err := sb.Write(); err != nil { 443 return err 444 } 445 } 446 return nil 447 } 448 449 func (b *tableBatch) ValueSize() int { 450 return b.size 451 } 452 453 func (b *tableBatch) Reset() { 454 for _, sb := range b.batches { 455 sb.Reset() 456 } 457 b.size = 0 458 }