schema_test.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. package storage
  2. import (
  3. "bufio"
  4. "fmt"
  5. "net"
  6. "sort"
  7. "strings"
  8. "sync"
  9. "testing"
  10. "time"
  11. )
  12. type testScan struct {
  13. keys []string
  14. offset int
  15. limit uint32
  16. keysOnly bool
  17. }
  18. type testKVServer struct {
  19. mu sync.Mutex
  20. data map[string][]byte
  21. lsns map[string]uint64
  22. writes map[string]int
  23. nextLSN uint64
  24. maxScanPage uint32
  25. scanOpens int
  26. scanNexts int
  27. scanCloses int
  28. keyOnlyOpens int
  29. closers []net.Conn
  30. }
  31. func newTestKVServer(t *testing.T) *testKVServer {
  32. t.Helper()
  33. return &testKVServer{
  34. data: make(map[string][]byte),
  35. lsns: make(map[string]uint64),
  36. writes: make(map[string]int),
  37. }
  38. }
  39. func newTestKVPool(kv *testKVServer, size int, timeout time.Duration) *KVPool {
  40. pool := &KVPool{
  41. pool: make(chan *KVClient, size),
  42. size: size,
  43. timeout: timeout,
  44. }
  45. for i := 0; i < size; i++ {
  46. pool.pool <- kv.client()
  47. }
  48. return pool
  49. }
  50. func (s *testKVServer) close() {
  51. s.mu.Lock()
  52. closers := append([]net.Conn(nil), s.closers...)
  53. s.mu.Unlock()
  54. for _, conn := range closers {
  55. _ = conn.Close()
  56. }
  57. }
  58. func (s *testKVServer) client() *KVClient {
  59. clientConn, serverConn := net.Pipe()
  60. s.mu.Lock()
  61. s.closers = append(s.closers, clientConn, serverConn)
  62. s.mu.Unlock()
  63. go s.handle(serverConn)
  64. return &KVClient{
  65. conn: clientConn,
  66. reader: bufio.NewReader(clientConn),
  67. writer: bufio.NewWriter(clientConn),
  68. nextID: 1,
  69. lastUsed: time.Now(),
  70. }
  71. }
  72. func (s *testKVServer) writeCount(prefix string) int {
  73. s.mu.Lock()
  74. defer s.mu.Unlock()
  75. var count int
  76. for key, writes := range s.writes {
  77. if strings.Contains(key, prefix) {
  78. count += writes
  79. }
  80. }
  81. return count
  82. }
  83. func (s *testKVServer) hasKey(key string) bool {
  84. s.mu.Lock()
  85. defer s.mu.Unlock()
  86. _, ok := s.data[key]
  87. return ok
  88. }
  89. func (s *testKVServer) countKeys(prefix string) int {
  90. s.mu.Lock()
  91. defer s.mu.Unlock()
  92. var count int
  93. for key := range s.data {
  94. if strings.HasPrefix(key, prefix) {
  95. count++
  96. }
  97. }
  98. return count
  99. }
  100. func (s *testKVServer) scanStats() (opens, nexts, closes int) {
  101. s.mu.Lock()
  102. defer s.mu.Unlock()
  103. return s.scanOpens, s.scanNexts, s.scanCloses
  104. }
  105. func (s *testKVServer) keyOnlyOpenCount() int {
  106. s.mu.Lock()
  107. defer s.mu.Unlock()
  108. return s.keyOnlyOpens
  109. }
  110. func (s *testKVServer) handle(conn net.Conn) {
  111. defer conn.Close()
  112. r := bufio.NewReader(conn)
  113. scans := make(map[uint64]*testScan)
  114. var nextScan uint64 = 1
  115. for {
  116. opcode, _, requestID, payload, err := readFrame(r)
  117. if err != nil {
  118. return
  119. }
  120. body := s.execute(opcode, payload, scans, &nextScan)
  121. if _, err := conn.Write(encodeResponse(opcode, requestID, body)); err != nil {
  122. return
  123. }
  124. }
  125. }
  126. func (s *testKVServer) execute(opcode uint16, payload []byte, scans map[uint64]*testScan, nextScan *uint64) []byte {
  127. switch opcode {
  128. case opPing:
  129. body := make([]byte, 2+len(payload))
  130. putU16(body[0:2], statusOK)
  131. copy(body[2:], payload)
  132. return body
  133. case opGet:
  134. key, ok := parseOneKey(payload)
  135. if !ok {
  136. return errorBody("InvalidPayload")
  137. }
  138. s.mu.Lock()
  139. value, found := s.data[string(key)]
  140. lsn := s.lsns[string(key)]
  141. s.mu.Unlock()
  142. if !found {
  143. body := make([]byte, 2)
  144. putU16(body[0:2], statusNotFound)
  145. return body
  146. }
  147. body := make([]byte, 14+len(value))
  148. putU16(body[0:2], statusOK)
  149. putU64(body[2:10], lsn)
  150. putU32(body[10:14], uint32(len(value)))
  151. copy(body[14:], value)
  152. return body
  153. case opPut:
  154. key, value, ok := parsePut(payload)
  155. if !ok {
  156. return errorBody("InvalidPayload")
  157. }
  158. s.mu.Lock()
  159. s.nextLSN++
  160. lsn := s.nextLSN
  161. s.data[string(key)] = append([]byte(nil), value...)
  162. s.lsns[string(key)] = lsn
  163. s.writes[string(key)]++
  164. s.mu.Unlock()
  165. body := make([]byte, 10)
  166. putU16(body[0:2], statusOK)
  167. putU64(body[2:10], lsn)
  168. return body
  169. case opDelete:
  170. key, ok := parseOneKey(payload)
  171. if !ok {
  172. return errorBody("InvalidPayload")
  173. }
  174. s.mu.Lock()
  175. _, found := s.data[string(key)]
  176. delete(s.data, string(key))
  177. delete(s.lsns, string(key))
  178. s.mu.Unlock()
  179. body := make([]byte, 3)
  180. putU16(body[0:2], statusOK)
  181. if found {
  182. body[2] = 1
  183. }
  184. return body
  185. case opExists:
  186. key, ok := parseOneKey(payload)
  187. if !ok {
  188. return errorBody("InvalidPayload")
  189. }
  190. s.mu.Lock()
  191. _, found := s.data[string(key)]
  192. s.mu.Unlock()
  193. body := make([]byte, 3)
  194. putU16(body[0:2], statusOK)
  195. if found {
  196. body[2] = 1
  197. }
  198. return body
  199. case opMultiGet:
  200. keys, ok := parseMultiGetKeys(payload)
  201. if !ok {
  202. return errorBody("InvalidPayload")
  203. }
  204. body := make([]byte, 6)
  205. putU16(body[0:2], statusOK)
  206. putU32(body[2:6], uint32(len(keys)))
  207. s.mu.Lock()
  208. for _, key := range keys {
  209. value, found := s.data[string(key)]
  210. if !found {
  211. body = append(body, make([]byte, 16)...)
  212. continue
  213. }
  214. lsn := s.lsns[string(key)]
  215. entry := make([]byte, 16+len(value))
  216. entry[0] = 1
  217. putU32(entry[4:8], uint32(len(value)))
  218. putU64(entry[8:16], lsn)
  219. copy(entry[16:], value)
  220. body = append(body, entry...)
  221. }
  222. s.mu.Unlock()
  223. return body
  224. case opBatchWrite:
  225. ops, ok := parseBatchOps(payload)
  226. if !ok {
  227. return errorBody("InvalidPayload")
  228. }
  229. s.mu.Lock()
  230. s.nextLSN++
  231. lsn := s.nextLSN
  232. for _, op := range ops {
  233. if op.Op == batchPut {
  234. s.data[string(op.Key)] = append([]byte(nil), op.Value...)
  235. s.lsns[string(op.Key)] = lsn
  236. s.writes[string(op.Key)]++
  237. } else {
  238. delete(s.data, string(op.Key))
  239. delete(s.lsns, string(op.Key))
  240. }
  241. }
  242. s.mu.Unlock()
  243. body := make([]byte, 10)
  244. putU16(body[0:2], statusOK)
  245. putU64(body[2:10], lsn)
  246. return body
  247. case opScanOpen:
  248. includeValues, limit, prefix, ok := parseScanOpen(payload)
  249. if !ok {
  250. return errorBody("InvalidPayload")
  251. }
  252. s.mu.Lock()
  253. s.scanOpens++
  254. if !includeValues {
  255. s.keyOnlyOpens++
  256. }
  257. s.mu.Unlock()
  258. s.mu.Lock()
  259. keys := make([]string, 0)
  260. for key := range s.data {
  261. if strings.HasPrefix(key, string(prefix)) {
  262. keys = append(keys, key)
  263. }
  264. }
  265. s.mu.Unlock()
  266. sort.Strings(keys)
  267. if s.maxScanPage > 0 && limit > s.maxScanPage {
  268. limit = s.maxScanPage
  269. }
  270. id := *nextScan
  271. *nextScan = id + 1
  272. scans[id] = &testScan{keys: keys, limit: limit, keysOnly: !includeValues}
  273. body := make([]byte, 10)
  274. putU16(body[0:2], statusOK)
  275. putU64(body[2:10], id)
  276. return body
  277. case opScanNext:
  278. id, ok := parseScanID(payload)
  279. if !ok {
  280. return errorBody("InvalidPayload")
  281. }
  282. scan := scans[id]
  283. if scan == nil {
  284. return errorBody("ScanNotFound")
  285. }
  286. s.mu.Lock()
  287. s.scanNexts++
  288. s.mu.Unlock()
  289. remaining := len(scan.keys) - scan.offset
  290. count := int(scan.limit)
  291. if count > remaining {
  292. count = remaining
  293. }
  294. end := scan.offset + count
  295. body := make([]byte, 10)
  296. putU16(body[0:2], statusOK)
  297. if end >= len(scan.keys) {
  298. body[2] = 1
  299. }
  300. putU32(body[6:10], uint32(count))
  301. s.mu.Lock()
  302. for _, key := range scan.keys[scan.offset:end] {
  303. value := s.data[key]
  304. if scan.keysOnly {
  305. value = nil
  306. }
  307. lsn := s.lsns[key]
  308. entry := make([]byte, 16+len(key)+len(value))
  309. putU32(entry[0:4], uint32(len(key)))
  310. putU32(entry[4:8], uint32(len(value)))
  311. putU64(entry[8:16], lsn)
  312. copy(entry[16:], key)
  313. copy(entry[16+len(key):], value)
  314. body = append(body, entry...)
  315. }
  316. s.mu.Unlock()
  317. scan.offset = end
  318. return body
  319. case opScanClose:
  320. id, ok := parseScanID(payload)
  321. if !ok {
  322. return errorBody("InvalidPayload")
  323. }
  324. s.mu.Lock()
  325. s.scanCloses++
  326. s.mu.Unlock()
  327. delete(scans, id)
  328. body := make([]byte, 3)
  329. putU16(body[0:2], statusOK)
  330. body[2] = 1
  331. return body
  332. default:
  333. return errorBody("UnknownOpcode")
  334. }
  335. }
  336. func parsePut(payload []byte) ([]byte, []byte, bool) {
  337. if len(payload) < 8 {
  338. return nil, nil, false
  339. }
  340. keyLen := getU32(payload[0:4])
  341. valueLen := getU32(payload[4:8])
  342. if keyLen > maxKeySize || valueLen > maxValueSize {
  343. return nil, nil, false
  344. }
  345. if uint64(8)+uint64(keyLen)+uint64(valueLen) != uint64(len(payload)) {
  346. return nil, nil, false
  347. }
  348. return payload[8 : 8+keyLen], payload[8+keyLen:], true
  349. }
  350. func parseMultiGetKeys(payload []byte) ([][]byte, bool) {
  351. if len(payload) < 4 {
  352. return nil, false
  353. }
  354. count := getU32(payload[0:4])
  355. if count > maxOperations {
  356. return nil, false
  357. }
  358. keys := make([][]byte, 0, count)
  359. pos := 4
  360. for i := uint32(0); i < count; i++ {
  361. if len(payload)-pos < 4 {
  362. return nil, false
  363. }
  364. length := getU32(payload[pos : pos+4])
  365. pos += 4
  366. if length > maxKeySize || len(payload)-pos < int(length) {
  367. return nil, false
  368. }
  369. keys = append(keys, payload[pos:pos+int(length)])
  370. pos += int(length)
  371. }
  372. return keys, pos == len(payload)
  373. }
  374. func parseBatchOps(payload []byte) ([]BatchOp, bool) {
  375. if len(payload) < 8 {
  376. return nil, false
  377. }
  378. count := getU32(payload[0:4])
  379. metadataLen := getU32(payload[4:8])
  380. if count == 0 || count > maxOperations || uint64(metadataLen) > uint64(len(payload)-8) {
  381. return nil, false
  382. }
  383. pos := 8 + int(metadataLen)
  384. ops := make([]BatchOp, 0, count)
  385. for i := uint32(0); i < count; i++ {
  386. if len(payload)-pos < 12 {
  387. return nil, false
  388. }
  389. opcode := payload[pos]
  390. keyLen := getU32(payload[pos+4 : pos+8])
  391. valueLen := getU32(payload[pos+8 : pos+12])
  392. pos += 12
  393. if opcode != batchPut && opcode != batchDelete {
  394. return nil, false
  395. }
  396. if keyLen > maxKeySize || valueLen > maxValueSize {
  397. return nil, false
  398. }
  399. if opcode == batchDelete && valueLen != 0 {
  400. return nil, false
  401. }
  402. if len(payload)-pos < int(keyLen)+int(valueLen) {
  403. return nil, false
  404. }
  405. key := payload[pos : pos+int(keyLen)]
  406. pos += int(keyLen)
  407. value := payload[pos : pos+int(valueLen)]
  408. pos += int(valueLen)
  409. ops = append(ops, BatchOp{Op: opcode, Key: key, Value: value})
  410. }
  411. return ops, pos == len(payload)
  412. }
  413. func parseScanOpen(payload []byte) (bool, uint32, []byte, bool) {
  414. if len(payload) < 12 {
  415. return false, 0, nil, false
  416. }
  417. includeValues := payload[0] != 0
  418. limit := getU32(payload[4:8])
  419. prefixLen := getU32(payload[8:12])
  420. if limit == 0 || limit > 4096 || prefixLen > maxKeySize {
  421. return false, 0, nil, false
  422. }
  423. if uint64(12)+uint64(prefixLen) != uint64(len(payload)) {
  424. return false, 0, nil, false
  425. }
  426. return includeValues, limit, payload[12:], true
  427. }
  428. func parseScanID(payload []byte) (uint64, bool) {
  429. if len(payload) < 8 {
  430. return 0, false
  431. }
  432. return getU64(payload[0:8]), true
  433. }
  434. func TestInsertDoesNotRewriteSchemaForRowIDUpdates(t *testing.T) {
  435. kv := newTestKVServer(t)
  436. defer kv.close()
  437. pool := newTestKVPool(kv, 2, 5*time.Second)
  438. defer pool.Close()
  439. schemas := NewSchemaManager(pool, "testdb")
  440. tables := NewTableManager(pool, schemas, "testdb")
  441. err := schemas.CreateTable(&Schema{
  442. Name: "users",
  443. Columns: []Column{
  444. {Name: "id", Type: "INTEGER", Nullable: false, PrimaryKey: true},
  445. {Name: "name", Type: "TEXT", Nullable: true},
  446. },
  447. })
  448. if err != nil {
  449. t.Fatalf("create table: %v", err)
  450. }
  451. initialSchemaWrites := kv.writeCount(":_schema:")
  452. if initialSchemaWrites != 1 {
  453. t.Fatalf("expected create table to write schema once, got %d", initialSchemaWrites)
  454. }
  455. for i := int64(1); i <= 3; i++ {
  456. err := tables.Insert("users", Row{"id": i, "name": fmt.Sprintf("user-%d", i)})
  457. if err != nil {
  458. t.Fatalf("insert %d: %v", i, err)
  459. }
  460. }
  461. if got := kv.writeCount(":_schema:"); got != initialSchemaWrites {
  462. t.Fatalf("expected inserts not to rewrite schema, got %d schema writes", got)
  463. }
  464. if got := kv.writeCount(":_sys:rowid:"); got != 0 {
  465. t.Fatalf("expected no rowid counter writes for inserts, got %d", got)
  466. }
  467. }
  468. func TestRowIDIsDerivedFromRowsAfterRestart(t *testing.T) {
  469. kv := newTestKVServer(t)
  470. defer kv.close()
  471. pool := newTestKVPool(kv, 2, 5*time.Second)
  472. defer pool.Close()
  473. schemas := NewSchemaManager(pool, "testdb")
  474. tables := NewTableManager(pool, schemas, "testdb")
  475. err := schemas.CreateTable(&Schema{
  476. Name: "events",
  477. Columns: []Column{
  478. {Name: "name", Type: "TEXT", Nullable: true},
  479. },
  480. })
  481. if err != nil {
  482. t.Fatalf("create table: %v", err)
  483. }
  484. for i := 1; i <= 2; i++ {
  485. err := tables.Insert("events", Row{"name": fmt.Sprintf("event-%d", i)})
  486. if err != nil {
  487. t.Fatalf("insert %d: %v", i, err)
  488. }
  489. }
  490. // Simulate a process restart: new managers have empty in-memory ROWID state
  491. // but the same durable KV rows.
  492. restartedSchemas := NewSchemaManager(pool, "testdb")
  493. restartedTables := NewTableManager(pool, restartedSchemas, "testdb")
  494. if err := restartedTables.Insert("events", Row{"name": "event-3"}); err != nil {
  495. t.Fatalf("insert after restart: %v", err)
  496. }
  497. if !kv.hasKey("testdb:_data:events:3") {
  498. t.Fatalf("expected restart insert to continue at rowid 3")
  499. }
  500. if got := kv.writeCount(":_sys:rowid:"); got != 0 {
  501. t.Fatalf("expected no rowid counter writes, got %d", got)
  502. }
  503. }
  504. func TestInsertDoesNotWriteDurableIndexEntries(t *testing.T) {
  505. kv := newTestKVServer(t)
  506. defer kv.close()
  507. pool := newTestKVPool(kv, 2, 5*time.Second)
  508. defer pool.Close()
  509. schemas := NewSchemaManager(pool, "testdb")
  510. tables := NewTableManager(pool, schemas, "testdb")
  511. err := schemas.CreateTable(&Schema{
  512. Name: "users",
  513. Columns: []Column{
  514. {Name: "id", Type: "INTEGER", Nullable: false, PrimaryKey: true},
  515. {Name: "status", Type: "TEXT", Nullable: false},
  516. },
  517. })
  518. if err != nil {
  519. t.Fatalf("create table: %v", err)
  520. }
  521. err = schemas.CreateIndex(&Index{
  522. Name: "idx_users_status",
  523. Table: "users",
  524. Columns: []IndexColumn{
  525. {Name: "status"},
  526. },
  527. })
  528. if err != nil {
  529. t.Fatalf("create index: %v", err)
  530. }
  531. for i := int64(1); i <= 3; i++ {
  532. status := "active"
  533. if i == 2 {
  534. status = "inactive"
  535. }
  536. err := tables.Insert("users", Row{"id": i, "status": status})
  537. if err != nil {
  538. t.Fatalf("insert %d: %v", i, err)
  539. }
  540. }
  541. if got := kv.writeCount(":idx:"); got != 0 {
  542. t.Fatalf("expected no durable index entry writes, got %d", got)
  543. }
  544. rows, err := tables.SelectByIndex("users", "idx_users_status", "active")
  545. if err != nil {
  546. t.Fatalf("select by index: %v", err)
  547. }
  548. if len(rows) != 2 {
  549. t.Fatalf("expected 2 active rows from derived index, got %d", len(rows))
  550. }
  551. }
  552. func TestIndexIsDerivedFromRowsAfterRestart(t *testing.T) {
  553. kv := newTestKVServer(t)
  554. defer kv.close()
  555. pool := newTestKVPool(kv, 2, 5*time.Second)
  556. defer pool.Close()
  557. schemas := NewSchemaManager(pool, "testdb")
  558. tables := NewTableManager(pool, schemas, "testdb")
  559. err := schemas.CreateTable(&Schema{
  560. Name: "users",
  561. Columns: []Column{
  562. {Name: "id", Type: "INTEGER", Nullable: false, PrimaryKey: true},
  563. {Name: "status", Type: "TEXT", Nullable: false},
  564. },
  565. })
  566. if err != nil {
  567. t.Fatalf("create table: %v", err)
  568. }
  569. err = schemas.CreateIndex(&Index{
  570. Name: "idx_users_status",
  571. Table: "users",
  572. Columns: []IndexColumn{
  573. {Name: "status"},
  574. },
  575. })
  576. if err != nil {
  577. t.Fatalf("create index: %v", err)
  578. }
  579. for i := int64(1); i <= 3; i++ {
  580. status := "active"
  581. if i == 3 {
  582. status = "inactive"
  583. }
  584. if err := tables.Insert("users", Row{"id": i, "status": status}); err != nil {
  585. t.Fatalf("insert %d: %v", i, err)
  586. }
  587. }
  588. restartedSchemas := NewSchemaManager(pool, "testdb")
  589. restartedTables := NewTableManager(pool, restartedSchemas, "testdb")
  590. rows, err := restartedTables.SelectByIndex("users", "idx_users_status", "active")
  591. if err != nil {
  592. t.Fatalf("select by index after restart: %v", err)
  593. }
  594. if len(rows) != 2 {
  595. t.Fatalf("expected 2 active rows from restart-derived index, got %d", len(rows))
  596. }
  597. if got := kv.writeCount(":idx:"); got != 0 {
  598. t.Fatalf("expected no durable index entry writes, got %d", got)
  599. }
  600. }