xact.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. // Copyright 2014 The lldb Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // Structural transactions.
  5. package lldb
  6. //DONE+ TransactionalMemoryFiler
  7. // ----
  8. // Use NewRollbackFiler(myMemFiler, ...)
  9. /*
  10. bfBits: 3
  11. BenchmarkRollbackFiler 20000000 102 ns/op 9.73 MB/s
  12. bfBits: 4
  13. BenchmarkRollbackFiler 50000000 55.7 ns/op 17.95 MB/s
  14. bfBits: 5
  15. BenchmarkRollbackFiler 100000000 32.2 ns/op 31.06 MB/s
  16. bfBits: 6
  17. BenchmarkRollbackFiler 100000000 20.6 ns/op 48.46 MB/s
  18. bfBits: 7
  19. BenchmarkRollbackFiler 100000000 15.1 ns/op 66.12 MB/s
  20. bfBits: 8
  21. BenchmarkRollbackFiler 100000000 10.5 ns/op 95.66 MB/s
  22. bfBits: 9
  23. BenchmarkRollbackFiler 200000000 8.02 ns/op 124.74 MB/s
  24. bfBits: 10
  25. BenchmarkRollbackFiler 200000000 9.25 ns/op 108.09 MB/s
  26. bfBits: 11
  27. BenchmarkRollbackFiler 100000000 11.7 ns/op 85.47 MB/s
  28. bfBits: 12
  29. BenchmarkRollbackFiler 100000000 17.2 ns/op 57.99 MB/s
  30. bfBits: 13
  31. BenchmarkRollbackFiler 100000000 32.7 ns/op 30.58 MB/s
  32. bfBits: 14
  33. BenchmarkRollbackFiler 50000000 39.6 ns/op 25.27 MB/s
  34. */
  35. import (
  36. "fmt"
  37. "io"
  38. "sync"
  39. "github.com/cznic/fileutil"
  40. "github.com/cznic/internal/buffer"
  41. "github.com/cznic/mathutil"
  42. )
  43. var (
  44. _ Filer = &bitFiler{} // Ensure bitFiler is a Filer.
  45. _ Filer = &RollbackFiler{} // ditto
  46. )
  47. const (
  48. bfBits = 12
  49. bfSize = 1 << bfBits
  50. bfMask = bfSize - 1
  51. )
  52. type (
  53. bitPage struct {
  54. prev, next *bitPage
  55. pdata *[]byte
  56. data []byte
  57. dirty bool
  58. }
  59. bitFilerMap map[int64]*bitPage
  60. bitFiler struct {
  61. parent Filer
  62. m bitFilerMap
  63. size int64
  64. }
  65. )
  66. func newBitFiler(parent Filer) (f *bitFiler, err error) {
  67. sz, err := parent.Size()
  68. if err != nil {
  69. return
  70. }
  71. return &bitFiler{parent: parent, m: bitFilerMap{}, size: sz}, nil
  72. }
  73. func (f *bitFiler) BeginUpdate() error { panic("internal error") }
  74. func (f *bitFiler) EndUpdate() error { panic("internal error") }
  75. func (f *bitFiler) Rollback() error { panic("internal error") }
  76. func (f *bitFiler) Sync() error { panic("internal error") }
  77. func (f *bitFiler) Close() (err error) { return }
  78. func (f *bitFiler) Name() string { return fmt.Sprintf("%p.bitfiler", f) }
  79. func (f *bitFiler) Size() (int64, error) { return f.size, nil }
  80. func (f *bitFiler) free() {
  81. for _, pg := range f.m {
  82. buffer.Put(pg.pdata)
  83. }
  84. }
  85. func (f *bitFiler) PunchHole(off, size int64) (err error) {
  86. first := off >> bfBits
  87. if off&bfMask != 0 {
  88. first++
  89. }
  90. off += size - 1
  91. last := off >> bfBits
  92. if off&bfMask != 0 {
  93. last--
  94. }
  95. if limit := f.size >> bfBits; last > limit {
  96. last = limit
  97. }
  98. for pgI := first; pgI <= last; pgI++ {
  99. pg := &bitPage{}
  100. pg.pdata = buffer.CGet(bfSize)
  101. pg.data = *pg.pdata
  102. pg.dirty = true
  103. f.m[pgI] = pg
  104. }
  105. return
  106. }
  107. func (f *bitFiler) ReadAt(b []byte, off int64) (n int, err error) {
  108. avail := f.size - off
  109. pgI := off >> bfBits
  110. pgO := int(off & bfMask)
  111. rem := len(b)
  112. if int64(rem) >= avail {
  113. rem = int(avail)
  114. err = io.EOF
  115. }
  116. for rem != 0 && avail > 0 {
  117. pg := f.m[pgI]
  118. if pg == nil {
  119. pg = &bitPage{}
  120. pg.pdata = buffer.CGet(bfSize)
  121. pg.data = *pg.pdata
  122. if f.parent != nil {
  123. _, err = f.parent.ReadAt(pg.data, off&^bfMask)
  124. if err != nil && !fileutil.IsEOF(err) {
  125. return
  126. }
  127. err = nil
  128. }
  129. f.m[pgI] = pg
  130. }
  131. nc := copy(b[:mathutil.Min(rem, bfSize)], pg.data[pgO:])
  132. pgI++
  133. pgO = 0
  134. rem -= nc
  135. n += nc
  136. b = b[nc:]
  137. off += int64(nc)
  138. }
  139. return
  140. }
  141. func (f *bitFiler) Truncate(size int64) (err error) {
  142. switch {
  143. case size < 0:
  144. return &ErrINVAL{"Truncate size", size}
  145. case size == 0:
  146. f.m = bitFilerMap{}
  147. f.size = 0
  148. return
  149. }
  150. first := size >> bfBits
  151. if size&bfMask != 0 {
  152. first++
  153. }
  154. last := f.size >> bfBits
  155. if f.size&bfMask != 0 {
  156. last++
  157. }
  158. for ; first < last; first++ {
  159. if bp, ok := f.m[first]; ok {
  160. buffer.Put(bp.pdata)
  161. }
  162. delete(f.m, first)
  163. }
  164. f.size = size
  165. return
  166. }
  167. func (f *bitFiler) WriteAt(b []byte, off int64) (n int, err error) {
  168. off0 := off
  169. pgI := off >> bfBits
  170. pgO := int(off & bfMask)
  171. n = len(b)
  172. rem := n
  173. var nc int
  174. for rem != 0 {
  175. pg := f.m[pgI]
  176. if pg == nil {
  177. pg = &bitPage{}
  178. pg.pdata = buffer.CGet(bfSize)
  179. pg.data = *pg.pdata
  180. if f.parent != nil {
  181. _, err = f.parent.ReadAt(pg.data, off&^bfMask)
  182. if err != nil && !fileutil.IsEOF(err) {
  183. return
  184. }
  185. err = nil
  186. }
  187. f.m[pgI] = pg
  188. }
  189. nc = copy(pg.data[pgO:], b)
  190. pgI++
  191. pg.dirty = true
  192. pgO = 0
  193. rem -= nc
  194. b = b[nc:]
  195. off += int64(nc)
  196. }
  197. f.size = mathutil.MaxInt64(f.size, off0+int64(n))
  198. return
  199. }
  200. func (f *bitFiler) link() {
  201. for pgI, pg := range f.m {
  202. nx, ok := f.m[pgI+1]
  203. if !ok || !nx.dirty {
  204. continue
  205. }
  206. nx.prev, pg.next = pg, nx
  207. }
  208. }
  209. func (f *bitFiler) dumpDirty(w io.WriterAt) (nwr int, err error) {
  210. f.link()
  211. for pgI, pg := range f.m {
  212. if !pg.dirty {
  213. continue
  214. }
  215. for pg.prev != nil && pg.prev.dirty {
  216. pg = pg.prev
  217. pgI--
  218. }
  219. for pg != nil && pg.dirty {
  220. if _, err := w.WriteAt(pg.data, pgI<<bfBits); err != nil {
  221. return 0, err
  222. }
  223. nwr++
  224. pg.dirty = false
  225. pg = pg.next
  226. pgI++
  227. }
  228. }
  229. return
  230. }
  231. // RollbackFiler is a Filer implementing structural transaction handling.
  232. // Structural transactions should be small and short lived because all non
  233. // committed data are held in memory until committed or discarded by a
  234. // Rollback.
  235. //
  236. // While using RollbackFiler, every intended update of the wrapped Filler, by
  237. // WriteAt, Truncate or PunchHole, _must_ be made within a transaction.
  238. // Attempts to do it outside of a transaction will return ErrPERM. OTOH,
  239. // invoking ReadAt outside of a transaction is not a problem.
  240. //
  241. // No nested transactions: All updates within a transaction are held in memory.
  242. // On a matching EndUpdate the updates held in memory are actually written to
  243. // the wrapped Filer.
  244. //
  245. // Nested transactions: Correct data will be seen from RollbackFiler when any
  246. // level of a nested transaction is rollbacked. The actual writing to the
  247. // wrapped Filer happens only when the outer most transaction nesting level is
  248. // closed.
  249. //
  250. // Invoking Rollback is an alternative to EndUpdate. It discards all changes
  251. // made at the current transaction level and returns the "state" (possibly not
  252. // yet persisted) of the Filer to what it was before the corresponding
  253. // BeginUpdate.
  254. //
  255. // During an open transaction, all reads (using ReadAt) are "dirty" reads,
  256. // seeing the uncommitted changes made to the Filer's data.
  257. //
  258. // Lldb databases should be based upon a RollbackFiler.
  259. //
  260. // With a wrapped MemFiler one gets transactional memory. With, for example a
  261. // wrapped disk based SimpleFileFiler it protects against at least some HW
  262. // errors - if Rollback is properly invoked on such failures and/or if there's
  263. // some WAL or 2PC or whatever other safe mechanism based recovery procedure
  264. // used by the client.
  265. //
  266. // The "real" writes to the wrapped Filer (or WAL instead) go through the
  267. // writerAt supplied to NewRollbackFiler.
  268. //
  269. // List of functions/methods which are recommended to be wrapped in a
  270. // BeginUpdate/EndUpdate structural transaction:
  271. //
  272. // Allocator.Alloc
  273. // Allocator.Free
  274. // Allocator.Realloc
  275. //
  276. // CreateBTree
  277. // RemoveBTree
  278. // BTree.Clear
  279. // BTree.Delete
  280. // BTree.DeleteAny
  281. // BTree.Clear
  282. // BTree.Extract
  283. // BTree.Get (it can mutate the DB)
  284. // BTree.Put
  285. // BTree.Set
  286. //
  287. // NOTE: RollbackFiler is a generic solution intended to wrap Filers provided
  288. // by this package which do not implement any of the transactional methods.
  289. // RollbackFiler thus _does not_ invoke any of the transactional methods of its
  290. // wrapped Filer.
  291. //
  292. // RollbackFiler is safe for concurrent use by multiple goroutines.
  293. type RollbackFiler struct {
  294. mu sync.RWMutex
  295. inCallback bool
  296. inCallbackMu sync.RWMutex
  297. bitFiler *bitFiler
  298. checkpoint func(int64) error
  299. closed bool
  300. f Filer
  301. parent Filer
  302. tlevel int // transaction nesting level, 0 == not in transaction
  303. writerAt io.WriterAt
  304. // afterRollback, if not nil, is called after performing Rollback
  305. // without errros.
  306. afterRollback func() error
  307. }
  308. // NewRollbackFiler returns a RollbackFiler wrapping f.
  309. //
  310. // The checkpoint parameter
  311. //
  312. // The checkpoint function is called after closing (by EndUpdate) the upper
  313. // most level open transaction if all calls of writerAt were successful and the
  314. // DB (or eg. a WAL) is thus now in a consistent state (virtually, in the ideal
  315. // world with no write caches, no HW failures, no process crashes, ...).
  316. //
  317. // NOTE: In, for example, a 2PC it is necessary to reflect also the sz
  318. // parameter as the new file size (as in the parameter to Truncate). All
  319. // changes were successfully written already by writerAt before invoking
  320. // checkpoint.
  321. //
  322. // The writerAt parameter
  323. //
  324. // The writerAt interface is used to commit the updates of the wrapped Filer.
  325. // If any invocation of writerAt fails then a non nil error will be returned
  326. // from EndUpdate and checkpoint will _not_ ne called. Neither is necessary to
  327. // call Rollback. The rule of thumb: The [structural] transaction [level] is
  328. // closed by invoking exactly once one of EndUpdate _or_ Rollback.
  329. //
  330. // It is presumed that writerAt uses WAL or 2PC or whatever other safe
  331. // mechanism to physically commit the updates.
  332. //
  333. // Updates performed by invocations of writerAt are byte-precise, but not
  334. // necessarily maximum possible length precise. IOW, for example an update
  335. // crossing page boundaries may be performed by more than one writerAt
  336. // invocation. No offset sorting is performed. This may change if it proves
  337. // to be a problem. Such change would be considered backward compatible.
  338. //
  339. // NOTE: Using RollbackFiler, but failing to ever invoke a matching "closing"
  340. // EndUpdate after an "opening" BeginUpdate means neither writerAt or
  341. // checkpoint will ever get called - with all the possible data loss
  342. // consequences.
  343. func NewRollbackFiler(f Filer, checkpoint func(sz int64) error, writerAt io.WriterAt) (r *RollbackFiler, err error) {
  344. if f == nil || checkpoint == nil || writerAt == nil {
  345. return nil, &ErrINVAL{Src: "lldb.NewRollbackFiler, nil argument"}
  346. }
  347. return &RollbackFiler{
  348. checkpoint: checkpoint,
  349. f: f,
  350. writerAt: writerAt,
  351. }, nil
  352. }
  353. // Implements Filer.
  354. func (r *RollbackFiler) BeginUpdate() (err error) {
  355. r.mu.Lock()
  356. defer r.mu.Unlock()
  357. parent := r.f
  358. if r.tlevel != 0 {
  359. parent = r.bitFiler
  360. }
  361. r.bitFiler, err = newBitFiler(parent)
  362. if err != nil {
  363. return
  364. }
  365. r.tlevel++
  366. return
  367. }
  368. // Implements Filer.
  369. //
  370. // Close will return an error if not invoked at nesting level 0. However, to
  371. // allow emergency closing from eg. a signal handler; if Close is invoked
  372. // within an open transaction(s), it rollbacks any non committed open
  373. // transactions and performs the Close operation.
  374. //
  375. // IOW: Regardless of the transaction nesting level the Close is always
  376. // performed but any uncommitted transaction data are lost.
  377. func (r *RollbackFiler) Close() (err error) {
  378. r.mu.Lock()
  379. defer r.mu.Unlock()
  380. if r.closed {
  381. return &ErrPERM{r.f.Name() + ": Already closed"}
  382. }
  383. r.closed = true
  384. if err = r.f.Close(); err != nil {
  385. return
  386. }
  387. if r.tlevel != 0 {
  388. err = &ErrPERM{r.f.Name() + ": Close inside an open transaction"}
  389. }
  390. if r.bitFiler != nil {
  391. r.bitFiler.free()
  392. r.bitFiler = nil
  393. }
  394. return
  395. }
  396. // Implements Filer.
  397. func (r *RollbackFiler) EndUpdate() (err error) {
  398. r.mu.Lock()
  399. defer r.mu.Unlock()
  400. if r.tlevel == 0 {
  401. return &ErrPERM{r.f.Name() + " : EndUpdate outside of a transaction"}
  402. }
  403. sz, err := r.size() // Cannot call .Size() -> deadlock
  404. if err != nil {
  405. return
  406. }
  407. r.tlevel--
  408. bf := r.bitFiler
  409. parent := bf.parent
  410. w := r.writerAt
  411. if r.tlevel != 0 {
  412. w = parent
  413. }
  414. nwr, err := bf.dumpDirty(w)
  415. if err != nil {
  416. return
  417. }
  418. switch {
  419. case r.tlevel == 0:
  420. defer func() {
  421. r.bitFiler.free()
  422. r.bitFiler = nil
  423. }()
  424. if nwr == 0 {
  425. return
  426. }
  427. return r.checkpoint(sz)
  428. default:
  429. r.bitFiler.free()
  430. r.bitFiler = parent.(*bitFiler)
  431. sz, _ := bf.Size() // bitFiler.Size() never returns err != nil
  432. return parent.Truncate(sz)
  433. }
  434. }
  435. // Implements Filer.
  436. func (r *RollbackFiler) Name() string {
  437. r.mu.RLock()
  438. defer r.mu.RUnlock()
  439. return r.f.Name()
  440. }
  441. // Implements Filer.
  442. func (r *RollbackFiler) PunchHole(off, size int64) error {
  443. r.mu.Lock()
  444. defer r.mu.Unlock()
  445. if r.tlevel == 0 {
  446. return &ErrPERM{r.f.Name() + ": PunchHole outside of a transaction"}
  447. }
  448. if off < 0 {
  449. return &ErrINVAL{r.f.Name() + ": PunchHole off", off}
  450. }
  451. if size < 0 || off+size > r.bitFiler.size {
  452. return &ErrINVAL{r.f.Name() + ": PunchHole size", size}
  453. }
  454. return r.bitFiler.PunchHole(off, size)
  455. }
  456. // Implements Filer.
  457. func (r *RollbackFiler) ReadAt(b []byte, off int64) (n int, err error) {
  458. r.inCallbackMu.RLock()
  459. defer r.inCallbackMu.RUnlock()
  460. if !r.inCallback {
  461. r.mu.RLock()
  462. defer r.mu.RUnlock()
  463. }
  464. if r.tlevel == 0 {
  465. return r.f.ReadAt(b, off)
  466. }
  467. return r.bitFiler.ReadAt(b, off)
  468. }
  469. // Implements Filer.
  470. func (r *RollbackFiler) Rollback() (err error) {
  471. r.mu.Lock()
  472. defer r.mu.Unlock()
  473. if r.tlevel == 0 {
  474. return &ErrPERM{r.f.Name() + ": Rollback outside of a transaction"}
  475. }
  476. if r.tlevel > 1 {
  477. r.bitFiler.free()
  478. r.bitFiler = r.bitFiler.parent.(*bitFiler)
  479. }
  480. r.tlevel--
  481. if f := r.afterRollback; f != nil {
  482. r.inCallbackMu.Lock()
  483. r.inCallback = true
  484. r.inCallbackMu.Unlock()
  485. defer func() {
  486. r.inCallbackMu.Lock()
  487. r.inCallback = false
  488. r.inCallbackMu.Unlock()
  489. }()
  490. return f()
  491. }
  492. return
  493. }
  494. func (r *RollbackFiler) size() (sz int64, err error) {
  495. if r.tlevel == 0 {
  496. return r.f.Size()
  497. }
  498. return r.bitFiler.Size()
  499. }
  500. // Implements Filer.
  501. func (r *RollbackFiler) Size() (sz int64, err error) {
  502. r.mu.Lock()
  503. defer r.mu.Unlock()
  504. return r.size()
  505. }
  506. // Implements Filer.
  507. func (r *RollbackFiler) Sync() error {
  508. r.mu.Lock()
  509. defer r.mu.Unlock()
  510. return r.f.Sync()
  511. }
  512. // Implements Filer.
  513. func (r *RollbackFiler) Truncate(size int64) error {
  514. r.mu.Lock()
  515. defer r.mu.Unlock()
  516. if r.tlevel == 0 {
  517. return &ErrPERM{r.f.Name() + ": Truncate outside of a transaction"}
  518. }
  519. return r.bitFiler.Truncate(size)
  520. }
  521. // Implements Filer.
  522. func (r *RollbackFiler) WriteAt(b []byte, off int64) (n int, err error) {
  523. r.mu.Lock()
  524. defer r.mu.Unlock()
  525. if r.tlevel == 0 {
  526. return 0, &ErrPERM{r.f.Name() + ": WriteAt outside of a transaction"}
  527. }
  528. return r.bitFiler.WriteAt(b, off)
  529. }