Kiril
Kiril

Reputation: 6219

Synchronization in a Go-lang webapplication

I'm writing a golang web application. The web application accesses the file system (reading and writing) and an sqlite3 database file.

Question 1: How can I synchronize file system access in Go?

type DataObject struct {
  data []byte
}

func (*d DataObject) Write() {
   //
   // Synchronization ?
   //
   ioutil.WriteFile("file.name", d.data, 0644)
   //
   // Stop synchronization ?
   //
}

Question 2: Do I need to synchronize sqlite3 database file access?

type SqlObject struct {
  sqldata string
}

func (*s SqlObject) Store() error {
  //
  // Open the file, do I need some sort of synchronization?
  //
  con, err := sqlite.Open("database/datafile.db")
  if err != nil {
    return err
  }
  defer con.Close()

  err = con.Exec("INSERT INTO data(sqldata) values(?)", s.sqldata)
  if err != nil {
    return err
  }
  return nil
}

I'm using the gosqlite3 driver (http://code.google.com/p/gosqlite/).

Upvotes: 5

Views: 4780

Answers (4)

Harel
Harel

Reputation: 2039

I know its an old question but since I had the same "db lock" problem, and a transaction resolved it for me, I thought I'd mention it here:

db, err := sql.Open("sqlite3", db_path)
if err != nil {
    log.Printf("Cannot connect to database: %s\n", err ) 
}
defer db.Close()
tx, _ := db.Begin() 
var value string
err = tx.QueryRow("SELECT X FROM Y").Scan(&value)
tx.Rollback() // or: tx.Commit()

Upvotes: -1

Daniel Heath
Daniel Heath

Reputation: 414

1) You should use a read/write mutex (in the go std library). Code would look something like:

import "sync" // http://golang.org/pkg/sync/
const (
    filename = "file.name"
)
var globalFileLock sync.RWMutex

type DataObject struct {
  data []byte
}

func (*d DataObject) Write() {
   globalFileLock.Lock()
   defer globalFileLock.Unlock()
   ioutil.WriteFile(filename, d.data, 0644)
}

func (*d DataObject) Read() {
   globalFileLock.RLock()
   defer globalFileLock.RUnlock()
   d.data = ioutil.ReadFile(filename)
}

2) As you haven't posted the 'import' section from your program, I don't know which sqlite driver you're using.

If you are using database/sql to open the database connection, the driver provides concurrency control between goroutines.

Upvotes: 3

Ask Bjørn Hansen
Ask Bjørn Hansen

Reputation: 6943

For files it depends on your application. If you only have one goroutine writing to the file, you shouldn't need to. If more than one then it depends:

If you are coordinating between different processes (programs) you can use flock (and it probably won't be fun).

If you are coordinating multiple goroutines in your program you can use mutexes or you can see if you can re-organize the program so just one routine writes to the file and the others send updates via channels.

For SQLite I believe the easiest way would be to just keep one sqlite connection open and use that from the various goroutines; though it does support multiple processes having it open at once and if your system does many concurrent reads that might be faster (it uses a global lock for writing).

Upvotes: 7

Joshua Tacoma
Joshua Tacoma

Reputation: 407

Question 1

The "sync" package provides Lock and RWLock for synchronizing access to resources in the conventional way. While there's nothing wrong with that, I like playing around with idioms so I might do something like this:

package main

import "fmt"

func SyncFile(path string) chan<- func(string) {
    ch := make(chan func(string))
    go func() {
        for process := range ch {
            process(path)
        }
    }()
    return ch
}

func main() {
    data := SyncFile("data.name")
    data <- func(path string) {
        fmt.Println(path)
    }
}

Then if you only access the file through this channel of funcs, access will be synchronized. This is reliable, but you may be able to find something more efficient.

Question 2

The gosqlite3 driver is just a Go binding to libsqlite3, so the SQLite FAQ applies (and it looks like you're in the clear on this). If you want both of these to be synchronized with each other, then just wrap the SQLite usage inside the file access synchronization code.

Upvotes: 4

Related Questions