mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-12 02:54:20 +00:00
fix: lock again (#1132)
* start sub * start implement subsciptions * start subscription * implementation for member done * admin done * fix: tests * extend handlers * prepary notification * no errors in adminapi * changed current sequence in all packages * ignore mocks * works * subscriptions as singleton * tests * refactor: rename function scope var * fix: process ALL previous sequences * fix: spooler and pubsub * handler check * fix: process events until all done * fix break on query err * fix: handler * fix: process sequence or return error * check aggregate id * fix: log only in error case * fix tests * fix: handlers * fix: spooler * fix: spooler * fix: tests * fix: continue * fix: locker duration * fix: variable lock duration * fix: test * fix: test * fix: test min max time Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
parent
f96838cf62
commit
a6c4702b8e
@ -95,6 +95,10 @@ func (h *handler) MinimumCycleDuration() time.Duration {
|
|||||||
return h.cycleDuration
|
return h.cycleDuration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *handler) LockDuration() time.Duration {
|
||||||
|
return h.cycleDuration / 3
|
||||||
|
}
|
||||||
|
|
||||||
func (h *handler) QueryLimit() uint64 {
|
func (h *handler) QueryLimit() uint64 {
|
||||||
return h.bulkLimit
|
return h.bulkLimit
|
||||||
}
|
}
|
||||||
|
@ -2,26 +2,18 @@ package spooler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
es_locker "github.com/caos/zitadel/internal/eventstore/locker"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
lockTable = "adminapi.locks"
|
lockTable = "adminapi.locks"
|
||||||
lockedUntilKey = "locked_until"
|
|
||||||
lockerIDKey = "locker_id"
|
|
||||||
objectTypeKey = "object_type"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type locker struct {
|
type locker struct {
|
||||||
dbClient *sql.DB
|
dbClient *sql.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
type lock struct {
|
|
||||||
LockerID string `gorm:"column:locker_id;primary_key"`
|
|
||||||
LockedUntil time.Time `gorm:"column:locked_until"`
|
|
||||||
ViewName string `gorm:"column:object_type;primary_key"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error {
|
func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error {
|
||||||
return nil
|
return es_locker.Renew(l.dbClient, lockTable, lockerID, viewModel, waitTime)
|
||||||
}
|
}
|
||||||
|
@ -108,6 +108,10 @@ func (h *handler) MinimumCycleDuration() time.Duration {
|
|||||||
return h.cycleDuration
|
return h.cycleDuration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *handler) LockDuration() time.Duration {
|
||||||
|
return h.cycleDuration / 3
|
||||||
|
}
|
||||||
|
|
||||||
func (h *handler) QueryLimit() uint64 {
|
func (h *handler) QueryLimit() uint64 {
|
||||||
return h.bulkLimit
|
return h.bulkLimit
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,18 @@ package spooler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
es_locker "github.com/caos/zitadel/internal/eventstore/locker"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
lockTable = "management.locks"
|
||||||
|
)
|
||||||
|
|
||||||
type locker struct {
|
type locker struct {
|
||||||
dbClient *sql.DB
|
dbClient *sql.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error {
|
func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error {
|
||||||
return nil
|
return es_locker.Renew(l.dbClient, lockTable, lockerID, viewModel, waitTime)
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,12 @@ package handler
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/authz/repository/eventsourcing/view"
|
||||||
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
||||||
|
"github.com/caos/zitadel/internal/config/types"
|
||||||
"github.com/caos/zitadel/internal/eventstore"
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
"github.com/caos/zitadel/internal/eventstore/query"
|
"github.com/caos/zitadel/internal/eventstore/query"
|
||||||
iam_events "github.com/caos/zitadel/internal/iam/repository/eventsourcing"
|
iam_events "github.com/caos/zitadel/internal/iam/repository/eventsourcing"
|
||||||
|
|
||||||
"github.com/caos/zitadel/internal/authz/repository/eventsourcing/view"
|
|
||||||
"github.com/caos/zitadel/internal/config/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Configs map[string]*Config
|
type Configs map[string]*Config
|
||||||
@ -60,6 +59,10 @@ func (h *handler) MinimumCycleDuration() time.Duration {
|
|||||||
return h.cycleDuration
|
return h.cycleDuration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *handler) LockDuration() time.Duration {
|
||||||
|
return h.cycleDuration / 3
|
||||||
|
}
|
||||||
|
|
||||||
func (h *handler) QueryLimit() uint64 {
|
func (h *handler) QueryLimit() uint64 {
|
||||||
return h.bulkLimit
|
return h.bulkLimit
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package spooler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
es_locker "github.com/caos/zitadel/internal/eventstore/locker"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,5 +15,5 @@ type locker struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error {
|
func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error {
|
||||||
return nil
|
return es_locker.Renew(l.dbClient, lockTable, lockerID, viewModel, waitTime)
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ type Handler interface {
|
|||||||
OnError(event *models.Event, err error) error
|
OnError(event *models.Event, err error) error
|
||||||
OnSuccess() error
|
OnSuccess() error
|
||||||
MinimumCycleDuration() time.Duration
|
MinimumCycleDuration() time.Duration
|
||||||
|
LockDuration() time.Duration
|
||||||
QueryLimit() uint64
|
QueryLimit() uint64
|
||||||
|
|
||||||
AggregateTypes() []models.AggregateType
|
AggregateTypes() []models.AggregateType
|
||||||
|
@ -160,12 +160,12 @@ func (s *spooledHandler) lock(ctx context.Context, errs chan<- error, workerID s
|
|||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
case <-renewTimer:
|
case <-renewTimer:
|
||||||
err := s.locker.Renew(workerID, s.ViewModel(), s.MinimumCycleDuration()*2)
|
err := s.locker.Renew(workerID, s.ViewModel(), s.LockDuration())
|
||||||
firstLock.Do(func() {
|
firstLock.Do(func() {
|
||||||
locked <- err == nil
|
locked <- err == nil
|
||||||
})
|
})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
renewTimer = time.After(s.MinimumCycleDuration())
|
renewTimer = time.After(s.LockDuration())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,16 +40,19 @@ func (h *testHandler) Eventstore() eventstore.Eventstore {
|
|||||||
func (h *testHandler) ViewModel() string {
|
func (h *testHandler) ViewModel() string {
|
||||||
return h.viewModel
|
return h.viewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *testHandler) EventQuery() (*models.SearchQuery, error) {
|
func (h *testHandler) EventQuery() (*models.SearchQuery, error) {
|
||||||
if h.queryError != nil {
|
if h.queryError != nil {
|
||||||
return nil, h.queryError
|
return nil, h.queryError
|
||||||
}
|
}
|
||||||
return &models.SearchQuery{}, nil
|
return &models.SearchQuery{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *testHandler) Reduce(*models.Event) error {
|
func (h *testHandler) Reduce(*models.Event) error {
|
||||||
<-time.After(h.processSleep)
|
<-time.After(h.processSleep)
|
||||||
return h.processError
|
return h.processError
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *testHandler) OnError(event *models.Event, err error) error {
|
func (h *testHandler) OnError(event *models.Event, err error) error {
|
||||||
if h.maxErrCount == 2 {
|
if h.maxErrCount == 2 {
|
||||||
return nil
|
return nil
|
||||||
@ -57,12 +60,19 @@ func (h *testHandler) OnError(event *models.Event, err error) error {
|
|||||||
h.maxErrCount++
|
h.maxErrCount++
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *testHandler) OnSuccess() error {
|
func (h *testHandler) OnSuccess() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *testHandler) MinimumCycleDuration() time.Duration {
|
func (h *testHandler) MinimumCycleDuration() time.Duration {
|
||||||
return h.cycleDuration
|
return h.cycleDuration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *testHandler) LockDuration() time.Duration {
|
||||||
|
return h.cycleDuration / 2
|
||||||
|
}
|
||||||
|
|
||||||
func (h *testHandler) QueryLimit() uint64 {
|
func (h *testHandler) QueryLimit() uint64 {
|
||||||
return h.bulkLimit
|
return h.bulkLimit
|
||||||
}
|
}
|
||||||
@ -232,31 +242,31 @@ func TestSpooler_load(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"lock exists",
|
"lock exists",
|
||||||
fields{
|
fields{
|
||||||
currentHandler: &testHandler{processSleep: 500 * time.Millisecond, viewModel: "testView", cycleDuration: 1 * time.Second, bulkLimit: 10},
|
currentHandler: &testHandler{processSleep: 500 * time.Millisecond, viewModel: "testView1", cycleDuration: 1 * time.Second, bulkLimit: 10},
|
||||||
locker: newTestLocker(t, "testID", "testView").expectRenew(t, fmt.Errorf("lock already exists"), 2000*time.Millisecond),
|
locker: newTestLocker(t, "testID", "testView1").expectRenew(t, fmt.Errorf("lock already exists"), 500*time.Millisecond),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"lock fails",
|
"lock fails",
|
||||||
fields{
|
fields{
|
||||||
currentHandler: &testHandler{processSleep: 100 * time.Millisecond, viewModel: "testView", cycleDuration: 1 * time.Second, bulkLimit: 10},
|
currentHandler: &testHandler{processSleep: 100 * time.Millisecond, viewModel: "testView2", cycleDuration: 1 * time.Second, bulkLimit: 10},
|
||||||
locker: newTestLocker(t, "testID", "testView").expectRenew(t, fmt.Errorf("fail"), 2000*time.Millisecond),
|
locker: newTestLocker(t, "testID", "testView2").expectRenew(t, fmt.Errorf("fail"), 500*time.Millisecond),
|
||||||
eventstore: &eventstoreStub{events: []*models.Event{{}}},
|
eventstore: &eventstoreStub{events: []*models.Event{{}}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"query fails",
|
"query fails",
|
||||||
fields{
|
fields{
|
||||||
currentHandler: &testHandler{processSleep: 100 * time.Millisecond, viewModel: "testView", queryError: fmt.Errorf("query fail"), cycleDuration: 1 * time.Second, bulkLimit: 10},
|
currentHandler: &testHandler{processSleep: 100 * time.Millisecond, viewModel: "testView3", queryError: fmt.Errorf("query fail"), cycleDuration: 1 * time.Second, bulkLimit: 10},
|
||||||
locker: newTestLocker(t, "testID", "testView").expectRenew(t, nil, 2000*time.Millisecond),
|
locker: newTestLocker(t, "testID", "testView3").expectRenew(t, nil, 500*time.Millisecond),
|
||||||
eventstore: &eventstoreStub{err: fmt.Errorf("fail")},
|
eventstore: &eventstoreStub{err: fmt.Errorf("fail")},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"process event fails",
|
"process event fails",
|
||||||
fields{
|
fields{
|
||||||
currentHandler: &testHandler{processError: fmt.Errorf("oups"), processSleep: 100 * time.Millisecond, viewModel: "testView", cycleDuration: 500 * time.Millisecond, bulkLimit: 10},
|
currentHandler: &testHandler{processError: fmt.Errorf("oups"), processSleep: 100 * time.Millisecond, viewModel: "testView4", cycleDuration: 500 * time.Millisecond, bulkLimit: 10},
|
||||||
locker: newTestLocker(t, "testID", "testView").expectRenew(t, nil, 1000*time.Millisecond).expectRenew(t, nil, 1000*time.Millisecond),
|
locker: newTestLocker(t, "testID", "testView4").expectRenew(t, nil, 250*time.Millisecond),
|
||||||
eventstore: &eventstoreStub{events: []*models.Event{{}}},
|
eventstore: &eventstoreStub{events: []*models.Event{{}}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -292,7 +302,7 @@ func TestSpooler_lock(t *testing.T) {
|
|||||||
"renew correct",
|
"renew correct",
|
||||||
fields{
|
fields{
|
||||||
currentHandler: &testHandler{cycleDuration: 1 * time.Second, viewModel: "testView"},
|
currentHandler: &testHandler{cycleDuration: 1 * time.Second, viewModel: "testView"},
|
||||||
locker: newTestLocker(t, "testID", "testView").expectRenew(t, nil, 2000*time.Millisecond),
|
locker: newTestLocker(t, "testID", "testView").expectRenew(t, nil, 500*time.Millisecond),
|
||||||
expectsErr: false,
|
expectsErr: false,
|
||||||
},
|
},
|
||||||
args{
|
args{
|
||||||
@ -303,7 +313,7 @@ func TestSpooler_lock(t *testing.T) {
|
|||||||
"renew fails",
|
"renew fails",
|
||||||
fields{
|
fields{
|
||||||
currentHandler: &testHandler{cycleDuration: 900 * time.Millisecond, viewModel: "testView"},
|
currentHandler: &testHandler{cycleDuration: 900 * time.Millisecond, viewModel: "testView"},
|
||||||
locker: newTestLocker(t, "testID", "testView").expectRenew(t, fmt.Errorf("renew failed"), 1800*time.Millisecond),
|
locker: newTestLocker(t, "testID", "testView").expectRenew(t, fmt.Errorf("renew failed"), 450*time.Millisecond),
|
||||||
expectsErr: true,
|
expectsErr: true,
|
||||||
},
|
},
|
||||||
args{
|
args{
|
||||||
@ -357,13 +367,15 @@ func newTestLocker(t *testing.T, lockerID, viewName string) *testLocker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *testLocker) expectRenew(t *testing.T, err error, waitTime time.Duration) *testLocker {
|
func (l *testLocker) expectRenew(t *testing.T, err error, waitTime time.Duration) *testLocker {
|
||||||
|
t.Helper()
|
||||||
l.mock.EXPECT().Renew(gomock.Any(), l.viewName, gomock.Any()).DoAndReturn(
|
l.mock.EXPECT().Renew(gomock.Any(), l.viewName, gomock.Any()).DoAndReturn(
|
||||||
func(_, _ string, gotten time.Duration) error {
|
func(_, _ string, gotten time.Duration) error {
|
||||||
|
t.Helper()
|
||||||
if waitTime-gotten != 0 {
|
if waitTime-gotten != 0 {
|
||||||
t.Errorf("expected waittime %v got %v", waitTime, gotten)
|
t.Errorf("expected waittime %v got %v", waitTime, gotten)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}).Times(1)
|
}).MinTimes(1).MaxTimes(3)
|
||||||
|
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
@ -118,6 +118,10 @@ func (h *handler) MinimumCycleDuration() time.Duration {
|
|||||||
return h.cycleDuration
|
return h.cycleDuration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *handler) LockDuration() time.Duration {
|
||||||
|
return h.cycleDuration / 3
|
||||||
|
}
|
||||||
|
|
||||||
func (h *handler) QueryLimit() uint64 {
|
func (h *handler) QueryLimit() uint64 {
|
||||||
return h.bulkLimit
|
return h.bulkLimit
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package spooler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
es_locker "github.com/caos/zitadel/internal/eventstore/locker"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,5 +15,5 @@ type locker struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error {
|
func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error {
|
||||||
return nil
|
return es_locker.Renew(l.dbClient, lockTable, lockerID, viewModel, waitTime)
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,10 @@ func (h *handler) MinimumCycleDuration() time.Duration {
|
|||||||
return h.cycleDuration
|
return h.cycleDuration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *handler) LockDuration() time.Duration {
|
||||||
|
return h.cycleDuration / 3
|
||||||
|
}
|
||||||
|
|
||||||
func (h *handler) QueryLimit() uint64 {
|
func (h *handler) QueryLimit() uint64 {
|
||||||
return h.bulkLimit
|
return h.bulkLimit
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package spooler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
es_locker "github.com/caos/zitadel/internal/eventstore/locker"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,5 +15,5 @@ type locker struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error {
|
func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error {
|
||||||
return nil
|
return es_locker.Renew(l.dbClient, lockTable, lockerID, viewModel, waitTime)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user