sqlite_concurrent_demo.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. package main
  2. import (
  3. "database/sql"
  4. "io"
  5. "log"
  6. "os"
  7. "strconv"
  8. "sync"
  9. "syscall"
  10. "time"
  11. _ "github.com/mattn/go-sqlite3"
  12. "golang.org/x/sys/unix"
  13. )
  14. const (
  15. File = "demo.db"
  16. // DSN points to a local DB file without cache enabled.
  17. DSN = "file:" + File
  18. )
  19. func setup() {
  20. _ = os.Remove(File)
  21. db, err := sql.Open("sqlite3", DSN)
  22. if err != nil {
  23. log.Fatalf("open: %v", err)
  24. }
  25. defer func() {
  26. _ = db.Close()
  27. log.Println("finishing setup")
  28. }()
  29. if _, err = db.Exec(`
  30. CREATE TABLE foo (
  31. id SERIAL PRIMARY KEY,
  32. description VARCHAR(255)
  33. );
  34. `); err != nil {
  35. log.Fatalf("creating schema: %v", err)
  36. }
  37. }
  38. func ping(wg *sync.WaitGroup, id int, start, linger time.Duration) {
  39. logger := log.New(os.Stderr, "("+strconv.Itoa(id)+") ", log.LstdFlags)
  40. defer func() {
  41. wg.Done()
  42. }()
  43. time.Sleep(start)
  44. f, err := os.Open(File)
  45. if err != nil {
  46. logger.Fatalf("file open: %v", err)
  47. }
  48. lock := unix.Flock_t{
  49. Start: 0,
  50. Len: 0,
  51. Pid: int32(os.Getpid()),
  52. Type: syscall.F_WRLCK,
  53. Whence: io.SeekStart,
  54. }
  55. for {
  56. if err := unix.FcntlFlock(f.Fd(), unix.F_GETLK, &lock); err != nil {
  57. logger.Fatalf("file get lock info: %v", err)
  58. }
  59. // if err := unix.Flock(int(f.Fd()), unix.LOCK_SH); err != nil {
  60. // logger.Fatalf("file lock: %v", err)
  61. // }
  62. if int(lock.Type) == syscall.F_UNLCK {
  63. logger.Println("locked DB")
  64. break
  65. }
  66. logger.Println("attempted to locked DB, but it was locked")
  67. time.Sleep(100 * time.Millisecond)
  68. }
  69. defer func() {
  70. if err := unix.FcntlFlock(f.Fd(), unix.F_SETLK, &lock); err != nil {
  71. logger.Fatalf("file unlock: %v", err)
  72. }
  73. logger.Println("unlocked DB")
  74. }()
  75. db, err := sql.Open("sqlite3", DSN)
  76. if err != nil {
  77. logger.Fatalf("sql open: %v", err)
  78. }
  79. defer db.Close()
  80. logger.Println("open: OK")
  81. res, err := db.Exec(`
  82. INSERT INTO foo(id, description)
  83. VALUES (?, ?)
  84. `, id, strconv.Itoa(id))
  85. if err != nil {
  86. logger.Fatalf("insert: %v", err)
  87. } else {
  88. ra, _ := res.RowsAffected()
  89. last, _ := res.LastInsertId()
  90. logger.Printf("inserted count %d / last %d", ra, last)
  91. }
  92. row := db.QueryRow(`
  93. SELECT COUNT(id)
  94. FROM foo
  95. `)
  96. count := 0
  97. if err = row.Scan(&count); err != nil {
  98. logger.Fatalf("scanning: %v", err)
  99. }
  100. logger.Printf("select %d rows: OK", count)
  101. time.Sleep(linger)
  102. if err := db.Close(); err != nil {
  103. logger.Fatalf("close: %v", err)
  104. }
  105. logger.Println("close: OK")
  106. }
  107. func main() {
  108. setup()
  109. wg := sync.WaitGroup{}
  110. wg.Add(2)
  111. go ping(&wg, 1, 0, 2*time.Second)
  112. go ping(&wg, 2, time.Second, 0)
  113. wg.Wait()
  114. log.Println("all done")
  115. }