logicalslotmanager.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  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. LogicalSlotManager data structure
  21. */
  22. type LogicalSlotManager struct {
  23. storagefile *file.StorageFile // StorageFile which is wrapped
  24. pager *paging.PagedStorageFile // Pager for StorageFile
  25. freeManager *FreeLogicalSlotManager // Manager for free slots
  26. recordSize uint32 // Size of records
  27. elementsPerPage uint16 // Available elements per page
  28. }
  29. /*
  30. NewLogicalSlotManager creates a new object to manage logical slots. This
  31. factory function requires two PagedStorageFiles the first will hold the actual
  32. logical slots, the second is used to manage free logical slots.
  33. */
  34. func NewLogicalSlotManager(lsf *paging.PagedStorageFile,
  35. flsf *paging.PagedStorageFile) *LogicalSlotManager {
  36. sf := lsf.StorageFile()
  37. freeManager := NewFreeLogicalSlotManager(flsf)
  38. recordSize := sf.RecordSize()
  39. return &LogicalSlotManager{sf, lsf, freeManager,
  40. recordSize, uint16((recordSize - pageview.OffsetTransData) / util.LocationSize)}
  41. }
  42. /*
  43. ElementsPerPage returns the available elements per page.
  44. */
  45. func (lsm *LogicalSlotManager) ElementsPerPage() uint16 {
  46. return lsm.elementsPerPage
  47. }
  48. /*
  49. Insert inserts a given physical slot info and returns a logical slot for it.
  50. */
  51. func (lsm *LogicalSlotManager) Insert(location uint64) (uint64, error) {
  52. // Try to get a free slot from the FreeLogicalSlotManager
  53. slot, err := lsm.freeManager.Get()
  54. if err != nil {
  55. return 0, err
  56. }
  57. if slot == 0 {
  58. // Allocate a new page and give all its rows to the free manager
  59. allocPage, err := lsm.pager.AllocatePage(view.TypeTranslationPage)
  60. if err != nil {
  61. return 0, err
  62. }
  63. offset := uint16(pageview.OffsetTransData)
  64. var i uint16
  65. for i = 0; i < lsm.elementsPerPage; i++ {
  66. lsm.freeManager.Add(util.PackLocation(allocPage, offset))
  67. offset += util.LocationSize
  68. }
  69. err = lsm.Flush()
  70. if err != nil {
  71. // Try to clean up if something goes wrong
  72. // Make the freeManager forget that he received anything
  73. lsm.freeManager.slots = make([]uint64, 0)
  74. // Free the allocated page again
  75. lsm.pager.FreePage(allocPage)
  76. return 0, err
  77. }
  78. // Now get the first slot from the newly allocated page - no need for
  79. // error checking since we just flushed the page and all is well
  80. slot, _ = lsm.freeManager.Get()
  81. }
  82. // Write physical slot data to translation page
  83. return slot, lsm.Update(slot, location)
  84. }
  85. /*
  86. ForceInsert inserts a given physical slot info at a given logical slot.
  87. */
  88. func (lsm *LogicalSlotManager) ForceInsert(logicalSlot uint64, location uint64) error {
  89. page := lsm.pager.Last(view.TypeTranslationPage)
  90. targetPage := util.LocationRecord(logicalSlot)
  91. // If the target page hasn't been allocated yet then create new pages
  92. // until the target page is available and we can force insert into the
  93. // requested slot
  94. for page < targetPage {
  95. var err error
  96. page, err = lsm.pager.AllocatePage(view.TypeTranslationPage)
  97. if err != nil {
  98. return err
  99. }
  100. }
  101. slot, err := lsm.Fetch(logicalSlot)
  102. if err != nil {
  103. return err
  104. }
  105. if slot != 0 {
  106. panic(fmt.Sprintf("Cannot force insert into slot %v because it already exists",
  107. logicalSlot))
  108. }
  109. return lsm.Update(logicalSlot, location)
  110. }
  111. /*
  112. Update updates a given logical slot with a physical slot info.
  113. */
  114. func (lsm *LogicalSlotManager) Update(logicalSlot uint64, location uint64) error {
  115. recordID := util.LocationRecord(logicalSlot)
  116. record, err := lsm.storagefile.Get(recordID)
  117. if err != nil {
  118. return err
  119. }
  120. page := pageview.NewTransPage(record)
  121. page.SetSlotInfo(util.LocationOffset(logicalSlot), util.LocationRecord(location),
  122. util.LocationOffset(location))
  123. lsm.storagefile.ReleaseInUseID(recordID, true)
  124. return nil
  125. }
  126. /*
  127. Free frees a given logical slot. The given slot is given to the FreeLogicalSlotManager.
  128. */
  129. func (lsm *LogicalSlotManager) Free(logicalSlot uint64) error {
  130. recordID := util.LocationRecord(logicalSlot)
  131. record, err := lsm.storagefile.Get(recordID)
  132. if err != nil {
  133. return err
  134. }
  135. page := pageview.NewTransPage(record)
  136. page.SetSlotInfo(util.LocationOffset(logicalSlot), util.LocationRecord(0),
  137. util.LocationOffset(0))
  138. return lsm.storagefile.ReleaseInUseID(recordID, true)
  139. }
  140. /*
  141. Fetch looks up a physical slot using a given logical slot.
  142. */
  143. func (lsm *LogicalSlotManager) Fetch(logicalSlot uint64) (uint64, error) {
  144. recordID := util.LocationRecord(logicalSlot)
  145. offset := util.LocationOffset(logicalSlot)
  146. if lastPage := lsm.pager.Last(view.TypeTranslationPage); lastPage < recordID {
  147. // Return if the requested page doesn't exist yet
  148. return 0, nil
  149. }
  150. record, err := lsm.storagefile.Get(recordID)
  151. if err != nil {
  152. return 0, err
  153. }
  154. page := pageview.NewTransPage(record)
  155. slot := util.PackLocation(page.SlotInfoRecord(offset), page.SlotInfoOffset(offset))
  156. lsm.storagefile.ReleaseInUseID(recordID, false)
  157. return slot, nil
  158. }
  159. /*
  160. Flush writes all pending changes.
  161. */
  162. func (lsm *LogicalSlotManager) Flush() error {
  163. return lsm.freeManager.Flush()
  164. }