1 package repositories 2 3 import ( 4 "database/sql" 5 "fmt" 6 7 lru "github.com/hashicorp/golang-lru" 8 "github.com/pkg/errors" 9 10 e "repodiff/entities" 11 repoSQL "repodiff/persistence/sql" 12 ) 13 14 var cacheSingleton *lru.Cache 15 16 const cacheSize = 1024 17 18 type source struct { 19 db *sql.DB 20 } 21 22 func (s source) getOrCreateURLBranchID(url, branch string) (int16, error) { 23 url = protocolStrippedURL(url) 24 id, ok := cacheSingleton.Get(cacheKey(url, branch)) 25 if ok { 26 return id.(int16), nil 27 } 28 val, err := s.getOrCreateURLBranchIDPersistence(url, branch) 29 if err != nil { 30 return 0, err 31 } 32 cacheSingleton.Add(cacheKey(url, branch), val) 33 return val, nil 34 } 35 36 func (s source) getOrCreateURLBranchIDPersistence(url, branch string) (int16, error) { 37 id, err := s.getIDByURLBranch(url, branch) 38 if err == nil { 39 return id, nil 40 } 41 s.insertIgnoreError(url, branch) 42 return s.getIDByURLBranch(url, branch) 43 } 44 45 func (s source) insertIgnoreError(url, branch string) { 46 repoSQL.SingleTransactionInsert( 47 s.db, 48 `INSERT INTO id_to_url_branch ( 49 url, 50 branch 51 ) VALUES (?, ?)`, 52 [][]interface{}{ 53 []interface{}{ 54 url, 55 branch, 56 }, 57 }, 58 ) 59 } 60 61 func (s source) getIDByURLBranch(url, branch string) (int16, error) { 62 var id *int16 63 repoSQL.Select( 64 s.db, 65 func(row *sql.Rows) { 66 id = new(int16) 67 row.Scan(id) 68 }, 69 "SELECT id FROM id_to_url_branch WHERE url = ? AND branch = ?", 70 url, 71 branch, 72 ) 73 if id == nil { 74 return 0, errors.New(fmt.Sprintf("No ID found for %s %s", url, branch)) 75 } 76 return *id, nil 77 } 78 79 func (s source) GetURLBranchByID(id int16) (string, string, error) { 80 urlBranchPair, ok := cacheSingleton.Get(id) 81 if ok { 82 asSlice := urlBranchPair.([]string) 83 return asSlice[0], asSlice[1], nil 84 } 85 url, branch, err := s.getURLBranchByIDPersistence(id) 86 if err == nil { 87 cacheSingleton.Add(id, []string{url, branch}) 88 } 89 return url, branch, err 90 } 91 92 func (s source) getURLBranchByIDPersistence(id int16) (string, string, error) { 93 url := "" 94 branch := "" 95 repoSQL.Select( 96 s.db, 97 func(row *sql.Rows) { 98 row.Scan(&url, &branch) 99 }, 100 "SELECT url, branch FROM id_to_url_branch WHERE id = ?", 101 id, 102 ) 103 if url == "" { 104 return "", "", errors.New(fmt.Sprintf("No matching records for ID %d", id)) 105 } 106 return url, branch, nil 107 } 108 109 func (s source) DiffTargetToMapped(target e.DiffTarget) (e.MappedDiffTarget, error) { 110 upstream, errU := s.getOrCreateURLBranchID( 111 target.Upstream.URL, 112 target.Upstream.Branch, 113 ) 114 downstream, errD := s.getOrCreateURLBranchID( 115 target.Downstream.URL, 116 target.Downstream.Branch, 117 ) 118 if errU != nil || errD != nil { 119 return e.MappedDiffTarget{}, errors.New("Failed interacting with the database") 120 } 121 return e.MappedDiffTarget{ 122 UpstreamTarget: upstream, 123 DownstreamTarget: downstream, 124 }, nil 125 } 126 127 func NewSourceRepository() (source, error) { 128 db, err := repoSQL.GetDBConnectionPool() 129 return source{ 130 db: db, 131 }, errors.Wrap(err, "Could not establish a database connection") 132 } 133 134 func cacheKey(url, branch string) string { 135 return fmt.Sprintf("%s:%s", url, branch) 136 } 137 138 func init() { 139 cacheSingleton, _ = lru.New(cacheSize) 140 } 141