blob_read.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. package core
  2. import (
  3. "encoding/binary"
  4. "errors"
  5. "io"
  6. "github.com/golang/snappy"
  7. "TsDB2/DB/core/types"
  8. )
  9. // ReadBlob types.BlobReader 接口实现 — 给 ORM 在读回 Blob 后拿 io.ReadCloser.
  10. //
  11. // 行为: 与 BlobRead 内部走同一份链页 / 解码路径, 区别只在"按流 / 按块":
  12. // - BlobRead 返 []byte, 适合一次性读完的小内容;
  13. // - ReadBlob 返 io.ReadCloser, 适合"调方按需读、不全拉到内存"的大内容与流式对接 (用户
  14. // 可在 io.Copy 中途断流)。
  15. //
  16. // chainReader 已经在跨页时按 PageSize 缓冲, 不需要再包一层 bufio。
  17. // 关链逻辑: chainReader 不持有 fd / 内存分配, 直接 NopCloser 即可。
  18. func (t *Table) ReadBlob(ref types.BlobRef, compress types.FieldCompress) (io.ReadCloser, error) {
  19. if t == nil || t.db == nil {
  20. return nil, errors.New("ReadBlob: table is nil")
  21. }
  22. if !compress.IsValid() {
  23. return nil, errors.New("ReadBlob: unknown compress option")
  24. }
  25. if ref.Empty() {
  26. return nil, nil
  27. }
  28. chainR, err := newBlobChainReader(t.db, ref.FirstPageID)
  29. if err != nil {
  30. return nil, err
  31. }
  32. if compress == types.CompressSnappy {
  33. return io.NopCloser(snappy.NewReader(chainR)), nil
  34. }
  35. return io.NopCloser(chainR), nil
  36. }
  37. // 静态断言: *Table 实现了 types.BlobReader 接口, 防止接口签名漂移。
  38. var _ types.BlobReader = (*Table)(nil)
  39. // BlobRead 把 BlobRef 引用的整条链读回, 自动按 compress 解压.
  40. //
  41. // 返回的是原始字节 (解压后), 长度 == ref.TotalLen, 任何不一致都返 ErrBlobCorrupted.
  42. //
  43. // 内存: 全读到内存 (io.ReadAll). Snappy 是块解压, 需要看到全部数据才能解, 无法流式;
  44. // 对极大 blob (>1GB) 请改用 Seek+部分读 API (v2 TODO).
  45. func (t *Table) BlobRead(ref BlobRef, compress types.FieldCompress) ([]byte, error) {
  46. if t == nil || t.db == nil {
  47. return nil, errors.New("BlobRead: table is nil")
  48. }
  49. if !compress.IsValid() {
  50. return nil, errors.New("BlobRead: unknown compress option")
  51. }
  52. if ref.Empty() {
  53. return nil, nil
  54. }
  55. chainR, err := newBlobChainReader(t.db, ref.FirstPageID)
  56. if err != nil {
  57. return nil, err
  58. }
  59. var data []byte
  60. if compress == types.CompressSnappy {
  61. // snappy.NewReader 是流式 API: 内部缓冲, 但 Read 返 EOF 后才能确认解压完整.
  62. // io.ReadAll 一次性读完, 与解码要求一致 (Snappy 帧必须见尾才知道解压完).
  63. sr := snappy.NewReader(chainR)
  64. data, err = io.ReadAll(sr)
  65. } else {
  66. data, err = io.ReadAll(chainR)
  67. }
  68. if err != nil {
  69. return nil, err
  70. }
  71. if uint32(len(data)) != ref.TotalLen {
  72. return nil, ErrBlobCorrupted
  73. }
  74. return data, nil
  75. }
  76. // BlobFree 把整条 blob 链归还空闲链表.
  77. //
  78. // 行删除/更新/DropTable 都需要释放旧 blob; 否则链页成为永久泄漏.
  79. //
  80. // 时序: 在事务里调用, 走 DeferFreeChain 路径, Commit 成功才真正还链, Rollback 丢弃.
  81. // 无事务上下文时直写空闲链 (与 DeferFreePage 在 db==nil 时的行为一致).
  82. func (t *Table) BlobFree(ref BlobRef) error {
  83. if ref.Empty() {
  84. return nil
  85. }
  86. if t == nil || t.db == nil {
  87. return errors.New("BlobFree: table is nil")
  88. }
  89. // 1. 走链, 找到尾页 + 计链长.
  90. cur := ref.FirstPageID
  91. tail := cur
  92. count := uint32(0)
  93. buf := make([]byte, PageSize)
  94. for cur != 0 {
  95. if err := t.db.file.ReadPage(cur, buf); err != nil {
  96. return err
  97. }
  98. tail = cur
  99. count++
  100. if count > BlobChainMax {
  101. return ErrBlobCorrupted // 链长不可能这么长, 拒绝继续走
  102. }
  103. cur = pageHdrNextPage(buf)
  104. }
  105. // 2. 整链回收 (与现有 FreeChain 同一接口).
  106. t.db.deferFreeBlobChain(ref.FirstPageID, tail, count)
  107. return nil
  108. }
  109. // pageHdrNextPage 读 PageHdr.NextPage, 避免依赖 page 包类型 (本文件独立可读).
  110. func pageHdrNextPage(buf []byte) uint32 {
  111. const off = 12 // PageHdr.NextPage 在头内偏移
  112. return uint32(buf[off])<<24 | uint32(buf[off+1])<<16 |
  113. uint32(buf[off+2])<<8 | uint32(buf[off+3])
  114. }
  115. // deferFreeBlobChain 走事务的 pendingFreeChain 路径 (Commit 后真正还链).
  116. func (db *DB) deferFreeBlobChain(head, tail, count uint32) {
  117. if head == 0 || count == 0 {
  118. return
  119. }
  120. if db.currentTxn != nil {
  121. db.currentTxn.pendingFreeChain = append(db.currentTxn.pendingFreeChain,
  122. pendingChain{head: head, tail: tail, count: count})
  123. return
  124. }
  125. db.pendingMu.Lock()
  126. db.pendingFreeChain = append(db.pendingFreeChain,
  127. pendingChain{head: head, tail: tail, count: count})
  128. db.pendingMu.Unlock()
  129. }
  130. // rowBlobRefs 扫描行内 blob 列, 收集非空 BlobRef.
  131. //
  132. // 引擎层在 DeleteRow / UpdateRow / DropTable 时需要把旧行的 blob 链还掉.
  133. // 行内 blob 列的 8B BlobRef 没有 RowVarTag (内联内容), 必须按列类型定位, 不像堆引
  134. // 用可以走 RowFieldRefTagged.
  135. //
  136. // 返回 []BlobRef (空切片表示"无 blob 列"或"全 NULL"); 行布局解析失败时返 nil.
  137. //
  138. // 列级顺序与 FieldIndices 一致, 调用方按位置 / master 索引映射.
  139. func (table *Table) rowBlobRefs(rowData []byte) []BlobRef {
  140. if len(rowData) < 1 {
  141. return nil
  142. }
  143. sv := table.getVersion(rowData[0])
  144. if sv == nil || len(sv.FieldIndices) == 0 {
  145. return nil
  146. }
  147. n := len(sv.FieldIndices)
  148. col0Width := table.svCol0Width(sv)
  149. ends, ok := types.RowParseOffsets(rowData, n, col0Width)
  150. if !ok {
  151. return nil
  152. }
  153. bitmap, ok := types.RowNullBitmap(rowData, ends, n)
  154. if !ok {
  155. return nil
  156. }
  157. var refs []BlobRef
  158. for i, idx := range sv.FieldIndices {
  159. f := table.Field[idx]
  160. if f.IsDropped() || !types.IsBlob(f.FieldType) {
  161. continue
  162. }
  163. if types.NullBitSet(bitmap, i) {
  164. continue
  165. }
  166. start := int(types.RowFieldStart(ends, i))
  167. end := int(types.RowFieldEnd(ends, i, col0Width))
  168. if end-start < types.BlobRefSize || start+types.BlobRefSize > len(rowData) {
  169. continue
  170. }
  171. seg := rowData[start : start+types.BlobRefSize]
  172. ref := BlobRef{
  173. TotalLen: binary.BigEndian.Uint32(seg[:4]),
  174. FirstPageID: binary.BigEndian.Uint32(seg[4:8]),
  175. }
  176. if ref.Empty() {
  177. continue
  178. }
  179. refs = append(refs, ref)
  180. }
  181. return refs
  182. }
  183. // releaseRowBlobRefs 把 row 中所有 blob 链归还空闲链表 (DeleteRow / DropTable /
  184. // Update 替换旧值时调用).
  185. //
  186. // 单独走这条路径而不是合并进 heapReleaseRow: blob 链在文件上是"链页"序列, 释放走
  187. // deferFreeBlobChain, 与 heap 页 (PageID + RefCount) 不是同一条释放路径.
  188. //
  189. // 错误采用"放任单列失败不影响后续列"语义: DropTable 要把所有 blob 都还掉, 一列
  190. // 失败而中断会把后面的全漏, 与"全部回收"的承诺背离. 调用方最后会再走 defrag/
  191. // 复检; 单条失败可被日志/计数器观察, 但不会留下半回收状态.
  192. //
  193. // 行布局不可信 / 表结构已变时返错误但仍继续归还本轮已扫到的 blob.
  194. func (table *Table) releaseRowBlobRefs(rowData []byte) error {
  195. refs := table.rowBlobRefs(rowData)
  196. if refs == nil {
  197. if len(rowData) == 0 || rowData[0] == 0 {
  198. return nil // 空行 / 零版本 → 跳过
  199. }
  200. // 布局解析失败, 但仍可能是合法的"无 blob 列" — 行/表不匹配时 rowBlobRefs 返 nil
  201. // (其实有合法返回) 还是错乱都让这条路径容错过去, 不让一个坏行毁掉整批回收.
  202. }
  203. var firstErr error
  204. for _, ref := range refs {
  205. if err := table.BlobFree(ref); err != nil && firstErr == nil {
  206. firstErr = err
  207. }
  208. }
  209. return firstErr
  210. }
  211. // oldBlobRefsByMaster 与 rowBlobRefs 类似, 但按 masterIdx (字段 master 注册表下标)
  212. // 索引 ——
  213. //
  214. // Update 路径 (serializeRowInternal) 按 masterIdx 决定是否释放旧 ref, 这是按列访问,
  215. // 与 rowBlobRefs 返回的"按 FieldIndices 下标"的扁平切片不匹配, 单独提供这条.
  216. //
  217. // 行内 NULL / 空 blob 列不出现在返回 map 中 ——
  218. //
  219. // "old == empty ref"在行里语义上等价于"该列没有旧 ref", 不需要释放.
  220. func (table *Table) oldBlobRefsByMaster(rowData []byte) map[int]BlobRef {
  221. if len(rowData) < 1 {
  222. return nil
  223. }
  224. sv := table.getVersion(rowData[0])
  225. if sv == nil || len(sv.FieldIndices) == 0 {
  226. return nil
  227. }
  228. n := len(sv.FieldIndices)
  229. col0Width := table.svCol0Width(sv)
  230. ends, ok := types.RowParseOffsets(rowData, n, col0Width)
  231. if !ok {
  232. return nil
  233. }
  234. bitmap, ok := types.RowNullBitmap(rowData, ends, n)
  235. if !ok {
  236. return nil
  237. }
  238. var refs map[int]BlobRef
  239. for i, idx := range sv.FieldIndices {
  240. f := table.Field[idx]
  241. if f.IsDropped() || !types.IsBlob(f.FieldType) {
  242. continue
  243. }
  244. if types.NullBitSet(bitmap, i) {
  245. continue
  246. }
  247. start := int(types.RowFieldStart(ends, i))
  248. end := int(types.RowFieldEnd(ends, i, col0Width))
  249. if end-start < types.BlobRefSize || start+types.BlobRefSize > len(rowData) {
  250. continue
  251. }
  252. seg := rowData[start : start+types.BlobRefSize]
  253. ref := BlobRef{
  254. TotalLen: binary.BigEndian.Uint32(seg[:4]),
  255. FirstPageID: binary.BigEndian.Uint32(seg[4:8]),
  256. }
  257. if ref.Empty() {
  258. continue
  259. }
  260. if refs == nil {
  261. refs = make(map[int]BlobRef, n)
  262. }
  263. refs[int(idx)] = ref
  264. }
  265. return refs
  266. }