2
0

schema_test.go 19 KB

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