func main() { flag.Parse() // Set up profiling, if requested. if cpuprofile != "" { log.Info("Profiling enabled") f, err := os.Create(cpuprofile) if err != nil { log.Errorf("Unable to create path: %s", err.Error()) } defer closeFile(f) err = pprof.StartCPUProfile(f) if err != nil { log.Errorf("Unable to start CPU Profile: %s", err.Error()) } defer pprof.StopCPUProfile() } // Set logging log.SetLevel(logLevel) if logFile != "stdout" { f := createFile(logFile) defer closeFile(f) log.Infof("Redirecting logging to %s", logFile) log.SetOutput(f) } // Set the data directory. if flag.NArg() == 0 { flag.Usage() println("Data path argument required") log.Error("No data path supplied -- aborting") os.Exit(1) } dataPath := flag.Arg(0) createDir(dataPath) s := server.NewServer(dataPath, dbfile, snapAfter, host, port) go func() { log.Error(s.ListenAndServe(join).Error()) }() if !disableReporting { reportLaunch() } terminate := make(chan os.Signal, 1) signal.Notify(terminate, os.Interrupt) <-terminate log.Info("rqlite server stopped") }
// Query runs the supplied query against the sqlite database. It returns a slice of // RowResults. func (db *DB) Query(query string) (RowResults, error) { if !strings.HasPrefix(strings.ToUpper(query), "SELECT ") { log.Warnf("Query \"%s\" may modify the database", query) } rows, err := db.dbConn.Query(query) if err != nil { log.Errorf("failed to execute SQLite query: %s", err.Error()) return nil, err } defer func() { err = rows.Close() if err != nil { log.Errorf("failed to close rows: %s", err.Error()) } }() results := make(RowResults, 0) columns, _ := rows.Columns() rawResult := make([][]byte, len(columns)) dest := make([]interface{}, len(columns)) for i := range rawResult { dest[i] = &rawResult[i] // Pointers to each string in the interface slice } for rows.Next() { err = rows.Scan(dest...) if err != nil { log.Errorf("failed to scan SQLite row: %s", err.Error()) return nil, err } r := make(RowResult) for i, raw := range rawResult { if raw == nil { r[columns[i]] = "null" } else { r[columns[i]] = string(raw) } } results = append(results, r) } log.Debugf("Executed query successfully: %s", query) return results, 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) tableReadHandler(w http.ResponseWriter, req *http.Request) { log.Tracef("tableReadHandler for URL: %s", req.URL) s.metrics.queryReceived.Inc(1) var failures = make([]FailedSqlStmt, 0) vars := mux.Vars(req) tablename := vars["tablename"] log.Tracef("Getting data from table %s", tablename) startTime := time.Now() stmt := "Select * from " + tablename 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.Errorf("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()) } } }
func reportLaunch() { json := fmt.Sprintf(`{"os": "%s", "arch": "%s", "app": "rqlite"}`, runtime.GOOS, runtime.GOARCH) data := bytes.NewBufferString(json) client := http.Client{Timeout: time.Duration(5 * time.Second)} go func() { _, err := client.Post("https://logs-01.loggly.com/inputs/8a0edd84-92ba-46e4-ada8-c529d0f105af/tag/reporting/", "application/json", data) if err != nil { log.Errorf("Report launch failed: %s", err.Error()) } }() }
func createFile(path string) *os.File { usr, _ := user.Current() dir := usr.HomeDir // Check in case of paths like "/something/~/something/" if path[:2] == "~/" { path = strings.Replace(path, "~/", dir+"/", 1) } if err := os.MkdirAll(filepath.Dir(path), 0744); err != nil { log.Errorf("Unable to create path: %s", err.Error()) os.Exit(1) } f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) if err != nil && !strings.Contains(err.Error(), "is a directory") { log.Errorf("Unable to open file: %s", err.Error()) os.Exit(1) } return f }
// 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 }
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()) } } }
// ListenAndServe starts the server. func (s *Server) ListenAndServe(leader string) error { var err error log.Infof("Initializing Raft Server: %s", s.path) // Initialize and start Raft server. transporter := raft.NewHTTPTransporter("/raft", 200*time.Millisecond) stateMachine := NewDbStateMachine(s.dbPath) s.raftServer, err = raft.NewServer(s.name, s.path, transporter, stateMachine, s.db, "") if err != nil { log.Errorf("Failed to create new Raft server: %s", err.Error()) return err } log.Info("Loading latest snapshot, if any, from disk") if err := s.raftServer.LoadSnapshot(); err != nil && os.IsNotExist(err) { log.Info("no snapshot found") } else if err != nil { log.Errorf("Error loading snapshot: %s", err.Error()) } transporter.Install(s.raftServer, s) if err := s.raftServer.Start(); err != nil { log.Errorf("Error starting raft server: %s", err.Error()) } if leader != "" { // Join to leader if specified. log.Infof("Attempting to join leader at %s", leader) if !s.raftServer.IsLogEmpty() { log.Error("Cannot join with an existing log") return errors.New("Cannot join with an existing log") } if err := s.Join(leader); err != nil { log.Errorf("Failed to join leader: %s", err.Error()) return err } } else if s.raftServer.IsLogEmpty() { // Initialize the server by joining itself. log.Info("Initializing new cluster") _, err := s.raftServer.Do(&raft.DefaultJoinCommand{ Name: s.raftServer.Name(), ConnectionString: s.connectionString(), }) if err != nil { log.Errorf("Failed to join to self: %s", err.Error()) } } else { log.Info("Recovered from log") } log.Info("Initializing HTTP server") // Initialize and start HTTP server. s.httpServer = &http.Server{ Addr: fmt.Sprintf(":%d", s.port), Handler: s.router, } s.router.HandleFunc("/statistics", s.serveStatistics).Methods("GET") s.router.HandleFunc("/diagnostics", s.serveDiagnostics).Methods("GET") s.router.HandleFunc("/raft", s.serveRaftInfo).Methods("GET") s.router.HandleFunc("/db", s.readHandler).Methods("GET") s.router.HandleFunc("/db", s.writeHandler).Methods("POST") s.router.HandleFunc("/join", s.joinHandler).Methods("POST") log.Infof("Listening at %s", s.connectionString()) return s.httpServer.ListenAndServe() }
func createDir(path string) { if err := os.MkdirAll(path, 0744); err != nil { log.Errorf("Unable to create path: %s", err.Error()) os.Exit(1) } }
func closeFile(f *os.File) { if err := f.Close(); err != nil { log.Errorf("Unable to close file: %s", err.Error()) os.Exit(1) } }