func (s *Server) execute(tx bool, stmts []string) ([]FailedSqlStmt, error) { var failures = make([]FailedSqlStmt, 0) if tx { log.Trace("Transaction requested") s.metrics.executeTxReceived.Inc(1) _, err := s.raftServer.Do(command.NewTransactionExecuteCommandSet(stmts)) if err != nil { log.Tracef("Transaction failed: %s", err.Error()) s.metrics.executeFail.Inc(1) failures = append(failures, FailedSqlStmt{stmts[0], err.Error()}) } else { s.metrics.executeSuccess.Inc(1) } } else { log.Trace("No transaction requested") for i := range stmts { _, err := s.raftServer.Do(command.NewExecuteCommand(stmts[i])) if err != nil { log.Tracef("Execute statement %s failed: %s", stmts[i], err.Error()) s.metrics.executeFail.Inc(1) failures = append(failures, FailedSqlStmt{stmts[i], err.Error()}) } else { s.metrics.executeSuccess.Inc(1) } } } return failures, nil }
// Recovery restores the state of the database using the given data. func (d *DbStateMachine) Recovery(b []byte) error { log.Tracef("Restoring database state to path: %s", d.dbpath) err := ioutil.WriteFile(d.dbpath, b, os.ModePerm) if err != nil { log.Errorf("Failed to recover state: %s", err.Error()) return err } log.Tracef("Database restored successfully to %s", d.dbpath) return nil }
// Save captures the state of the database. The caller must ensure that // no transaction is taking place during this call. // // http://sqlite.org/howtocorrupt.html states it is safe to do this // as long as no transaction is in progress. func (d *DbStateMachine) Save() ([]byte, error) { log.Tracef("Capturing database state from path: %s", d.dbpath) b, err := ioutil.ReadFile(d.dbpath) if err != nil { log.Errorf("Failed to save state: %s", err.Error()) return nil, err } log.Tracef("Database state successfully saved to %s", d.dbpath) return b, nil }
func (s *Server) readHandler(w http.ResponseWriter, req *http.Request) { log.Tracef("readHandler for URL: %s", req.URL) s.metrics.queryReceived.Inc(1) var failures = make([]FailedSqlStmt, 0) // Get the query statement stmt, err := stmtParam(req) if err != nil { log.Tracef("Bad HTTP request: %s", err.Error()) w.WriteHeader(http.StatusBadRequest) s.metrics.queryFail.Inc(1) return } startTime := time.Now() r, err := s.db.Query(stmt) if err != nil { log.Tracef("Bad SQL statement: %s", err.Error()) s.metrics.queryFail.Inc(1) failures = append(failures, FailedSqlStmt{stmt, err.Error()}) } else { s.metrics.querySuccess.Inc(1) } duration := time.Since(startTime) rr := QueryResponse{Failures: failures, Rows: r} if e, _ := isExplain(req); e { rr.Time = duration.String() } pretty, _ := isPretty(req) var b []byte if pretty { b, err = json.MarshalIndent(rr, "", " ") } else { b, err = json.Marshal(rr) } if err != nil { log.Tracef("Failed to marshal JSON data: %s", err.Error()) http.Error(w, err.Error(), http.StatusBadRequest) // Internal error actually } else { _, err = w.Write([]byte(b)) if err != nil { log.Errorf("Error writting JSON data: %s", err.Error()) } } }
// NewDbStateMachine returns a StateMachine for capturing and restoring // the state of an sqlite database. func NewDbStateMachine(path string) *DbStateMachine { d := &DbStateMachine{ dbpath: path, } log.Tracef("New DB state machine created with path: %s", path) return d }
// Open an existing database, creating it if it does not exist. func Open(dbPath string) *DB { log.Tracef("Opening SQLite database path at %s", dbPath) dbc, err := sql.Open("sqlite3", dbPath) if err != nil { log.Error(err.Error()) return nil } return &DB{ dbConn: dbc, } }
// Apply executes a set of sqlite statements, within a transaction. All statements // will take effect, or none. func (c *TransactionExecuteCommandSet) Apply(server raft.Server) (interface{}, error) { log.Tracef("Applying TransactionExecuteCommandSet of size %d", len(c.Stmts)) commitSuccess := false db := server.Context().(*db.DB) defer func() { if !commitSuccess { err := db.RollbackTransaction() if err != nil { log.Errorf("Failed to rollback transaction: %s", err.Error()) } } }() err := db.StartTransaction() if err != nil { log.Errorf("Failed to start transaction: %s", err.Error()) return nil, err } for i := range c.Stmts { err = db.Execute(c.Stmts[i]) if err != nil { log.Errorf("Failed to execute statement within transaction: %s", err.Error()) return nil, err } } if err = db.CommitTransaction(); err != nil { log.Errorf("Failed to commit transaction: %s", err.Error()) return nil, err } commitSuccess = true return nil, nil }
// New creates a new database. Deletes any existing database. func New(dbPath string) *DB { log.Tracef("Removing any existing SQLite database at %s", dbPath) _ = os.Remove(dbPath) return Open(dbPath) }
func (s *Server) writeHandler(w http.ResponseWriter, req *http.Request) { s.mutex.Lock() defer s.mutex.Unlock() if s.raftServer.State() != "leader" { s.leaderRedirect(w, req) return } log.Tracef("writeHandler for URL: %s", req.URL) s.metrics.executeReceived.Inc(1) currentIndex := s.raftServer.CommitIndex() count := currentIndex - s.snapConf.lastIndex if uint64(count) > s.snapConf.snapshotAfter { log.Info("Committed log entries snapshot threshold reached, starting snapshot") err := s.raftServer.TakeSnapshot() s.logSnapshot(err, currentIndex, count) s.snapConf.lastIndex = currentIndex s.metrics.snapshotCreated.Inc(1) } // Read the value from the POST body. b, err := ioutil.ReadAll(req.Body) if err != nil { log.Tracef("Bad HTTP request: %s", err.Error()) s.metrics.executeFail.Inc(1) w.WriteHeader(http.StatusBadRequest) return } stmts := strings.Split(string(b), ";") if stmts[len(stmts)-1] == "" { stmts = stmts[:len(stmts)-1] } log.Tracef("Execute statement contains %d commands", len(stmts)) if len(stmts) == 0 { log.Trace("No database execute commands supplied") s.metrics.executeFail.Inc(1) w.WriteHeader(http.StatusBadRequest) return } transaction, _ := isTransaction(req) startTime := time.Now() failures, err := s.execute(transaction, stmts) if err != nil { log.Errorf("Database mutation failed: %s", err.Error()) http.Error(w, err.Error(), http.StatusInternalServerError) return } duration := time.Since(startTime) wr := StmtResponse{Failures: failures} if e, _ := isExplain(req); e { wr.Time = duration.String() } pretty, _ := isPretty(req) if pretty { b, err = json.MarshalIndent(wr, "", " ") } else { b, err = json.Marshal(wr) } if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } else { _, err = w.Write([]byte(b)) if err != nil { log.Errorf("Error writting JSON data: %s", err.Error()) } } }
// Apply executes an sqlite statement. func (c *ExecuteCommand) Apply(server raft.Server) (interface{}, error) { log.Tracef("Applying ExecuteCommand: '%s'", c.Stmt) db := server.Context().(*db.DB) return nil, db.Execute(c.Stmt) }