1 package bbolt
2
3 import (
4 "errors"
5 "fmt"
6 "hash/fnv"
7 "io"
8 "os"
9 "runtime"
10 "sort"
11 "sync"
12 "time"
13 "unsafe"
14 )
15
16
17 const maxMmapStep = 1 << 30
18
19
20 const version = 2
21
22
23 const magic uint32 = 0xED0CDAED
24
25 const pgidNoFreelist pgid = 0xffffffffffffffff
26
27
28
29
30
31 const IgnoreNoSync = runtime.GOOS == "openbsd"
32
33
34 const (
35 DefaultMaxBatchSize int = 1000
36 DefaultMaxBatchDelay = 10 * time.Millisecond
37 DefaultAllocSize = 16 * 1024 * 1024
38 )
39
40
41 var defaultPageSize = os.Getpagesize()
42
43
44 const flockRetryTimeout = 50 * time.Millisecond
45
46
47 type FreelistType string
48
49 const (
50
51 FreelistArrayType = FreelistType("array")
52
53 FreelistMapType = FreelistType("hashmap")
54 )
55
56
57
58
59 type DB struct {
60
61
62
63
64 stats Stats
65
66
67
68
69
70 StrictMode bool
71
72
73
74
75
76
77
78
79
80
81
82 NoSync bool
83
84
85
86
87 NoFreelistSync bool
88
89
90
91
92
93
94 FreelistType FreelistType
95
96
97
98
99
100
101
102 NoGrowSync bool
103
104
105
106
107 PreLoadFreelist bool
108
109
110
111 MmapFlags int
112
113
114
115
116
117
118
119 MaxBatchSize int
120
121
122
123
124
125
126
127 MaxBatchDelay time.Duration
128
129
130
131
132 AllocSize int
133
134
135
136
137
138 Mlock bool
139
140 path string
141 openFile func(string, int, os.FileMode) (*os.File, error)
142 file *os.File
143
144
145
146 dataref []byte
147 data *[maxMapSize]byte
148 datasz int
149 filesz int
150 meta0 *meta
151 meta1 *meta
152 pageSize int
153 opened bool
154 rwtx *Tx
155 txs []*Tx
156
157 freelist *freelist
158 freelistLoad sync.Once
159
160 pagePool sync.Pool
161
162 batchMu sync.Mutex
163 batch *batch
164
165 rwlock sync.Mutex
166 metalock sync.Mutex
167 mmaplock sync.RWMutex
168 statlock sync.RWMutex
169
170 ops struct {
171 writeAt func(b []byte, off int64) (n int, err error)
172 }
173
174
175
176 readOnly bool
177 }
178
179
180 func (db *DB) Path() string {
181 return db.path
182 }
183
184
185 func (db *DB) GoString() string {
186 return fmt.Sprintf("bolt.DB{path:%q}", db.path)
187 }
188
189
190 func (db *DB) String() string {
191 return fmt.Sprintf("DB<%q>", db.path)
192 }
193
194
195
196
197 func Open(path string, mode os.FileMode, options *Options) (*DB, error) {
198 db := &DB{
199 opened: true,
200 }
201
202 if options == nil {
203 options = DefaultOptions
204 }
205 db.NoSync = options.NoSync
206 db.NoGrowSync = options.NoGrowSync
207 db.MmapFlags = options.MmapFlags
208 db.NoFreelistSync = options.NoFreelistSync
209 db.PreLoadFreelist = options.PreLoadFreelist
210 db.FreelistType = options.FreelistType
211 db.Mlock = options.Mlock
212
213
214 db.MaxBatchSize = DefaultMaxBatchSize
215 db.MaxBatchDelay = DefaultMaxBatchDelay
216 db.AllocSize = DefaultAllocSize
217
218 flag := os.O_RDWR
219 if options.ReadOnly {
220 flag = os.O_RDONLY
221 db.readOnly = true
222 } else {
223
224 db.PreLoadFreelist = true
225 }
226
227 db.openFile = options.OpenFile
228 if db.openFile == nil {
229 db.openFile = os.OpenFile
230 }
231
232
233 var err error
234 if db.file, err = db.openFile(path, flag|os.O_CREATE, mode); err != nil {
235 _ = db.close()
236 return nil, err
237 }
238 db.path = db.file.Name()
239
240
241
242
243
244
245
246
247 if err := flock(db, !db.readOnly, options.Timeout); err != nil {
248 _ = db.close()
249 return nil, err
250 }
251
252
253 db.ops.writeAt = db.file.WriteAt
254
255 if db.pageSize = options.PageSize; db.pageSize == 0 {
256
257 db.pageSize = defaultPageSize
258 }
259
260
261 if info, err := db.file.Stat(); err != nil {
262 _ = db.close()
263 return nil, err
264 } else if info.Size() == 0 {
265
266 if err := db.init(); err != nil {
267
268 _ = db.close()
269 return nil, err
270 }
271 } else {
272
273 if pgSize, err := db.getPageSize(); err == nil {
274 db.pageSize = pgSize
275 } else {
276 _ = db.close()
277 return nil, ErrInvalid
278 }
279 }
280
281
282 db.pagePool = sync.Pool{
283 New: func() interface{} {
284 return make([]byte, db.pageSize)
285 },
286 }
287
288
289 if err := db.mmap(options.InitialMmapSize); err != nil {
290 _ = db.close()
291 return nil, err
292 }
293
294 if db.PreLoadFreelist {
295 db.loadFreelist()
296 }
297
298 if db.readOnly {
299 return db, nil
300 }
301
302
303
304 if !db.NoFreelistSync && !db.hasSyncedFreelist() {
305 tx, err := db.Begin(true)
306 if tx != nil {
307 err = tx.Commit()
308 }
309 if err != nil {
310 _ = db.close()
311 return nil, err
312 }
313 }
314
315
316 return db, nil
317 }
318
319
320
321
322 func (db *DB) getPageSize() (int, error) {
323 var (
324 meta0CanRead, meta1CanRead bool
325 )
326
327
328 if pgSize, canRead, err := db.getPageSizeFromFirstMeta(); err != nil {
329
330 meta0CanRead = canRead
331 } else {
332 return pgSize, nil
333 }
334
335
336 if pgSize, canRead, err := db.getPageSizeFromSecondMeta(); err != nil {
337
338 meta1CanRead = canRead
339 } else {
340 return pgSize, nil
341 }
342
343
344
345
346
347
348
349
350
351 if meta0CanRead || meta1CanRead {
352 return db.pageSize, nil
353 }
354
355 return 0, ErrInvalid
356 }
357
358
359 func (db *DB) getPageSizeFromFirstMeta() (int, bool, error) {
360 var buf [0x1000]byte
361 var metaCanRead bool
362 if bw, err := db.file.ReadAt(buf[:], 0); err == nil && bw == len(buf) {
363 metaCanRead = true
364 if m := db.pageInBuffer(buf[:], 0).meta(); m.validate() == nil {
365 return int(m.pageSize), metaCanRead, nil
366 }
367 }
368 return 0, metaCanRead, ErrInvalid
369 }
370
371
372 func (db *DB) getPageSizeFromSecondMeta() (int, bool, error) {
373 var (
374 fileSize int64
375 metaCanRead bool
376 )
377
378
379 if info, err := db.file.Stat(); err != nil {
380 return 0, metaCanRead, err
381 } else {
382 fileSize = info.Size()
383 }
384
385
386
387
388
389
390
391 for i := 0; i <= 14; i++ {
392 var buf [0x1000]byte
393 var pos int64 = 1024 << uint(i)
394 if pos >= fileSize-1024 {
395 break
396 }
397 bw, err := db.file.ReadAt(buf[:], pos)
398 if (err == nil && bw == len(buf)) || (err == io.EOF && int64(bw) == (fileSize-pos)) {
399 metaCanRead = true
400 if m := db.pageInBuffer(buf[:], 0).meta(); m.validate() == nil {
401 return int(m.pageSize), metaCanRead, nil
402 }
403 }
404 }
405
406 return 0, metaCanRead, ErrInvalid
407 }
408
409
410
411
412 func (db *DB) loadFreelist() {
413 db.freelistLoad.Do(func() {
414 db.freelist = newFreelist(db.FreelistType)
415 if !db.hasSyncedFreelist() {
416
417 db.freelist.readIDs(db.freepages())
418 } else {
419
420 db.freelist.read(db.page(db.meta().freelist))
421 }
422 db.stats.FreePageN = db.freelist.free_count()
423 })
424 }
425
426 func (db *DB) hasSyncedFreelist() bool {
427 return db.meta().freelist != pgidNoFreelist
428 }
429
430
431
432 func (db *DB) mmap(minsz int) (err error) {
433 db.mmaplock.Lock()
434 defer db.mmaplock.Unlock()
435
436 info, err := db.file.Stat()
437 if err != nil {
438 return fmt.Errorf("mmap stat error: %s", err)
439 } else if int(info.Size()) < db.pageSize*2 {
440 return fmt.Errorf("file size too small")
441 }
442
443
444 fileSize := int(info.Size())
445 var size = fileSize
446 if size < minsz {
447 size = minsz
448 }
449 size, err = db.mmapSize(size)
450 if err != nil {
451 return err
452 }
453
454 if db.Mlock {
455
456 if err := db.munlock(fileSize); err != nil {
457 return err
458 }
459 }
460
461
462 if db.rwtx != nil {
463 db.rwtx.root.dereference()
464 }
465
466
467 if err = db.munmap(); err != nil {
468 return err
469 }
470
471
472
473
474 if err = mmap(db, size); err != nil {
475 return err
476 }
477
478
479
480 defer func() {
481 if err != nil {
482 if unmapErr := db.munmap(); unmapErr != nil {
483 err = fmt.Errorf("%w; rollback unmap also failed: %v", err, unmapErr)
484 }
485 }
486 }()
487
488 if db.Mlock {
489
490 if err := db.mlock(fileSize); err != nil {
491 return err
492 }
493 }
494
495
496 db.meta0 = db.page(0).meta()
497 db.meta1 = db.page(1).meta()
498
499
500
501
502 err0 := db.meta0.validate()
503 err1 := db.meta1.validate()
504 if err0 != nil && err1 != nil {
505 return err0
506 }
507
508 return nil
509 }
510
511 func (db *DB) invalidate() {
512 db.dataref = nil
513 db.data = nil
514 db.datasz = 0
515
516 db.meta0 = nil
517 db.meta1 = nil
518 }
519
520
521 func (db *DB) munmap() error {
522 defer db.invalidate()
523
524
525
526 if err := munmap(db); err != nil {
527 return fmt.Errorf("unmap error: " + err.Error())
528 }
529
530 return nil
531 }
532
533
534
535
536 func (db *DB) mmapSize(size int) (int, error) {
537
538 for i := uint(15); i <= 30; i++ {
539 if size <= 1<<i {
540 return 1 << i, nil
541 }
542 }
543
544
545 if size > maxMapSize {
546 return 0, fmt.Errorf("mmap too large")
547 }
548
549
550 sz := int64(size)
551 if remainder := sz % int64(maxMmapStep); remainder > 0 {
552 sz += int64(maxMmapStep) - remainder
553 }
554
555
556
557 pageSize := int64(db.pageSize)
558 if (sz % pageSize) != 0 {
559 sz = ((sz / pageSize) + 1) * pageSize
560 }
561
562
563 if sz > maxMapSize {
564 sz = maxMapSize
565 }
566
567 return int(sz), nil
568 }
569
570 func (db *DB) munlock(fileSize int) error {
571
572
573 if err := munlock(db, fileSize); err != nil {
574 return fmt.Errorf("munlock error: " + err.Error())
575 }
576 return nil
577 }
578
579 func (db *DB) mlock(fileSize int) error {
580
581
582 if err := mlock(db, fileSize); err != nil {
583 return fmt.Errorf("mlock error: " + err.Error())
584 }
585 return nil
586 }
587
588 func (db *DB) mrelock(fileSizeFrom, fileSizeTo int) error {
589 if err := db.munlock(fileSizeFrom); err != nil {
590 return err
591 }
592 if err := db.mlock(fileSizeTo); err != nil {
593 return err
594 }
595 return nil
596 }
597
598
599 func (db *DB) init() error {
600
601 buf := make([]byte, db.pageSize*4)
602 for i := 0; i < 2; i++ {
603 p := db.pageInBuffer(buf, pgid(i))
604 p.id = pgid(i)
605 p.flags = metaPageFlag
606
607
608 m := p.meta()
609 m.magic = magic
610 m.version = version
611 m.pageSize = uint32(db.pageSize)
612 m.freelist = 2
613 m.root = bucket{root: 3}
614 m.pgid = 4
615 m.txid = txid(i)
616 m.checksum = m.sum64()
617 }
618
619
620 p := db.pageInBuffer(buf, pgid(2))
621 p.id = pgid(2)
622 p.flags = freelistPageFlag
623 p.count = 0
624
625
626 p = db.pageInBuffer(buf, pgid(3))
627 p.id = pgid(3)
628 p.flags = leafPageFlag
629 p.count = 0
630
631
632 if _, err := db.ops.writeAt(buf, 0); err != nil {
633 return err
634 }
635 if err := fdatasync(db); err != nil {
636 return err
637 }
638 db.filesz = len(buf)
639
640 return nil
641 }
642
643
644
645
646 func (db *DB) Close() error {
647 db.rwlock.Lock()
648 defer db.rwlock.Unlock()
649
650 db.metalock.Lock()
651 defer db.metalock.Unlock()
652
653 db.mmaplock.Lock()
654 defer db.mmaplock.Unlock()
655
656 return db.close()
657 }
658
659 func (db *DB) close() error {
660 if !db.opened {
661 return nil
662 }
663
664 db.opened = false
665
666 db.freelist = nil
667
668
669 db.ops.writeAt = nil
670
671 var errs []error
672
673 if err := db.munmap(); err != nil {
674 errs = append(errs, err)
675 }
676
677
678 if db.file != nil {
679
680 if !db.readOnly {
681
682 if err := funlock(db); err != nil {
683 errs = append(errs, fmt.Errorf("bolt.Close(): funlock error: %w", err))
684 }
685 }
686
687
688 if err := db.file.Close(); err != nil {
689 errs = append(errs, fmt.Errorf("db file close: %w", err))
690 }
691 db.file = nil
692 }
693
694 db.path = ""
695
696 if len(errs) > 0 {
697 return errs[0]
698 }
699 return nil
700 }
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719 func (db *DB) Begin(writable bool) (*Tx, error) {
720 if writable {
721 return db.beginRWTx()
722 }
723 return db.beginTx()
724 }
725
726 func (db *DB) beginTx() (*Tx, error) {
727
728
729
730 db.metalock.Lock()
731
732
733
734
735 db.mmaplock.RLock()
736
737
738 if !db.opened {
739 db.mmaplock.RUnlock()
740 db.metalock.Unlock()
741 return nil, ErrDatabaseNotOpen
742 }
743
744
745 if db.data == nil {
746 db.mmaplock.RUnlock()
747 db.metalock.Unlock()
748 return nil, ErrInvalidMapping
749 }
750
751
752 t := &Tx{}
753 t.init(db)
754
755
756 db.txs = append(db.txs, t)
757 n := len(db.txs)
758
759
760 db.metalock.Unlock()
761
762
763 db.statlock.Lock()
764 db.stats.TxN++
765 db.stats.OpenTxN = n
766 db.statlock.Unlock()
767
768 return t, nil
769 }
770
771 func (db *DB) beginRWTx() (*Tx, error) {
772
773 if db.readOnly {
774 return nil, ErrDatabaseReadOnly
775 }
776
777
778
779 db.rwlock.Lock()
780
781
782
783 db.metalock.Lock()
784 defer db.metalock.Unlock()
785
786
787 if !db.opened {
788 db.rwlock.Unlock()
789 return nil, ErrDatabaseNotOpen
790 }
791
792
793 if db.data == nil {
794 db.rwlock.Unlock()
795 return nil, ErrInvalidMapping
796 }
797
798
799 t := &Tx{writable: true}
800 t.init(db)
801 db.rwtx = t
802 db.freePages()
803 return t, nil
804 }
805
806
807 func (db *DB) freePages() {
808
809 sort.Sort(txsById(db.txs))
810 minid := txid(0xFFFFFFFFFFFFFFFF)
811 if len(db.txs) > 0 {
812 minid = db.txs[0].meta.txid
813 }
814 if minid > 0 {
815 db.freelist.release(minid - 1)
816 }
817
818 for _, t := range db.txs {
819 db.freelist.releaseRange(minid, t.meta.txid-1)
820 minid = t.meta.txid + 1
821 }
822 db.freelist.releaseRange(minid, txid(0xFFFFFFFFFFFFFFFF))
823
824 }
825
826 type txsById []*Tx
827
828 func (t txsById) Len() int { return len(t) }
829 func (t txsById) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
830 func (t txsById) Less(i, j int) bool { return t[i].meta.txid < t[j].meta.txid }
831
832
833 func (db *DB) removeTx(tx *Tx) {
834
835 db.mmaplock.RUnlock()
836
837
838 db.metalock.Lock()
839
840
841 for i, t := range db.txs {
842 if t == tx {
843 last := len(db.txs) - 1
844 db.txs[i] = db.txs[last]
845 db.txs[last] = nil
846 db.txs = db.txs[:last]
847 break
848 }
849 }
850 n := len(db.txs)
851
852
853 db.metalock.Unlock()
854
855
856 db.statlock.Lock()
857 db.stats.OpenTxN = n
858 db.stats.TxStats.add(&tx.stats)
859 db.statlock.Unlock()
860 }
861
862
863
864
865
866
867
868
869 func (db *DB) Update(fn func(*Tx) error) error {
870 t, err := db.Begin(true)
871 if err != nil {
872 return err
873 }
874
875
876 defer func() {
877 if t.db != nil {
878 t.rollback()
879 }
880 }()
881
882
883 t.managed = true
884
885
886 err = fn(t)
887 t.managed = false
888 if err != nil {
889 _ = t.Rollback()
890 return err
891 }
892
893 return t.Commit()
894 }
895
896
897
898
899
900 func (db *DB) View(fn func(*Tx) error) error {
901 t, err := db.Begin(false)
902 if err != nil {
903 return err
904 }
905
906
907 defer func() {
908 if t.db != nil {
909 t.rollback()
910 }
911 }()
912
913
914 t.managed = true
915
916
917 err = fn(t)
918 t.managed = false
919 if err != nil {
920 _ = t.Rollback()
921 return err
922 }
923
924 return t.Rollback()
925 }
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944 func (db *DB) Batch(fn func(*Tx) error) error {
945 errCh := make(chan error, 1)
946
947 db.batchMu.Lock()
948 if (db.batch == nil) || (db.batch != nil && len(db.batch.calls) >= db.MaxBatchSize) {
949
950 db.batch = &batch{
951 db: db,
952 }
953 db.batch.timer = time.AfterFunc(db.MaxBatchDelay, db.batch.trigger)
954 }
955 db.batch.calls = append(db.batch.calls, call{fn: fn, err: errCh})
956 if len(db.batch.calls) >= db.MaxBatchSize {
957
958 go db.batch.trigger()
959 }
960 db.batchMu.Unlock()
961
962 err := <-errCh
963 if err == trySolo {
964 err = db.Update(fn)
965 }
966 return err
967 }
968
969 type call struct {
970 fn func(*Tx) error
971 err chan<- error
972 }
973
974 type batch struct {
975 db *DB
976 timer *time.Timer
977 start sync.Once
978 calls []call
979 }
980
981
982 func (b *batch) trigger() {
983 b.start.Do(b.run)
984 }
985
986
987
988 func (b *batch) run() {
989 b.db.batchMu.Lock()
990 b.timer.Stop()
991
992
993 if b.db.batch == b {
994 b.db.batch = nil
995 }
996 b.db.batchMu.Unlock()
997
998 retry:
999 for len(b.calls) > 0 {
1000 var failIdx = -1
1001 err := b.db.Update(func(tx *Tx) error {
1002 for i, c := range b.calls {
1003 if err := safelyCall(c.fn, tx); err != nil {
1004 failIdx = i
1005 return err
1006 }
1007 }
1008 return nil
1009 })
1010
1011 if failIdx >= 0 {
1012
1013
1014
1015 c := b.calls[failIdx]
1016 b.calls[failIdx], b.calls = b.calls[len(b.calls)-1], b.calls[:len(b.calls)-1]
1017
1018 c.err <- trySolo
1019 continue retry
1020 }
1021
1022
1023 for _, c := range b.calls {
1024 c.err <- err
1025 }
1026 break retry
1027 }
1028 }
1029
1030
1031
1032
1033 var trySolo = errors.New("batch function returned an error and should be re-run solo")
1034
1035 type panicked struct {
1036 reason interface{}
1037 }
1038
1039 func (p panicked) Error() string {
1040 if err, ok := p.reason.(error); ok {
1041 return err.Error()
1042 }
1043 return fmt.Sprintf("panic: %v", p.reason)
1044 }
1045
1046 func safelyCall(fn func(*Tx) error, tx *Tx) (err error) {
1047 defer func() {
1048 if p := recover(); p != nil {
1049 err = panicked{p}
1050 }
1051 }()
1052 return fn(tx)
1053 }
1054
1055
1056
1057
1058
1059 func (db *DB) Sync() error { return fdatasync(db) }
1060
1061
1062
1063 func (db *DB) Stats() Stats {
1064 db.statlock.RLock()
1065 defer db.statlock.RUnlock()
1066 return db.stats
1067 }
1068
1069
1070
1071 func (db *DB) Info() *Info {
1072 _assert(db.data != nil, "database file isn't correctly mapped")
1073 return &Info{uintptr(unsafe.Pointer(&db.data[0])), db.pageSize}
1074 }
1075
1076
1077 func (db *DB) page(id pgid) *page {
1078 pos := id * pgid(db.pageSize)
1079 return (*page)(unsafe.Pointer(&db.data[pos]))
1080 }
1081
1082
1083 func (db *DB) pageInBuffer(b []byte, id pgid) *page {
1084 return (*page)(unsafe.Pointer(&b[id*pgid(db.pageSize)]))
1085 }
1086
1087
1088 func (db *DB) meta() *meta {
1089
1090
1091
1092 metaA := db.meta0
1093 metaB := db.meta1
1094 if db.meta1.txid > db.meta0.txid {
1095 metaA = db.meta1
1096 metaB = db.meta0
1097 }
1098
1099
1100 if err := metaA.validate(); err == nil {
1101 return metaA
1102 } else if err := metaB.validate(); err == nil {
1103 return metaB
1104 }
1105
1106
1107
1108 panic("bolt.DB.meta(): invalid meta pages")
1109 }
1110
1111
1112 func (db *DB) allocate(txid txid, count int) (*page, error) {
1113
1114 var buf []byte
1115 if count == 1 {
1116 buf = db.pagePool.Get().([]byte)
1117 } else {
1118 buf = make([]byte, count*db.pageSize)
1119 }
1120 p := (*page)(unsafe.Pointer(&buf[0]))
1121 p.overflow = uint32(count - 1)
1122
1123
1124 if p.id = db.freelist.allocate(txid, count); p.id != 0 {
1125 return p, nil
1126 }
1127
1128
1129 p.id = db.rwtx.meta.pgid
1130 var minsz = int((p.id+pgid(count))+1) * db.pageSize
1131 if minsz >= db.datasz {
1132 if err := db.mmap(minsz); err != nil {
1133 return nil, fmt.Errorf("mmap allocate error: %s", err)
1134 }
1135 }
1136
1137
1138 db.rwtx.meta.pgid += pgid(count)
1139
1140 return p, nil
1141 }
1142
1143
1144 func (db *DB) grow(sz int) error {
1145
1146 if sz <= db.filesz {
1147 return nil
1148 }
1149
1150
1151
1152 if db.datasz <= db.AllocSize {
1153 sz = db.datasz
1154 } else {
1155 sz += db.AllocSize
1156 }
1157
1158
1159
1160 if !db.NoGrowSync && !db.readOnly {
1161 if runtime.GOOS != "windows" {
1162 if err := db.file.Truncate(int64(sz)); err != nil {
1163 return fmt.Errorf("file resize error: %s", err)
1164 }
1165 }
1166 if err := db.file.Sync(); err != nil {
1167 return fmt.Errorf("file sync error: %s", err)
1168 }
1169 if db.Mlock {
1170
1171 if err := db.mrelock(db.filesz, sz); err != nil {
1172 return fmt.Errorf("mlock/munlock error: %s", err)
1173 }
1174 }
1175 }
1176
1177 db.filesz = sz
1178 return nil
1179 }
1180
1181 func (db *DB) IsReadOnly() bool {
1182 return db.readOnly
1183 }
1184
1185 func (db *DB) freepages() []pgid {
1186 tx, err := db.beginTx()
1187 defer func() {
1188 err = tx.Rollback()
1189 if err != nil {
1190 panic("freepages: failed to rollback tx")
1191 }
1192 }()
1193 if err != nil {
1194 panic("freepages: failed to open read only tx")
1195 }
1196
1197 reachable := make(map[pgid]*page)
1198 nofreed := make(map[pgid]bool)
1199 ech := make(chan error)
1200 go func() {
1201 for e := range ech {
1202 panic(fmt.Sprintf("freepages: failed to get all reachable pages (%v)", e))
1203 }
1204 }()
1205 tx.checkBucket(&tx.root, reachable, nofreed, HexKVStringer(), ech)
1206 close(ech)
1207
1208
1209
1210 var fids []pgid
1211 for i := pgid(2); i < db.meta().pgid; i++ {
1212 if _, ok := reachable[i]; !ok {
1213 fids = append(fids, i)
1214 }
1215 }
1216 return fids
1217 }
1218
1219
1220 type Options struct {
1221
1222
1223
1224 Timeout time.Duration
1225
1226
1227 NoGrowSync bool
1228
1229
1230
1231 NoFreelistSync bool
1232
1233
1234
1235
1236 PreLoadFreelist bool
1237
1238
1239
1240
1241
1242
1243 FreelistType FreelistType
1244
1245
1246
1247 ReadOnly bool
1248
1249
1250 MmapFlags int
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260 InitialMmapSize int
1261
1262
1263 PageSize int
1264
1265
1266
1267
1268 NoSync bool
1269
1270
1271
1272 OpenFile func(string, int, os.FileMode) (*os.File, error)
1273
1274
1275
1276
1277 Mlock bool
1278 }
1279
1280
1281
1282 var DefaultOptions = &Options{
1283 Timeout: 0,
1284 NoGrowSync: false,
1285 FreelistType: FreelistArrayType,
1286 }
1287
1288
1289 type Stats struct {
1290
1291
1292
1293
1294 TxStats TxStats
1295
1296
1297 FreePageN int
1298 PendingPageN int
1299 FreeAlloc int
1300 FreelistInuse int
1301
1302
1303 TxN int
1304 OpenTxN int
1305 }
1306
1307
1308
1309
1310 func (s *Stats) Sub(other *Stats) Stats {
1311 if other == nil {
1312 return *s
1313 }
1314 var diff Stats
1315 diff.FreePageN = s.FreePageN
1316 diff.PendingPageN = s.PendingPageN
1317 diff.FreeAlloc = s.FreeAlloc
1318 diff.FreelistInuse = s.FreelistInuse
1319 diff.TxN = s.TxN - other.TxN
1320 diff.TxStats = s.TxStats.Sub(&other.TxStats)
1321 return diff
1322 }
1323
1324 type Info struct {
1325 Data uintptr
1326 PageSize int
1327 }
1328
1329 type meta struct {
1330 magic uint32
1331 version uint32
1332 pageSize uint32
1333 flags uint32
1334 root bucket
1335 freelist pgid
1336 pgid pgid
1337 txid txid
1338 checksum uint64
1339 }
1340
1341
1342 func (m *meta) validate() error {
1343 if m.magic != magic {
1344 return ErrInvalid
1345 } else if m.version != version {
1346 return ErrVersionMismatch
1347 } else if m.checksum != m.sum64() {
1348 return ErrChecksum
1349 }
1350 return nil
1351 }
1352
1353
1354 func (m *meta) copy(dest *meta) {
1355 *dest = *m
1356 }
1357
1358
1359 func (m *meta) write(p *page) {
1360 if m.root.root >= m.pgid {
1361 panic(fmt.Sprintf("root bucket pgid (%d) above high water mark (%d)", m.root.root, m.pgid))
1362 } else if m.freelist >= m.pgid && m.freelist != pgidNoFreelist {
1363
1364 panic(fmt.Sprintf("freelist pgid (%d) above high water mark (%d)", m.freelist, m.pgid))
1365 }
1366
1367
1368 p.id = pgid(m.txid % 2)
1369 p.flags |= metaPageFlag
1370
1371
1372 m.checksum = m.sum64()
1373
1374 m.copy(p.meta())
1375 }
1376
1377
1378 func (m *meta) sum64() uint64 {
1379 var h = fnv.New64a()
1380 _, _ = h.Write((*[unsafe.Offsetof(meta{}.checksum)]byte)(unsafe.Pointer(m))[:])
1381 return h.Sum64()
1382 }
1383
1384
1385 func _assert(condition bool, msg string, v ...interface{}) {
1386 if !condition {
1387 panic(fmt.Sprintf("assertion failed: "+msg, v...))
1388 }
1389 }
1390
View as plain text