github.com/uber/kraken@v0.1.4/lib/persistedretry/writeback/store.go (about) 1 // Copyright (c) 2016-2019 Uber Technologies, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package writeback 15 16 import ( 17 "errors" 18 "fmt" 19 "time" 20 21 "github.com/uber/kraken/lib/persistedretry" 22 23 "github.com/jmoiron/sqlx" 24 "github.com/mattn/go-sqlite3" 25 ) 26 27 // Store stores writeback tasks. 28 type Store struct { 29 db *sqlx.DB 30 } 31 32 // NewStore creates a new Store. 33 func NewStore(db *sqlx.DB) *Store { 34 return &Store{db} 35 } 36 37 // GetPending returns all pending tasks. 38 func (s *Store) GetPending() ([]persistedretry.Task, error) { 39 return s.selectStatus("pending") 40 } 41 42 // GetFailed returns all failed tasks. 43 func (s *Store) GetFailed() ([]persistedretry.Task, error) { 44 return s.selectStatus("failed") 45 } 46 47 // AddPending adds r as pending. 48 func (s *Store) AddPending(r persistedretry.Task) error { 49 return s.addWithStatus(r, "pending") 50 } 51 52 // AddFailed adds r as failed. 53 func (s *Store) AddFailed(r persistedretry.Task) error { 54 return s.addWithStatus(r, "failed") 55 } 56 57 // MarkPending marks r as pending. 58 func (s *Store) MarkPending(r persistedretry.Task) error { 59 res, err := s.db.NamedExec(` 60 UPDATE writeback_task 61 SET status = "pending" 62 WHERE namespace=:namespace AND name=:name 63 `, r.(*Task)) 64 if err != nil { 65 return err 66 } 67 if n, err := res.RowsAffected(); err != nil { 68 panic("driver does not support RowsAffected") 69 } else if n == 0 { 70 return persistedretry.ErrTaskNotFound 71 } 72 return nil 73 } 74 75 // MarkFailed marks r as failed. 76 func (s *Store) MarkFailed(r persistedretry.Task) error { 77 t := r.(*Task) 78 res, err := s.db.NamedExec(` 79 UPDATE writeback_task 80 SET last_attempt = CURRENT_TIMESTAMP, 81 failures = failures + 1, 82 status = "failed" 83 WHERE namespace=:namespace AND name=:name 84 `, t) 85 if err != nil { 86 return err 87 } 88 if n, err := res.RowsAffected(); err != nil { 89 panic("driver does not support RowsAffected") 90 } else if n == 0 { 91 return persistedretry.ErrTaskNotFound 92 } 93 t.Failures++ 94 t.LastAttempt = time.Now() 95 return nil 96 } 97 98 // Remove removes r. 99 func (s *Store) Remove(r persistedretry.Task) error { 100 _, err := s.db.NamedExec(` 101 DELETE FROM writeback_task 102 WHERE namespace=:namespace AND name=:name 103 `, r.(*Task)) 104 return err 105 } 106 107 // Find finds tasks matching query. 108 func (s *Store) Find(query interface{}) ([]persistedretry.Task, error) { 109 var tasks []*Task 110 var err error 111 switch q := query.(type) { 112 case *NameQuery: 113 err = s.db.Select(&tasks, ` 114 SELECT namespace, name, created_at, last_attempt, failures, delay 115 FROM writeback_task 116 WHERE name=? 117 `, q.name) 118 default: 119 return nil, errors.New("unknown query type") 120 } 121 if err != nil { 122 return nil, err 123 } 124 return convert(tasks), nil 125 } 126 127 func (s *Store) addWithStatus(r persistedretry.Task, status string) error { 128 query := fmt.Sprintf(` 129 INSERT INTO writeback_task ( 130 namespace, 131 name, 132 last_attempt, 133 failures, 134 delay, 135 status 136 ) VALUES ( 137 :namespace, 138 :name, 139 :last_attempt, 140 :failures, 141 :delay, 142 %q 143 ) 144 `, status) 145 _, err := s.db.NamedExec(query, r.(*Task)) 146 if se, ok := err.(sqlite3.Error); ok { 147 if se.ExtendedCode == sqlite3.ErrConstraintPrimaryKey { 148 return persistedretry.ErrTaskExists 149 } 150 } 151 return err 152 } 153 154 func (s *Store) selectStatus(status string) ([]persistedretry.Task, error) { 155 var tasks []*Task 156 err := s.db.Select(&tasks, ` 157 SELECT namespace, name, created_at, last_attempt, failures, delay 158 FROM writeback_task 159 WHERE status=? 160 `, status) 161 if err != nil { 162 return nil, err 163 } 164 return convert(tasks), nil 165 } 166 167 func convert(tasks []*Task) (result []persistedretry.Task) { 168 for _, t := range tasks { 169 result = append(result, t) 170 } 171 return result 172 }