update wal and readdb
This commit is contained in:
parent
eb8cd9cc52
commit
fc891409ca
|
@ -20,7 +20,6 @@ import (
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/sorintlab/agola/internal/db"
|
"github.com/sorintlab/agola/internal/db"
|
||||||
"github.com/sorintlab/agola/internal/services/configstore/common"
|
|
||||||
"github.com/sorintlab/agola/internal/services/configstore/readdb"
|
"github.com/sorintlab/agola/internal/services/configstore/readdb"
|
||||||
"github.com/sorintlab/agola/internal/services/types"
|
"github.com/sorintlab/agola/internal/services/types"
|
||||||
"github.com/sorintlab/agola/internal/util"
|
"github.com/sorintlab/agola/internal/util"
|
||||||
|
@ -110,7 +109,8 @@ func (s *CommandHandler) CreateProjectGroup(ctx context.Context, projectGroup *t
|
||||||
actions := []*wal.Action{
|
actions := []*wal.Action{
|
||||||
{
|
{
|
||||||
ActionType: wal.ActionTypePut,
|
ActionType: wal.ActionTypePut,
|
||||||
Path: common.StorageProjectGroupFile(projectGroup.ID),
|
DataType: string(types.ConfigTypeProjectGroup),
|
||||||
|
ID: projectGroup.ID,
|
||||||
Data: pcj,
|
Data: pcj,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -185,7 +185,8 @@ func (s *CommandHandler) CreateProject(ctx context.Context, project *types.Proje
|
||||||
actions := []*wal.Action{
|
actions := []*wal.Action{
|
||||||
{
|
{
|
||||||
ActionType: wal.ActionTypePut,
|
ActionType: wal.ActionTypePut,
|
||||||
Path: common.StorageProjectFile(project.ID),
|
DataType: string(types.ConfigTypeProject),
|
||||||
|
ID: project.ID,
|
||||||
Data: pcj,
|
Data: pcj,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -231,7 +232,8 @@ func (s *CommandHandler) DeleteProject(ctx context.Context, projectRef string) e
|
||||||
actions := []*wal.Action{
|
actions := []*wal.Action{
|
||||||
{
|
{
|
||||||
ActionType: wal.ActionTypeDelete,
|
ActionType: wal.ActionTypeDelete,
|
||||||
Path: common.StorageProjectFile(project.ID),
|
DataType: string(types.ConfigTypeProject),
|
||||||
|
ID: project.ID,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,12 +337,14 @@ func (s *CommandHandler) CreateUser(ctx context.Context, req *CreateUserRequest)
|
||||||
actions := []*wal.Action{
|
actions := []*wal.Action{
|
||||||
{
|
{
|
||||||
ActionType: wal.ActionTypePut,
|
ActionType: wal.ActionTypePut,
|
||||||
Path: common.StorageUserFile(user.ID),
|
DataType: string(types.ConfigTypeUser),
|
||||||
|
ID: user.ID,
|
||||||
Data: userj,
|
Data: userj,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ActionType: wal.ActionTypePut,
|
ActionType: wal.ActionTypePut,
|
||||||
Path: common.StorageProjectGroupFile(pg.ID),
|
DataType: string(types.ConfigTypeProjectGroup),
|
||||||
|
ID: pg.ID,
|
||||||
Data: pgj,
|
Data: pgj,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -380,7 +384,8 @@ func (s *CommandHandler) DeleteUser(ctx context.Context, userName string) error
|
||||||
actions := []*wal.Action{
|
actions := []*wal.Action{
|
||||||
{
|
{
|
||||||
ActionType: wal.ActionTypeDelete,
|
ActionType: wal.ActionTypeDelete,
|
||||||
Path: common.StorageUserFile(user.ID),
|
DataType: string(types.ConfigTypeUser),
|
||||||
|
ID: user.ID,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -474,7 +479,8 @@ func (s *CommandHandler) CreateUserLA(ctx context.Context, req *CreateUserLARequ
|
||||||
actions := []*wal.Action{
|
actions := []*wal.Action{
|
||||||
{
|
{
|
||||||
ActionType: wal.ActionTypePut,
|
ActionType: wal.ActionTypePut,
|
||||||
Path: common.StorageUserFile(user.ID),
|
DataType: string(types.ConfigTypeUser),
|
||||||
|
ID: user.ID,
|
||||||
Data: userj,
|
Data: userj,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -532,7 +538,8 @@ func (s *CommandHandler) DeleteUserLA(ctx context.Context, userName, laID string
|
||||||
actions := []*wal.Action{
|
actions := []*wal.Action{
|
||||||
{
|
{
|
||||||
ActionType: wal.ActionTypePut,
|
ActionType: wal.ActionTypePut,
|
||||||
Path: common.StorageUserFile(user.ID),
|
DataType: string(types.ConfigTypeUser),
|
||||||
|
ID: user.ID,
|
||||||
Data: userj,
|
Data: userj,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -611,7 +618,8 @@ func (s *CommandHandler) UpdateUserLA(ctx context.Context, req *UpdateUserLARequ
|
||||||
actions := []*wal.Action{
|
actions := []*wal.Action{
|
||||||
{
|
{
|
||||||
ActionType: wal.ActionTypePut,
|
ActionType: wal.ActionTypePut,
|
||||||
Path: common.StorageUserFile(user.ID),
|
DataType: string(types.ConfigTypeUser),
|
||||||
|
ID: user.ID,
|
||||||
Data: userj,
|
Data: userj,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -666,7 +674,8 @@ func (s *CommandHandler) CreateUserToken(ctx context.Context, userName, tokenNam
|
||||||
actions := []*wal.Action{
|
actions := []*wal.Action{
|
||||||
{
|
{
|
||||||
ActionType: wal.ActionTypePut,
|
ActionType: wal.ActionTypePut,
|
||||||
Path: common.StorageUserFile(user.ID),
|
DataType: string(types.ConfigTypeUser),
|
||||||
|
ID: user.ID,
|
||||||
Data: userj,
|
Data: userj,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -715,7 +724,8 @@ func (s *CommandHandler) CreateRemoteSource(ctx context.Context, remoteSource *t
|
||||||
actions := []*wal.Action{
|
actions := []*wal.Action{
|
||||||
{
|
{
|
||||||
ActionType: wal.ActionTypePut,
|
ActionType: wal.ActionTypePut,
|
||||||
Path: common.StorageRemoteSourceFile(remoteSource.ID),
|
DataType: string(types.ConfigTypeRemoteSource),
|
||||||
|
ID: remoteSource.ID,
|
||||||
Data: rsj,
|
Data: rsj,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -755,7 +765,8 @@ func (s *CommandHandler) DeleteRemoteSource(ctx context.Context, remoteSourceNam
|
||||||
actions := []*wal.Action{
|
actions := []*wal.Action{
|
||||||
{
|
{
|
||||||
ActionType: wal.ActionTypeDelete,
|
ActionType: wal.ActionTypeDelete,
|
||||||
Path: common.StorageRemoteSourceFile(remoteSource.ID),
|
DataType: string(types.ConfigTypeRemoteSource),
|
||||||
|
ID: remoteSource.ID,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -814,12 +825,14 @@ func (s *CommandHandler) CreateOrg(ctx context.Context, org *types.Organization)
|
||||||
actions := []*wal.Action{
|
actions := []*wal.Action{
|
||||||
{
|
{
|
||||||
ActionType: wal.ActionTypePut,
|
ActionType: wal.ActionTypePut,
|
||||||
Path: common.StorageOrgFile(org.ID),
|
DataType: string(types.ConfigTypeOrg),
|
||||||
|
ID: org.ID,
|
||||||
Data: orgj,
|
Data: orgj,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ActionType: wal.ActionTypePut,
|
ActionType: wal.ActionTypePut,
|
||||||
Path: common.StorageProjectGroupFile(pg.ID),
|
DataType: string(types.ConfigTypeProjectGroup),
|
||||||
|
ID: pg.ID,
|
||||||
Data: pgj,
|
Data: pgj,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -861,14 +874,16 @@ func (s *CommandHandler) DeleteOrg(ctx context.Context, orgName string) error {
|
||||||
actions := []*wal.Action{
|
actions := []*wal.Action{
|
||||||
{
|
{
|
||||||
ActionType: wal.ActionTypeDelete,
|
ActionType: wal.ActionTypeDelete,
|
||||||
Path: common.StorageOrgFile(org.ID),
|
DataType: string(types.ConfigTypeOrg),
|
||||||
|
ID: org.ID,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
// delete all org projects
|
// delete all org projects
|
||||||
for _, project := range projects {
|
for _, project := range projects {
|
||||||
actions = append(actions, &wal.Action{
|
actions = append(actions, &wal.Action{
|
||||||
ActionType: wal.ActionTypeDelete,
|
ActionType: wal.ActionTypeDelete,
|
||||||
Path: common.StorageProjectFile(project.ID),
|
DataType: string(types.ConfigTypeProject),
|
||||||
|
ID: project.ID,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -931,7 +946,8 @@ func (s *CommandHandler) CreateSecret(ctx context.Context, secret *types.Secret)
|
||||||
actions := []*wal.Action{
|
actions := []*wal.Action{
|
||||||
{
|
{
|
||||||
ActionType: wal.ActionTypePut,
|
ActionType: wal.ActionTypePut,
|
||||||
Path: common.StorageSecretFile(secret.ID),
|
DataType: string(types.ConfigTypeSecret),
|
||||||
|
ID: secret.ID,
|
||||||
Data: secretj,
|
Data: secretj,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -977,7 +993,8 @@ func (s *CommandHandler) DeleteSecret(ctx context.Context, parentType types.Conf
|
||||||
actions := []*wal.Action{
|
actions := []*wal.Action{
|
||||||
{
|
{
|
||||||
ActionType: wal.ActionTypeDelete,
|
ActionType: wal.ActionTypeDelete,
|
||||||
Path: common.StorageSecretFile(secret.ID),
|
DataType: string(types.ConfigTypeSecret),
|
||||||
|
ID: secret.ID,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1040,7 +1057,8 @@ func (s *CommandHandler) CreateVariable(ctx context.Context, variable *types.Var
|
||||||
actions := []*wal.Action{
|
actions := []*wal.Action{
|
||||||
{
|
{
|
||||||
ActionType: wal.ActionTypePut,
|
ActionType: wal.ActionTypePut,
|
||||||
Path: common.StorageVariableFile(variable.ID),
|
DataType: string(types.ConfigTypeVariable),
|
||||||
|
ID: variable.ID,
|
||||||
Data: variablej,
|
Data: variablej,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1085,7 +1103,8 @@ func (s *CommandHandler) DeleteVariable(ctx context.Context, parentType types.Co
|
||||||
actions := []*wal.Action{
|
actions := []*wal.Action{
|
||||||
{
|
{
|
||||||
ActionType: wal.ActionTypeDelete,
|
ActionType: wal.ActionTypeDelete,
|
||||||
Path: common.StorageVariableFile(variable.ID),
|
DataType: string(types.ConfigTypeVariable),
|
||||||
|
ID: variable.ID,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -178,9 +178,11 @@ func (r *ReadDB) SyncFromFiles() (string, error) {
|
||||||
}
|
}
|
||||||
f.Close()
|
f.Close()
|
||||||
|
|
||||||
|
configType, id := common.PathToTypeID(obj)
|
||||||
action := &wal.Action{
|
action := &wal.Action{
|
||||||
ActionType: wal.ActionTypePut,
|
ActionType: wal.ActionTypePut,
|
||||||
Path: obj,
|
DataType: string(configType),
|
||||||
|
ID: id,
|
||||||
Data: data,
|
Data: data,
|
||||||
}
|
}
|
||||||
if err := r.applyAction(tx, action); err != nil {
|
if err := r.applyAction(tx, action); err != nil {
|
||||||
|
@ -616,11 +618,9 @@ func (r *ReadDB) applyWal(tx *db.Tx, walDataFileID string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ReadDB) applyAction(tx *db.Tx, action *wal.Action) error {
|
func (r *ReadDB) applyAction(tx *db.Tx, action *wal.Action) error {
|
||||||
configType, ID := common.PathToTypeID(action.Path)
|
|
||||||
|
|
||||||
switch action.ActionType {
|
switch action.ActionType {
|
||||||
case wal.ActionTypePut:
|
case wal.ActionTypePut:
|
||||||
switch configType {
|
switch types.ConfigType(action.DataType) {
|
||||||
case types.ConfigTypeUser:
|
case types.ConfigTypeUser:
|
||||||
if err := r.insertUser(tx, action.Data); err != nil {
|
if err := r.insertUser(tx, action.Data); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -652,40 +652,40 @@ func (r *ReadDB) applyAction(tx *db.Tx, action *wal.Action) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
case wal.ActionTypeDelete:
|
case wal.ActionTypeDelete:
|
||||||
switch configType {
|
switch types.ConfigType(action.DataType) {
|
||||||
case types.ConfigTypeUser:
|
case types.ConfigTypeUser:
|
||||||
r.log.Debugf("deleting user with id: %s", ID)
|
r.log.Debugf("deleting user with id: %s", action.ID)
|
||||||
if err := r.deleteUser(tx, ID); err != nil {
|
if err := r.deleteUser(tx, action.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case types.ConfigTypeOrg:
|
case types.ConfigTypeOrg:
|
||||||
r.log.Debugf("deleting org with id: %s", ID)
|
r.log.Debugf("deleting org with id: %s", action.ID)
|
||||||
if err := r.deleteOrg(tx, ID); err != nil {
|
if err := r.deleteOrg(tx, action.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case types.ConfigTypeProjectGroup:
|
case types.ConfigTypeProjectGroup:
|
||||||
r.log.Debugf("deleting project group with id: %s", ID)
|
r.log.Debugf("deleting project group with id: %s", action.ID)
|
||||||
if err := r.deleteProjectGroup(tx, ID); err != nil {
|
if err := r.deleteProjectGroup(tx, action.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case types.ConfigTypeProject:
|
case types.ConfigTypeProject:
|
||||||
r.log.Debugf("deleting project with id: %s", ID)
|
r.log.Debugf("deleting project with id: %s", action.ID)
|
||||||
if err := r.deleteProject(tx, ID); err != nil {
|
if err := r.deleteProject(tx, action.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case types.ConfigTypeRemoteSource:
|
case types.ConfigTypeRemoteSource:
|
||||||
r.log.Debugf("deleting remote source with id: %s", ID)
|
r.log.Debugf("deleting remote source with id: %s", action.ID)
|
||||||
if err := r.deleteRemoteSource(tx, ID); err != nil {
|
if err := r.deleteRemoteSource(tx, action.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case types.ConfigTypeSecret:
|
case types.ConfigTypeSecret:
|
||||||
r.log.Debugf("deleting secret with id: %s", ID)
|
r.log.Debugf("deleting secret with id: %s", action.ID)
|
||||||
if err := r.deleteSecret(tx, ID); err != nil {
|
if err := r.deleteSecret(tx, action.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case types.ConfigTypeVariable:
|
case types.ConfigTypeVariable:
|
||||||
r.log.Debugf("deleting variable with id: %s", ID)
|
r.log.Debugf("deleting variable with id: %s", action.ID)
|
||||||
if err := r.deleteVariable(tx, ID); err != nil {
|
if err := r.deleteVariable(tx, action.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,11 +20,13 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
uuid "github.com/satori/go.uuid"
|
uuid "github.com/satori/go.uuid"
|
||||||
|
"github.com/sorintlab/agola/internal/db"
|
||||||
"github.com/sorintlab/agola/internal/etcd"
|
"github.com/sorintlab/agola/internal/etcd"
|
||||||
"github.com/sorintlab/agola/internal/objectstorage"
|
"github.com/sorintlab/agola/internal/objectstorage"
|
||||||
"github.com/sorintlab/agola/internal/runconfig"
|
"github.com/sorintlab/agola/internal/runconfig"
|
||||||
"github.com/sorintlab/agola/internal/sequence"
|
"github.com/sorintlab/agola/internal/sequence"
|
||||||
"github.com/sorintlab/agola/internal/services/runservice/scheduler/common"
|
"github.com/sorintlab/agola/internal/services/runservice/scheduler/common"
|
||||||
|
"github.com/sorintlab/agola/internal/services/runservice/scheduler/readdb"
|
||||||
"github.com/sorintlab/agola/internal/services/runservice/scheduler/store"
|
"github.com/sorintlab/agola/internal/services/runservice/scheduler/store"
|
||||||
"github.com/sorintlab/agola/internal/services/runservice/types"
|
"github.com/sorintlab/agola/internal/services/runservice/types"
|
||||||
"github.com/sorintlab/agola/internal/util"
|
"github.com/sorintlab/agola/internal/util"
|
||||||
|
@ -37,14 +39,16 @@ import (
|
||||||
type CommandHandler struct {
|
type CommandHandler struct {
|
||||||
log *zap.SugaredLogger
|
log *zap.SugaredLogger
|
||||||
e *etcd.Store
|
e *etcd.Store
|
||||||
|
readDB *readdb.ReadDB
|
||||||
lts *objectstorage.ObjStorage
|
lts *objectstorage.ObjStorage
|
||||||
wal *wal.WalManager
|
wal *wal.WalManager
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCommandHandler(logger *zap.Logger, e *etcd.Store, lts *objectstorage.ObjStorage, wal *wal.WalManager) *CommandHandler {
|
func NewCommandHandler(logger *zap.Logger, e *etcd.Store, readDB *readdb.ReadDB, lts *objectstorage.ObjStorage, wal *wal.WalManager) *CommandHandler {
|
||||||
return &CommandHandler{
|
return &CommandHandler{
|
||||||
log: logger.Sugar(),
|
log: logger.Sugar(),
|
||||||
e: e,
|
e: e,
|
||||||
|
readDB: readDB,
|
||||||
lts: lts,
|
lts: lts,
|
||||||
wal: wal,
|
wal: wal,
|
||||||
}
|
}
|
||||||
|
@ -293,9 +297,9 @@ func (s *CommandHandler) saveRun(ctx context.Context, rb *types.RunBundle, runcg
|
||||||
rc := rb.Rc
|
rc := rb.Rc
|
||||||
rd := rb.Rd
|
rd := rb.Rd
|
||||||
|
|
||||||
c, cgt, err := store.LTSGetRunCounter(s.wal, run.Group)
|
c, cgt, err := s.getRunCounter(run.Group)
|
||||||
s.log.Infof("c: %d, cgt: %s", c, util.Dump(cgt))
|
s.log.Infof("c: %d, cgt: %s", c, util.Dump(cgt))
|
||||||
if err != nil && err != objectstorage.ErrNotExist {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c++
|
c++
|
||||||
|
@ -450,3 +454,28 @@ func (s *CommandHandler) DeleteExecutor(ctx context.Context, executorID string)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *CommandHandler) getRunCounter(group string) (uint64, *wal.ChangeGroupsUpdateToken, error) {
|
||||||
|
// use the first group dir after the root
|
||||||
|
pl := util.PathList(group)
|
||||||
|
if len(pl) < 2 {
|
||||||
|
return 0, nil, errors.Errorf("cannot determine group counter name, wrong group path %q", group)
|
||||||
|
}
|
||||||
|
|
||||||
|
var c uint64
|
||||||
|
var cgt *wal.ChangeGroupsUpdateToken
|
||||||
|
err := s.readDB.Do(func(tx *db.Tx) error {
|
||||||
|
var err error
|
||||||
|
c, err = s.readDB.GetRunCounterLTS(tx, pl[1])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cgt, err = s.readDB.GetChangeGroupsUpdateTokensLTS(tx, []string{"counter-" + pl[1]})
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, cgt, nil
|
||||||
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ package common
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ErrNotExist struct {
|
type ErrNotExist struct {
|
||||||
|
@ -43,8 +42,6 @@ var (
|
||||||
|
|
||||||
EtcdExecutorsDir = "executors"
|
EtcdExecutorsDir = "executors"
|
||||||
EtcdTasksDir = "tasks"
|
EtcdTasksDir = "tasks"
|
||||||
|
|
||||||
EtcdLastIndexKey = "lastindex"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func EtcdRunKey(runID string) string { return path.Join(EtcdRunsDir, runID) }
|
func EtcdRunKey(runID string) string { return path.Join(EtcdRunsDir, runID) }
|
||||||
|
@ -80,37 +77,30 @@ func StorageRunConfigFile(runID string) string {
|
||||||
return path.Join(StorageRunsConfigDir, runID)
|
return path.Join(StorageRunsConfigDir, runID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func StorageCounterFile(group string) string {
|
func StorageRunCounterFile(group string) string {
|
||||||
return path.Join(StorageCountersDir, group)
|
return path.Join(StorageCountersDir, group)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConfigType int
|
type DataType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ConfigTypeRun ConfigType = iota + 1
|
DataTypeRun DataType = "run"
|
||||||
ConfigTypeRunData
|
DataTypeRunData DataType = "rundata"
|
||||||
ConfigTypeRunConfig
|
DataTypeRunConfig DataType = "runconfig"
|
||||||
ConfigTypeCounter
|
DataTypeRunCounter DataType = "runcounter"
|
||||||
)
|
)
|
||||||
|
|
||||||
func PathToTypeID(p string) (ConfigType, string) {
|
func DataToPathFunc(dataType string, id string) string {
|
||||||
var configType ConfigType
|
switch DataType(dataType) {
|
||||||
switch path.Dir(p) {
|
case DataTypeRun:
|
||||||
case StorageRunsDir:
|
return StorageRunFile(id)
|
||||||
configType = ConfigTypeRun
|
case DataTypeRunData:
|
||||||
case StorageRunsDataDir:
|
return StorageRunDataFile(id)
|
||||||
configType = ConfigTypeRunData
|
case DataTypeRunConfig:
|
||||||
case StorageRunsConfigDir:
|
return StorageRunConfigFile(id)
|
||||||
configType = ConfigTypeRunConfig
|
case DataTypeRunCounter:
|
||||||
|
return StorageRunCounterFile(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(p, StorageCountersDir+"/") {
|
panic(fmt.Errorf("unknown data type %q", dataType))
|
||||||
configType = ConfigTypeCounter
|
|
||||||
}
|
|
||||||
|
|
||||||
if configType == 0 {
|
|
||||||
panic(fmt.Errorf("cannot determine configtype for path: %q", p))
|
|
||||||
}
|
|
||||||
|
|
||||||
return configType, path.Base(p)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,11 @@ var Stmts = []string{
|
||||||
// committedwalsequence stores the last committed wal sequence
|
// committedwalsequence stores the last committed wal sequence
|
||||||
"create table committedwalsequence_lts (seq varchar, PRIMARY KEY (seq))",
|
"create table committedwalsequence_lts (seq varchar, PRIMARY KEY (seq))",
|
||||||
|
|
||||||
|
"create table changegrouprevision_lts (id varchar, revision varchar, PRIMARY KEY (id, revision))",
|
||||||
|
|
||||||
"create table run_lts (id varchar, grouppath varchar, phase varchar, PRIMARY KEY (id, grouppath, phase))",
|
"create table run_lts (id varchar, grouppath varchar, phase varchar, PRIMARY KEY (id, grouppath, phase))",
|
||||||
|
|
||||||
"create table rundata_lts (id varchar, data bytea, PRIMARY KEY (id))",
|
"create table rundata_lts (id varchar, data bytea, PRIMARY KEY (id))",
|
||||||
|
|
||||||
|
"create table runcounter_lts (groupid varchar, counter bigint, PRIMARY KEY (groupid))",
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -85,12 +85,12 @@ func (s *Scheduler) advanceRunTasks(ctx context.Context, r *types.Run) error {
|
||||||
log.Debugf("run: %s", util.Dump(r))
|
log.Debugf("run: %s", util.Dump(r))
|
||||||
rc, err := store.LTSGetRunConfig(s.wal, r.ID)
|
rc, err := store.LTSGetRunConfig(s.wal, r.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "cannot get run config %q from etcd", r.ID)
|
return errors.Wrapf(err, "cannot get run config %q", r.ID)
|
||||||
}
|
}
|
||||||
log.Debugf("rc: %s", util.Dump(rc))
|
log.Debugf("rc: %s", util.Dump(rc))
|
||||||
rd, err := store.LTSGetRunData(s.wal, r.ID)
|
rd, err := store.LTSGetRunData(s.wal, r.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "cannot get run data %q from etcd", r.ID)
|
return errors.Wrapf(err, "cannot get run data %q", r.ID)
|
||||||
}
|
}
|
||||||
log.Debugf("rd: %s", util.Dump(rd))
|
log.Debugf("rd: %s", util.Dump(rd))
|
||||||
|
|
||||||
|
@ -1077,25 +1077,9 @@ func (s *Scheduler) runLTSArchiver(ctx context.Context, r *types.Run) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := s.e.Get(ctx, common.EtcdLastIndexKey, 0)
|
actions := append([]*wal.Action{ra})
|
||||||
// if lastIndexKey doesn't exist return an error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
lastIndexRevision := resp.Kvs[0].ModRevision
|
|
||||||
lastIndexDir := string(resp.Kvs[0].Value)
|
|
||||||
|
|
||||||
indexActions, err := s.additionalActions(lastIndexDir, ra)
|
if _, err = s.wal.WriteWal(ctx, actions, nil); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
actions := append([]*wal.Action{ra}, indexActions...)
|
|
||||||
|
|
||||||
cmp := []etcdclientv3.Cmp{etcdclientv3.Compare(etcdclientv3.ModRevision(common.EtcdLastIndexKey), "=", lastIndexRevision)}
|
|
||||||
then := []etcdclientv3.Op{etcdclientv3.OpPut(common.EtcdLastIndexKey, lastIndexDir)}
|
|
||||||
|
|
||||||
if _, err = s.wal.WriteWalAdditionalOps(ctx, actions, nil, cmp, then); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1107,53 +1091,6 @@ func (s *Scheduler) runLTSArchiver(ctx context.Context, r *types.Run) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scheduler) additionalActions(indexDir string, action *wal.Action) ([]*wal.Action, error) {
|
|
||||||
type indexData struct {
|
|
||||||
ID string
|
|
||||||
Group string
|
|
||||||
Phase types.RunPhase
|
|
||||||
}
|
|
||||||
|
|
||||||
configType, _ := common.PathToTypeID(action.Path)
|
|
||||||
|
|
||||||
var actionType wal.ActionType
|
|
||||||
|
|
||||||
switch action.ActionType {
|
|
||||||
case wal.ActionTypePut:
|
|
||||||
actionType = wal.ActionTypePut
|
|
||||||
case wal.ActionTypeDelete:
|
|
||||||
actionType = wal.ActionTypeDelete
|
|
||||||
}
|
|
||||||
|
|
||||||
switch configType {
|
|
||||||
case common.ConfigTypeRun:
|
|
||||||
var run *types.Run
|
|
||||||
if err := json.Unmarshal(action.Data, &run); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to unmarshal run")
|
|
||||||
}
|
|
||||||
|
|
||||||
data := []byte{}
|
|
||||||
index := path.Join(common.StorageRunsIndexesDir, indexDir, "added", run.ID)
|
|
||||||
id := &indexData{ID: run.ID, Group: run.Group, Phase: run.Phase}
|
|
||||||
idj, err := json.Marshal(id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
data = append(data, idj...)
|
|
||||||
|
|
||||||
actions := []*wal.Action{
|
|
||||||
&wal.Action{
|
|
||||||
ActionType: actionType,
|
|
||||||
Path: index,
|
|
||||||
Data: data,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return actions, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return []*wal.Action{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Scheduler) dumpLTSLoop(ctx context.Context) {
|
func (s *Scheduler) dumpLTSLoop(ctx context.Context) {
|
||||||
for {
|
for {
|
||||||
log.Debugf("lts dump loop")
|
log.Debugf("lts dump loop")
|
||||||
|
@ -1174,37 +1111,50 @@ func (s *Scheduler) dumpLTSLoop(ctx context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scheduler) dumpLTS(ctx context.Context) error {
|
func (s *Scheduler) dumpLTS(ctx context.Context) error {
|
||||||
|
type indexHeader struct {
|
||||||
|
LastWalSequence string
|
||||||
|
}
|
||||||
type indexData struct {
|
type indexData struct {
|
||||||
|
DataType string
|
||||||
|
Data interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type indexDataRun struct {
|
||||||
ID string
|
ID string
|
||||||
Group string
|
Group string
|
||||||
Phase types.RunPhase
|
Phase types.RunPhase
|
||||||
}
|
}
|
||||||
|
type indexDataRunCounter struct {
|
||||||
resp, err := s.e.Get(ctx, common.EtcdLastIndexKey, 0)
|
Group string
|
||||||
// if lastIndexKey doesn't exist return an error
|
Counter uint64
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
lastIndexRevision := resp.Kvs[0].ModRevision
|
|
||||||
revision := resp.Header.Revision
|
|
||||||
|
|
||||||
indexDir := strconv.FormatInt(time.Now().UnixNano(), 10)
|
indexDir := strconv.FormatInt(time.Now().UnixNano(), 10)
|
||||||
|
|
||||||
readdbRevision, err := s.readDB.GetRevision()
|
var lastWalSequence string
|
||||||
|
err := s.readDB.Do(func(tx *db.Tx) error {
|
||||||
|
var err error
|
||||||
|
lastWalSequence, err = s.readDB.GetCommittedWalSequenceLTS(tx)
|
||||||
|
return err
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if readdbRevision < revision {
|
|
||||||
return errors.Errorf("readdb revision %d is lower than index revision %d", readdbRevision, revision)
|
|
||||||
}
|
|
||||||
|
|
||||||
runs := []*readdb.RunData{}
|
data := []byte{}
|
||||||
|
iheader := &indexHeader{LastWalSequence: lastWalSequence}
|
||||||
|
ihj, err := json.Marshal(iheader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data = append(data, ihj...)
|
||||||
|
|
||||||
var lastRunID string
|
var lastRunID string
|
||||||
stop := false
|
stop := false
|
||||||
for {
|
for {
|
||||||
err := s.readDB.Do(func(tx *db.Tx) error {
|
err := s.readDB.Do(func(tx *db.Tx) error {
|
||||||
var err error
|
var err error
|
||||||
lruns, err := s.readDB.GetRunsFilteredLTS(tx, nil, false, nil, lastRunID, 10000, types.SortOrderDesc)
|
lruns, err := s.readDB.GetRunsFilteredLTS(tx, nil, false, nil, lastRunID, 1000, types.SortOrderDesc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1213,7 +1163,14 @@ func (s *Scheduler) dumpLTS(ctx context.Context) error {
|
||||||
} else {
|
} else {
|
||||||
lastRunID = lruns[len(lruns)-1].ID
|
lastRunID = lruns[len(lruns)-1].ID
|
||||||
}
|
}
|
||||||
runs = append(runs, lruns...)
|
for _, run := range lruns {
|
||||||
|
id := &indexData{DataType: string(common.DataTypeRun), Data: indexDataRun{ID: run.ID, Group: run.GroupPath, Phase: types.RunPhase(run.Phase)}}
|
||||||
|
idj, err := json.Marshal(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data = append(data, idj...)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1224,30 +1181,41 @@ func (s *Scheduler) dumpLTS(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data := []byte{}
|
var lastGroup string
|
||||||
for _, run := range runs {
|
stop = false
|
||||||
id := &indexData{ID: run.ID, Group: run.GroupPath, Phase: types.RunPhase(run.Phase)}
|
for {
|
||||||
|
err := s.readDB.Do(func(tx *db.Tx) error {
|
||||||
|
var err error
|
||||||
|
counters, err := s.readDB.GetRunCountersLTS(tx, lastGroup, 1000)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(counters) == 0 {
|
||||||
|
stop = true
|
||||||
|
} else {
|
||||||
|
lastGroup = counters[len(counters)-1].Group
|
||||||
|
}
|
||||||
|
for _, counter := range counters {
|
||||||
|
id := &indexData{DataType: string(common.DataTypeRunCounter), Data: indexDataRunCounter{Group: counter.Group, Counter: counter.Counter}}
|
||||||
idj, err := json.Marshal(id)
|
idj, err := json.Marshal(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
data = append(data, idj...)
|
data = append(data, idj...)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if stop {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
index := path.Join(common.StorageRunsIndexesDir, indexDir, "all")
|
index := path.Join(common.StorageRunsIndexesDir, indexDir, "all")
|
||||||
|
|
||||||
cmp := []etcdclientv3.Cmp{etcdclientv3.Compare(etcdclientv3.ModRevision(common.EtcdLastIndexKey), "=", lastIndexRevision)}
|
if err = s.lts.WriteObject(index, bytes.NewReader(data)); err != nil {
|
||||||
then := []etcdclientv3.Op{etcdclientv3.OpPut(common.EtcdLastIndexKey, indexDir)}
|
|
||||||
|
|
||||||
actions := []*wal.Action{
|
|
||||||
&wal.Action{
|
|
||||||
ActionType: wal.ActionTypePut,
|
|
||||||
Path: index,
|
|
||||||
Data: data,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = s.wal.WriteWalAdditionalOps(ctx, actions, nil, cmp, then); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1279,18 +1247,12 @@ func (s *Scheduler) dumpLTSCleaner(ctx context.Context) error {
|
||||||
Phase types.RunPhase
|
Phase types.RunPhase
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := s.e.Get(ctx, common.EtcdLastIndexKey, 0)
|
// collect all old indexes
|
||||||
// if lastIndexKey doesn't exist return an error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
lastIndexDir := string(resp.Kvs[0].Value)
|
|
||||||
|
|
||||||
// collect all object that don't pertain to the lastIndexDir
|
|
||||||
objects := []string{}
|
objects := []string{}
|
||||||
doneCh := make(chan struct{})
|
doneCh := make(chan struct{})
|
||||||
defer close(doneCh)
|
defer close(doneCh)
|
||||||
for object := range s.wal.List(common.StorageRunsIndexesDir+"/", "", true, doneCh) {
|
var indexPath string
|
||||||
|
for object := range s.lts.List(common.StorageRunsIndexesDir+"/", "", true, doneCh) {
|
||||||
if object.Err != nil {
|
if object.Err != nil {
|
||||||
return object.Err
|
return object.Err
|
||||||
}
|
}
|
||||||
|
@ -1299,22 +1261,20 @@ func (s *Scheduler) dumpLTSCleaner(ctx context.Context) error {
|
||||||
if len(h) < 2 {
|
if len(h) < 2 {
|
||||||
return errors.Errorf("wrong index dir path %q", object.Path)
|
return errors.Errorf("wrong index dir path %q", object.Path)
|
||||||
}
|
}
|
||||||
indexDir := h[1]
|
curIndexPath := object.Path
|
||||||
if indexDir != lastIndexDir {
|
if curIndexPath > indexPath {
|
||||||
objects = append(objects, object.Path)
|
if indexPath != "" {
|
||||||
|
objects = append(objects, indexPath)
|
||||||
|
}
|
||||||
|
indexPath = curIndexPath
|
||||||
|
} else {
|
||||||
|
objects = append(objects, curIndexPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
actions := make([]*wal.Action, len(objects))
|
for _, object := range objects {
|
||||||
for i, object := range objects {
|
if err := s.lts.DeleteObject(object); err != nil {
|
||||||
actions[i] = &wal.Action{
|
log.Errorf("object: %s, err: %v", object, err)
|
||||||
ActionType: wal.ActionTypeDelete,
|
|
||||||
Path: object,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(actions) > 0 {
|
|
||||||
if _, err = s.wal.WriteWal(ctx, actions, nil); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1354,6 +1314,7 @@ func NewScheduler(ctx context.Context, c *config.RunServiceScheduler) (*Schedule
|
||||||
walConf := &wal.WalManagerConfig{
|
walConf := &wal.WalManagerConfig{
|
||||||
E: e,
|
E: e,
|
||||||
Lts: lts,
|
Lts: lts,
|
||||||
|
DataToPathFunc: common.DataToPathFunc,
|
||||||
}
|
}
|
||||||
wal, err := wal.NewWalManager(ctx, logger, walConf)
|
wal, err := wal.NewWalManager(ctx, logger, walConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1361,13 +1322,13 @@ func NewScheduler(ctx context.Context, c *config.RunServiceScheduler) (*Schedule
|
||||||
}
|
}
|
||||||
s.wal = wal
|
s.wal = wal
|
||||||
|
|
||||||
readDB, err := readdb.NewReadDB(ctx, logger, filepath.Join(c.DataDir, "readdb"), e, wal)
|
readDB, err := readdb.NewReadDB(ctx, logger, filepath.Join(c.DataDir, "readdb"), e, lts, wal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
s.readDB = readDB
|
s.readDB = readDB
|
||||||
|
|
||||||
ch := command.NewCommandHandler(logger, e, lts, wal)
|
ch := command.NewCommandHandler(logger, e, readDB, lts, wal)
|
||||||
s.ch = ch
|
s.ch = ch
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
|
@ -1385,41 +1346,6 @@ func (s *Scheduler) InitEtcd(ctx context.Context) error {
|
||||||
return etcd.FromEtcdError(err)
|
return etcd.FromEtcdError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate lastIndexDir if empty
|
|
||||||
doneCh := make(chan struct{})
|
|
||||||
defer close(doneCh)
|
|
||||||
|
|
||||||
_, err := s.e.Get(ctx, common.EtcdLastIndexKey, 0)
|
|
||||||
if err != nil && err != etcd.ErrKeyNotFound {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// EtcdLastIndexKey already exist
|
|
||||||
if err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var indexDir string
|
|
||||||
// take the last (greater) indexdir
|
|
||||||
for object := range s.wal.List(common.StorageRunsIndexesDir+"/", "", true, doneCh) {
|
|
||||||
if object.Err != nil {
|
|
||||||
return object.Err
|
|
||||||
}
|
|
||||||
|
|
||||||
h := util.PathList(object.Path)
|
|
||||||
if len(h) < 2 {
|
|
||||||
return errors.Errorf("wrong index dir path %q", object.Path)
|
|
||||||
}
|
|
||||||
indexDir = h[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
// if an indexDir doesn't exist in lts then initialize
|
|
||||||
if indexDir == "" {
|
|
||||||
indexDir = strconv.FormatInt(time.Now().UnixNano(), 10)
|
|
||||||
if _, err := s.e.AtomicPut(ctx, common.EtcdLastIndexKey, []byte(indexDir), 0, nil); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,32 +88,11 @@ func LTSRunCounterPaths(group, runID string, sortOrder types.SortOrder) []string
|
||||||
paths := []string{}
|
paths := []string{}
|
||||||
subGroups := LTSSubGroups(group)
|
subGroups := LTSSubGroups(group)
|
||||||
for _, subGroup := range subGroups {
|
for _, subGroup := range subGroups {
|
||||||
paths = append(paths, common.StorageCounterFile(subGroup))
|
paths = append(paths, common.StorageRunCounterFile(subGroup))
|
||||||
}
|
}
|
||||||
return paths
|
return paths
|
||||||
}
|
}
|
||||||
|
|
||||||
func LTSGetRunCounter(wal *wal.WalManager, group string) (uint64, *wal.ChangeGroupsUpdateToken, error) {
|
|
||||||
// use the first group dir after the root
|
|
||||||
pl := util.PathList(group)
|
|
||||||
if len(pl) < 2 {
|
|
||||||
return 0, nil, errors.Errorf("cannot determine group counter name, wrong group path %q", group)
|
|
||||||
}
|
|
||||||
runCounterPath := common.StorageCounterFile(pl[1])
|
|
||||||
rcf, cgt, err := wal.ReadObject(runCounterPath, []string{"counter-" + pl[1]})
|
|
||||||
if err != nil {
|
|
||||||
return 0, cgt, err
|
|
||||||
}
|
|
||||||
defer rcf.Close()
|
|
||||||
d := json.NewDecoder(rcf)
|
|
||||||
var c uint64
|
|
||||||
if err := d.Decode(&c); err != nil {
|
|
||||||
return 0, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return c, cgt, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func LTSUpdateRunCounterAction(ctx context.Context, c uint64, group string) (*wal.Action, error) {
|
func LTSUpdateRunCounterAction(ctx context.Context, c uint64, group string) (*wal.Action, error) {
|
||||||
// use the first group dir after the root
|
// use the first group dir after the root
|
||||||
pl := util.PathList(group)
|
pl := util.PathList(group)
|
||||||
|
@ -128,7 +107,8 @@ func LTSUpdateRunCounterAction(ctx context.Context, c uint64, group string) (*wa
|
||||||
|
|
||||||
action := &wal.Action{
|
action := &wal.Action{
|
||||||
ActionType: wal.ActionTypePut,
|
ActionType: wal.ActionTypePut,
|
||||||
Path: common.StorageCounterFile(pl[1]),
|
DataType: string(common.DataTypeRunCounter),
|
||||||
|
ID: pl[1],
|
||||||
Data: cj,
|
Data: cj,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +155,8 @@ func LTSSaveRunConfigAction(rc *types.RunConfig) (*wal.Action, error) {
|
||||||
|
|
||||||
action := &wal.Action{
|
action := &wal.Action{
|
||||||
ActionType: wal.ActionTypePut,
|
ActionType: wal.ActionTypePut,
|
||||||
Path: common.StorageRunConfigFile(rc.ID),
|
DataType: string(common.DataTypeRunConfig),
|
||||||
|
ID: rc.ID,
|
||||||
Data: rcj,
|
Data: rcj,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +187,8 @@ func LTSSaveRunDataAction(rd *types.RunData) (*wal.Action, error) {
|
||||||
|
|
||||||
action := &wal.Action{
|
action := &wal.Action{
|
||||||
ActionType: wal.ActionTypePut,
|
ActionType: wal.ActionTypePut,
|
||||||
Path: common.StorageRunDataFile(rd.ID),
|
DataType: string(common.DataTypeRunData),
|
||||||
|
ID: rd.ID,
|
||||||
Data: rdj,
|
Data: rdj,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,7 +220,8 @@ func LTSSaveRunAction(r *types.Run) (*wal.Action, error) {
|
||||||
|
|
||||||
action := &wal.Action{
|
action := &wal.Action{
|
||||||
ActionType: wal.ActionTypePut,
|
ActionType: wal.ActionTypePut,
|
||||||
Path: common.StorageRunFile(r.ID),
|
DataType: string(common.DataTypeRun),
|
||||||
|
ID: r.ID,
|
||||||
Data: rj,
|
Data: rj,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,12 +23,6 @@ import (
|
||||||
"github.com/sorintlab/agola/internal/util"
|
"github.com/sorintlab/agola/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RunBundle struct {
|
|
||||||
Run *Run
|
|
||||||
Rc *RunConfig
|
|
||||||
Rd *RunData
|
|
||||||
}
|
|
||||||
|
|
||||||
type SortOrder int
|
type SortOrder int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -36,6 +30,17 @@ const (
|
||||||
SortOrderDesc
|
SortOrderDesc
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type RunBundle struct {
|
||||||
|
Run *Run
|
||||||
|
Rc *RunConfig
|
||||||
|
Rd *RunData
|
||||||
|
}
|
||||||
|
|
||||||
|
type RunCounter struct {
|
||||||
|
Group string
|
||||||
|
Counter uint64
|
||||||
|
}
|
||||||
|
|
||||||
type RunPhase string
|
type RunPhase string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -180,12 +180,16 @@ func (w *WalManager) applyWalChanges(ctx context.Context, walData *WalData, revi
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WalManager) applyWalChangesAction(ctx context.Context, action *Action, walSequence string, revision int64) {
|
func (w *WalManager) applyWalChangesAction(ctx context.Context, action *Action, walSequence string, revision int64) {
|
||||||
|
dataPath := w.dataToPathFunc(action.DataType, action.ID)
|
||||||
|
if dataPath == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
switch action.ActionType {
|
switch action.ActionType {
|
||||||
case ActionTypePut:
|
case ActionTypePut:
|
||||||
w.changes.addPut(action.Path, walSequence, revision)
|
w.changes.addPut(dataPath, walSequence, revision)
|
||||||
|
|
||||||
case ActionTypeDelete:
|
case ActionTypeDelete:
|
||||||
w.changes.addDelete(action.Path, walSequence, revision)
|
w.changes.addDelete(dataPath, walSequence, revision)
|
||||||
}
|
}
|
||||||
if w.changes.actions[walSequence] == nil {
|
if w.changes.actions[walSequence] == nil {
|
||||||
w.changes.actions[walSequence] = []*Action{}
|
w.changes.actions[walSequence] = []*Action{}
|
||||||
|
|
|
@ -114,7 +114,8 @@ const (
|
||||||
|
|
||||||
type Action struct {
|
type Action struct {
|
||||||
ActionType ActionType
|
ActionType ActionType
|
||||||
Path string
|
DataType string
|
||||||
|
ID string
|
||||||
Data []byte
|
Data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,11 +214,14 @@ func (w *WalManager) ReadObject(p string, cgNames []string) (io.ReadCloser, *Cha
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
for _, action := range actions {
|
for _, action := range actions {
|
||||||
if action.ActionType == ActionTypePut && action.Path == p {
|
if action.ActionType == ActionTypePut {
|
||||||
w.log.Debugf("reading file from wal: %q", action.Path)
|
dataPath := w.dataToPathFunc(action.DataType, action.ID)
|
||||||
|
if dataPath == p {
|
||||||
|
w.log.Debugf("reading file from wal: %q", dataPath)
|
||||||
return ioutil.NopCloser(bytes.NewReader(action.Data)), cgt, nil
|
return ioutil.NopCloser(bytes.NewReader(action.Data)), cgt, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return nil, nil, errors.Errorf("no file %s in wal %s", p, walseq)
|
return nil, nil, errors.Errorf("no file %s in wal %s", p, walseq)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -884,7 +888,11 @@ func (w *WalManager) checkpoint(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WalManager) checkpointAction(ctx context.Context, action *Action) error {
|
func (w *WalManager) checkpointAction(ctx context.Context, action *Action) error {
|
||||||
path := w.toStorageDataPath(action.Path)
|
dataPath := w.dataToPathFunc(action.DataType, action.ID)
|
||||||
|
if dataPath == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
path := w.toStorageDataPath(dataPath)
|
||||||
switch action.ActionType {
|
switch action.ActionType {
|
||||||
case ActionTypePut:
|
case ActionTypePut:
|
||||||
w.log.Debugf("writing file: %q", path)
|
w.log.Debugf("writing file: %q", path)
|
||||||
|
@ -1209,18 +1217,21 @@ func (w *WalManager) InitEtcd(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type AdditionalActionsFunc func(action *Action) ([]*Action, error)
|
type CheckpointFunc func(action *Action) error
|
||||||
|
|
||||||
func NoOpAdditionalActionFunc(action *Action) ([]*Action, error) {
|
type DataToPathFunc func(dataType string, id string) string
|
||||||
return []*Action{}, nil
|
|
||||||
|
func NoOpDataToPath(dataType string, id string) string {
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
type WalManagerConfig struct {
|
type WalManagerConfig struct {
|
||||||
BasePath string
|
BasePath string
|
||||||
E *etcd.Store
|
E *etcd.Store
|
||||||
Lts *objectstorage.ObjStorage
|
Lts *objectstorage.ObjStorage
|
||||||
AdditionalActionsFunc AdditionalActionsFunc
|
|
||||||
EtcdWalsKeepNum int
|
EtcdWalsKeepNum int
|
||||||
|
CheckpointFunc CheckpointFunc
|
||||||
|
DataToPathFunc DataToPathFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
type WalManager struct {
|
type WalManager struct {
|
||||||
|
@ -1230,6 +1241,8 @@ type WalManager struct {
|
||||||
lts *objectstorage.ObjStorage
|
lts *objectstorage.ObjStorage
|
||||||
changes *WalChanges
|
changes *WalChanges
|
||||||
etcdWalsKeepNum int
|
etcdWalsKeepNum int
|
||||||
|
checkpointFunc CheckpointFunc
|
||||||
|
dataToPathFunc DataToPathFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWalManager(ctx context.Context, logger *zap.Logger, conf *WalManagerConfig) (*WalManager, error) {
|
func NewWalManager(ctx context.Context, logger *zap.Logger, conf *WalManagerConfig) (*WalManager, error) {
|
||||||
|
@ -1240,9 +1253,9 @@ func NewWalManager(ctx context.Context, logger *zap.Logger, conf *WalManagerConf
|
||||||
return nil, errors.New("etcdWalsKeepNum must be greater than 0")
|
return nil, errors.New("etcdWalsKeepNum must be greater than 0")
|
||||||
}
|
}
|
||||||
|
|
||||||
additionalActionsFunc := conf.AdditionalActionsFunc
|
dataToPathFunc := conf.DataToPathFunc
|
||||||
if additionalActionsFunc == nil {
|
if dataToPathFunc == nil {
|
||||||
additionalActionsFunc = NoOpAdditionalActionFunc
|
dataToPathFunc = NoOpDataToPath
|
||||||
}
|
}
|
||||||
|
|
||||||
w := &WalManager{
|
w := &WalManager{
|
||||||
|
@ -1252,6 +1265,8 @@ func NewWalManager(ctx context.Context, logger *zap.Logger, conf *WalManagerConf
|
||||||
lts: conf.Lts,
|
lts: conf.Lts,
|
||||||
etcdWalsKeepNum: conf.EtcdWalsKeepNum,
|
etcdWalsKeepNum: conf.EtcdWalsKeepNum,
|
||||||
changes: NewWalChanges(),
|
changes: NewWalChanges(),
|
||||||
|
checkpointFunc: conf.CheckpointFunc,
|
||||||
|
dataToPathFunc: dataToPathFunc,
|
||||||
}
|
}
|
||||||
|
|
||||||
// add trailing slash the basepath
|
// add trailing slash the basepath
|
||||||
|
|
|
@ -90,6 +90,8 @@ func TestEtcdReset(t *testing.T) {
|
||||||
}
|
}
|
||||||
wal, err := NewWalManager(ctx, logger, walConfig)
|
wal, err := NewWalManager(ctx, logger, walConfig)
|
||||||
walReadyCh := make(chan struct{})
|
walReadyCh := make(chan struct{})
|
||||||
|
|
||||||
|
t.Logf("starting wal")
|
||||||
go wal.Run(ctx, walReadyCh)
|
go wal.Run(ctx, walReadyCh)
|
||||||
<-walReadyCh
|
<-walReadyCh
|
||||||
|
|
||||||
|
@ -102,9 +104,9 @@ func TestEtcdReset(t *testing.T) {
|
||||||
|
|
||||||
expectedObjects := []string{}
|
expectedObjects := []string{}
|
||||||
for i := 0; i < 20; i++ {
|
for i := 0; i < 20; i++ {
|
||||||
objectPath := fmt.Sprintf("object%02d", i)
|
objectID := fmt.Sprintf("object%02d", i)
|
||||||
expectedObjects = append(expectedObjects, objectPath)
|
expectedObjects = append(expectedObjects, objectID)
|
||||||
actions[0].Path = objectPath
|
actions[0].ID = objectID
|
||||||
if _, err := wal.WriteWal(ctx, actions, nil); err != nil {
|
if _, err := wal.WriteWal(ctx, actions, nil); err != nil {
|
||||||
t.Fatalf("unexpected err: %v", err)
|
t.Fatalf("unexpected err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -113,20 +115,39 @@ func TestEtcdReset(t *testing.T) {
|
||||||
// wait for wal to be committed storage
|
// wait for wal to be committed storage
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
|
|
||||||
|
t.Logf("stopping wal")
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
t.Logf("stopping etcd")
|
||||||
// Reset etcd
|
// Reset etcd
|
||||||
shutdownEtcd(tetcd)
|
shutdownEtcd(tetcd)
|
||||||
tetcd.WaitDown(10 * time.Second)
|
tetcd.WaitDown(10 * time.Second)
|
||||||
|
t.Logf("resetting etcd")
|
||||||
os.RemoveAll(etcdDir)
|
os.RemoveAll(etcdDir)
|
||||||
|
t.Logf("starting etcd")
|
||||||
|
tetcd = setupEtcd(t, etcdDir)
|
||||||
|
defer shutdownEtcd(tetcd)
|
||||||
if err := tetcd.Start(); err != nil {
|
if err := tetcd.Start(); err != nil {
|
||||||
t.Fatalf("unexpected err: %v", err)
|
t.Fatalf("unexpected err: %v", err)
|
||||||
}
|
}
|
||||||
defer shutdownEtcd(tetcd)
|
defer shutdownEtcd(tetcd)
|
||||||
|
|
||||||
cancel()
|
ctx, cancel = context.WithCancel(context.Background())
|
||||||
ctx = context.Background()
|
walConfig = &WalManagerConfig{
|
||||||
|
BasePath: "basepath",
|
||||||
|
E: tetcd.TestEtcd.Store,
|
||||||
|
Lts: objectstorage.NewObjStorage(lts, "/"),
|
||||||
|
EtcdWalsKeepNum: 10,
|
||||||
|
}
|
||||||
|
wal, err = NewWalManager(ctx, logger, walConfig)
|
||||||
|
walReadyCh = make(chan struct{})
|
||||||
|
|
||||||
|
t.Logf("starting wal")
|
||||||
go wal.Run(ctx, walReadyCh)
|
go wal.Run(ctx, walReadyCh)
|
||||||
<-walReadyCh
|
<-walReadyCh
|
||||||
|
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
|
||||||
curObjects := []string{}
|
curObjects := []string{}
|
||||||
doneCh := make(chan struct{})
|
doneCh := make(chan struct{})
|
||||||
for object := range wal.List("", "", true, doneCh) {
|
for object := range wal.List("", "", true, doneCh) {
|
||||||
|
@ -174,7 +195,7 @@ func TestConcurrentUpdate(t *testing.T) {
|
||||||
actions := []*Action{
|
actions := []*Action{
|
||||||
{
|
{
|
||||||
ActionType: ActionTypePut,
|
ActionType: ActionTypePut,
|
||||||
Path: "/object01",
|
ID: "/object01",
|
||||||
Data: []byte("{}"),
|
Data: []byte("{}"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -183,6 +204,8 @@ func TestConcurrentUpdate(t *testing.T) {
|
||||||
go wal.Run(ctx, walReadyCh)
|
go wal.Run(ctx, walReadyCh)
|
||||||
<-walReadyCh
|
<-walReadyCh
|
||||||
|
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
|
||||||
cgNames := []string{"changegroup01", "changegroup02"}
|
cgNames := []string{"changegroup01", "changegroup02"}
|
||||||
cgt, err := wal.GetChangeGroupsUpdateToken(cgNames)
|
cgt, err := wal.GetChangeGroupsUpdateToken(cgNames)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -253,7 +276,7 @@ func TestWalCleaner(t *testing.T) {
|
||||||
actions := []*Action{
|
actions := []*Action{
|
||||||
{
|
{
|
||||||
ActionType: ActionTypePut,
|
ActionType: ActionTypePut,
|
||||||
Path: "/object01",
|
ID: "/object01",
|
||||||
Data: []byte("{}"),
|
Data: []byte("{}"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue