index: gracefully handle duplicate module indexes (#1645)

* index: gracefully handle duplicate module indexes

* fix memory impl
This commit is contained in:
Marwan Sulaiman
2020-07-03 10:42:51 -04:00
committed by GitHub
parent ae69e1f57d
commit d6f06d0302
5 changed files with 62 additions and 5 deletions
+21
View File
@@ -6,12 +6,16 @@ import (
"testing"
"time"
"github.com/gomods/athens/pkg/errors"
"github.com/gomods/athens/pkg/index"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/technosophos/moniker"
)
// RunTests runs compliance tests for the given Indexer implementation.
// clearIndex is a function that must clear the entire storage so that
// tests can assume a clean state.
func RunTests(t *testing.T, indexer index.Indexer, clearIndex func() error) {
if err := clearIndex(); err != nil {
t.Fatal(err)
@@ -80,6 +84,23 @@ func RunTests(t *testing.T, indexer index.Indexer, clearIndex func() error) {
},
limit: 0,
},
{
name: "duplicate module version",
desc: "if we try to index a module that already exists, a KindAlreadyExists must be returned",
preTest: func(t *testing.T) ([]*index.Line, time.Time) {
m := &index.Line{Path: "gomods.io/tobeduplicated", Version: "v0.1.0"}
err := indexer.Index(context.Background(), m.Path, m.Version)
if err != nil {
t.Fatal(err)
}
err = indexer.Index(context.Background(), m.Path, m.Version)
if !errors.Is(err, errors.KindAlreadyExists) {
t.Fatalf("expected an error of kind AlreadyExists but got %s", errors.KindText(err))
}
return []*index.Line{m}, time.Time{}
},
limit: 2000,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
+7 -1
View File
@@ -2,6 +2,7 @@ package mem
import (
"context"
"fmt"
"sync"
"time"
@@ -22,12 +23,17 @@ type indexer struct {
func (i *indexer) Index(ctx context.Context, mod, ver string) error {
const op errors.Op = "mem.Index"
i.mu.Lock()
defer i.mu.Unlock()
for _, l := range i.lines {
if l.Path == mod && l.Version == ver {
return errors.E(op, fmt.Sprintf("%s@%s already indexed", mod, ver), errors.KindAlreadyExists)
}
}
i.lines = append(i.lines, &index.Line{
Path: mod,
Version: ver,
Timestamp: time.Now(),
})
i.mu.Unlock()
return nil
}
+16 -1
View File
@@ -12,6 +12,9 @@ import (
"github.com/gomods/athens/pkg/index"
)
// New returns a new Indexer with a MySQL implementation.
// It attempts to connect to the DB and create the index table
// if it doesn ot already exist.
func New(cfg *config.MySQL) (index.Indexer, error) {
dataSource := getMySQLSource(cfg)
db, err := sql.Open("mysql", dataSource)
@@ -65,7 +68,7 @@ func (i *indexer) Index(ctx context.Context, mod, ver string) error {
time.Now().Format(time.RFC3339Nano),
)
if err != nil {
return errors.E(op, err)
return errors.E(op, err, getKind(err))
}
return nil
}
@@ -103,3 +106,15 @@ func getMySQLSource(cfg *config.MySQL) string {
c.Params = cfg.Params
return c.FormatDSN()
}
func getKind(err error) int {
mysqlErr, ok := err.(*mysql.MySQLError)
if !ok {
return errors.KindUnexpected
}
switch mysqlErr.Number {
case 1062:
return errors.KindAlreadyExists
}
return errors.KindUnexpected
}
+17 -2
View File
@@ -8,13 +8,16 @@ import (
"time"
// register the driver with database/sql
_ "github.com/lib/pq"
"github.com/lib/pq"
"github.com/gomods/athens/pkg/config"
"github.com/gomods/athens/pkg/errors"
"github.com/gomods/athens/pkg/index"
)
// New returns a new Indexer with a PostgreSQL implementation.
// It attempts to connect to the DB and create the index table
// if it doesn ot already exist.
func New(cfg *config.Postgres) (index.Indexer, error) {
dataSource := getPostgresSource(cfg)
db, err := sql.Open("postgres", dataSource)
@@ -64,7 +67,7 @@ func (i *indexer) Index(ctx context.Context, mod, ver string) error {
time.Now().Format(time.RFC3339Nano),
)
if err != nil {
return errors.E(op, err)
return errors.E(op, err, getKind(err))
}
return nil
}
@@ -104,3 +107,15 @@ func getPostgresSource(cfg *config.Postgres) string {
}
return strings.Join(args, " ")
}
func getKind(err error) int {
pqerr, ok := err.(*pq.Error)
if !ok {
return errors.KindUnexpected
}
switch pqerr.Code {
case "23505":
return errors.KindAlreadyExists
}
return errors.KindUnexpected
}
+1 -1
View File
@@ -74,7 +74,7 @@ func (s *stasher) Stash(ctx context.Context, mod, ver string) (string, error) {
return "", errors.E(op, err)
}
err = s.indexer.Index(ctx, mod, v.Semver)
if err != nil {
if err != nil && !errors.Is(err, errors.KindAlreadyExists) {
return "", errors.E(op, err)
}
return v.Semver, nil