freephysicalslotmanager.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. /*
  2. * EliasDB
  3. *
  4. * Copyright 2016 Matthias Ladkau. All rights reserved.
  5. *
  6. * This Source Code Form is subject to the terms of the Mozilla Public
  7. * License, v. 2.0. If a copy of the MPL was not distributed with this
  8. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  9. */
  10. package slotting
  11. import (
  12. "fmt"
  13. "devt.de/krotik/eliasdb/storage/file"
  14. "devt.de/krotik/eliasdb/storage/paging"
  15. "devt.de/krotik/eliasdb/storage/paging/view"
  16. "devt.de/krotik/eliasdb/storage/slotting/pageview"
  17. "devt.de/krotik/eliasdb/storage/util"
  18. )
  19. /*
  20. FreePhysicalSlotManager data structure
  21. */
  22. type FreePhysicalSlotManager struct {
  23. storagefile *file.StorageFile // StorageFile which is wrapped
  24. pager *paging.PagedStorageFile // Pager for StorageFile
  25. onlyAppend bool // Flag for append-only mode
  26. lastMaxSlotSize int // Last max slot size
  27. slots []uint64 // List of free slots
  28. sizes []uint32 // List of free slot sizes
  29. }
  30. /*
  31. NewFreePhysicalSlotManager creates a new object to manage free physical slots.
  32. */
  33. func NewFreePhysicalSlotManager(psf *paging.PagedStorageFile, onlyAppend bool) *FreePhysicalSlotManager {
  34. return &FreePhysicalSlotManager{psf.StorageFile(), psf, onlyAppend, 0,
  35. make([]uint64, 0), make([]uint32, 0)}
  36. }
  37. /*
  38. Get searches for a free location with the given size.
  39. */
  40. func (fpsm *FreePhysicalSlotManager) Get(size uint32) (uint64, error) {
  41. // Return always nothing found if we are in only-append mode
  42. if fpsm.onlyAppend {
  43. return 0, nil
  44. }
  45. // Return nothing if all previous found pages were too small
  46. if fpsm.lastMaxSlotSize != 0 && int(size) > fpsm.lastMaxSlotSize {
  47. return 0, nil
  48. }
  49. cursor := paging.NewPageCursor(fpsm.pager, view.TypeFreePhysicalSlotPage, 0)
  50. // No need for error checking on cursor next since all pages will be opened
  51. // via Get calls in the loop.
  52. page, _ := cursor.Next()
  53. for page != 0 {
  54. record, err := fpsm.storagefile.Get(page)
  55. if err != nil {
  56. // Reset the lastMaxSlotSize since we didn't visit all
  57. // FreePhysicalSlotPages
  58. fpsm.lastMaxSlotSize = 0
  59. return 0, err
  60. }
  61. fpsp := pageview.NewFreePhysicalSlotPage(record)
  62. slot := fpsp.FindSlot(size)
  63. // If a slot was found (Important: a slot can be >= 0)
  64. if slot >= 0 {
  65. // Return a found slot and free the free page if necessary
  66. fpsm.lastMaxSlotSize = 0
  67. loc := fpsp.SlotInfoLocation(uint16(slot))
  68. // Release slot
  69. fpsp.ReleaseSlotInfo(uint16(slot))
  70. if fpsp.FreeSlotCount() == 0 {
  71. // Free the page if no free slot is stored
  72. fpsm.storagefile.ReleaseInUseID(page, false)
  73. fpsm.pager.FreePage(page)
  74. } else {
  75. fpsm.storagefile.ReleaseInUseID(page, false)
  76. }
  77. return loc, nil
  78. }
  79. if fpsm.lastMaxSlotSize < -slot {
  80. fpsm.lastMaxSlotSize = -slot
  81. }
  82. fpsm.storagefile.ReleaseInUseID(page, false)
  83. page, _ = cursor.Next()
  84. }
  85. return 0, nil
  86. }
  87. /*
  88. Add adds a slotinfo to the free slot set.
  89. */
  90. func (fpsm *FreePhysicalSlotManager) Add(loc uint64, size uint32) {
  91. if size > 0 {
  92. fpsm.slots = append(fpsm.slots, loc)
  93. fpsm.sizes = append(fpsm.sizes, size)
  94. }
  95. }
  96. /*
  97. Flush writes all added slotinfos to FreePhysicalSlotPages.
  98. */
  99. func (fpsm *FreePhysicalSlotManager) Flush() error {
  100. cursor := paging.NewPageCursor(fpsm.pager, view.TypeFreePhysicalSlotPage, 0)
  101. index := 0
  102. // Go through all free physical row ID pages
  103. // No need for error checking on cursor next since all pages will be opened
  104. // via Get calls in the loop.
  105. page, _ := cursor.Next()
  106. for page != 0 {
  107. // Need to declare err here otherwise index becomes a local for
  108. // the "for" block
  109. var err error
  110. index, err = fpsm.doFlush(page, index)
  111. if err != nil {
  112. return err
  113. }
  114. if index >= len(fpsm.slots) {
  115. break
  116. }
  117. page, _ = cursor.Next()
  118. }
  119. // Allocate new free physical slot pages if all present ones are full
  120. // and we have still slots to process
  121. for index < len(fpsm.slots) {
  122. allocPage, err := fpsm.pager.AllocatePage(view.TypeFreePhysicalSlotPage)
  123. if err != nil {
  124. return err
  125. }
  126. index, err = fpsm.doFlush(allocPage, index)
  127. if err != nil {
  128. // Try to free the allocated page if there was an error
  129. // ignore any error of the FreePage call
  130. fpsm.pager.FreePage(allocPage)
  131. return err
  132. }
  133. }
  134. // Clear lists after all slots information have been written
  135. fpsm.slots = make([]uint64, 0)
  136. fpsm.sizes = make([]uint32, 0)
  137. return nil
  138. }
  139. /*
  140. doFlush writes all added slotinfos to a FreePhysicalSlotPage. Stop if the page is full.
  141. */
  142. func (fpsm *FreePhysicalSlotManager) doFlush(page uint64, index int) (int, error) {
  143. r, err := fpsm.storagefile.Get(page)
  144. if err != nil {
  145. return index, err
  146. }
  147. fpsp := pageview.NewFreePhysicalSlotPage(r)
  148. // Iterate all page slots (stop if the page has no more available slots
  149. // or we reached the end of the page)
  150. slot := fpsp.FirstFreeSlotInfo()
  151. for ; slot != -1 && index < len(fpsm.slots); index++ {
  152. loc := fpsm.slots[index]
  153. size := fpsm.sizes[index]
  154. if size > 0 {
  155. offset := fpsp.AllocateSlotInfo(uint16(slot))
  156. fpsp.SetSlotInfo(offset, util.LocationRecord(loc), util.LocationOffset(loc))
  157. fpsp.SetFreeSlotSize(offset, size)
  158. slot = fpsp.FirstFreeSlotInfo()
  159. }
  160. }
  161. fpsm.storagefile.ReleaseInUseID(page, true)
  162. return index, nil
  163. }
  164. /*
  165. String returns a string representation of this FreePhysicalSlotManager.
  166. */
  167. func (fpsm *FreePhysicalSlotManager) String() string {
  168. return fmt.Sprintf("FreePhysicalSlotManager: %v (onlyAppend:%v lastMaxSlotSize:%v)\nIds :%v\nSizes:%v",
  169. fpsm.storagefile.Name(), fpsm.onlyAppend, fpsm.lastMaxSlotSize, fpsm.slots, fpsm.sizes)
  170. }