diff --git a/api/converter/from_pb.go b/api/converter/from_pb.go index d93912593..13fe4b9b3 100644 --- a/api/converter/from_pb.go +++ b/api/converter/from_pb.go @@ -18,6 +18,7 @@ package converter import ( "fmt" + gotime "time" protoTypes "github.com/gogo/protobuf/types" @@ -69,14 +70,15 @@ func FromProject(pbProject *api.Project) (*types.Project, error) { return nil, fmt.Errorf("convert updatedAt to timestamp: %w", err) } return &types.Project{ - ID: types.ID(pbProject.Id), - Name: pbProject.Name, - AuthWebhookURL: pbProject.AuthWebhookUrl, - AuthWebhookMethods: pbProject.AuthWebhookMethods, - PublicKey: pbProject.PublicKey, - SecretKey: pbProject.SecretKey, - CreatedAt: createdAt, - UpdatedAt: updatedAt, + ID: types.ID(pbProject.Id), + Name: pbProject.Name, + AuthWebhookURL: pbProject.AuthWebhookUrl, + AuthWebhookMethods: pbProject.AuthWebhookMethods, + ClientDeactivateThreshold: gotime.Duration(pbProject.ClientDeactivateThreshold), + PublicKey: pbProject.PublicKey, + SecretKey: pbProject.SecretKey, + CreatedAt: createdAt, + UpdatedAt: updatedAt, }, nil } @@ -663,6 +665,9 @@ func FromUpdatableProjectFields(pbProjectFields *api.UpdatableProjectFields) (*t if pbProjectFields.AuthWebhookMethods != nil { updatableProjectFields.AuthWebhookMethods = &pbProjectFields.AuthWebhookMethods.Methods } + if pbProjectFields.ClientDeactivateThreshold != 0 { + updatableProjectFields.ClientDeactivateThreshold = gotime.Duration(pbProjectFields.ClientDeactivateThreshold) + } return updatableProjectFields, nil } diff --git a/api/converter/to_pb.go b/api/converter/to_pb.go index 5a51c9f55..323904731 100644 --- a/api/converter/to_pb.go +++ b/api/converter/to_pb.go @@ -72,14 +72,15 @@ func ToProject(project *types.Project) (*api.Project, error) { } return &api.Project{ - Id: project.ID.String(), - Name: project.Name, - AuthWebhookUrl: project.AuthWebhookURL, - AuthWebhookMethods: project.AuthWebhookMethods, - PublicKey: project.PublicKey, - SecretKey: project.SecretKey, - CreatedAt: pbCreatedAt, - UpdatedAt: pbUpdatedAt, + Id: project.ID.String(), + Name: project.Name, + AuthWebhookUrl: project.AuthWebhookURL, + AuthWebhookMethods: project.AuthWebhookMethods, + ClientDeactivateThreshold: int64(project.ClientDeactivateThreshold), + PublicKey: project.PublicKey, + SecretKey: project.SecretKey, + CreatedAt: pbCreatedAt, + UpdatedAt: pbUpdatedAt, }, nil } @@ -514,5 +515,8 @@ func ToUpdatableProjectFields(fields *types.UpdatableProjectFields) (*api.Updata } else { pbUpdatableProjectFields.AuthWebhookMethods = nil } + if fields.ClientDeactivateThreshold != 0 { + pbUpdatableProjectFields.ClientDeactivateThreshold = int64(fields.ClientDeactivateThreshold) + } return pbUpdatableProjectFields, nil } diff --git a/api/types/project.go b/api/types/project.go index 9137d7891..4975fbc01 100644 --- a/api/types/project.go +++ b/api/types/project.go @@ -37,6 +37,10 @@ type Project struct { // AuthWebhookMethods is the methods that run the authorization webhook. AuthWebhookMethods []string `json:"auth_webhook_methods"` + // ClientDeactivateThreshold is the time after which clients in + // specific project are considered deactivate for housekeeping. + ClientDeactivateThreshold time.Duration `bson:"client_deactivate_threshold"` + // PublicKey is the API key of this project. PublicKey string `json:"public_key"` diff --git a/api/types/updatable_project_fields.go b/api/types/updatable_project_fields.go index 35b21953d..e11eebedf 100644 --- a/api/types/updatable_project_fields.go +++ b/api/types/updatable_project_fields.go @@ -19,6 +19,7 @@ package types import ( "errors" + "time" "github.com/go-playground/validator/v10" ) @@ -36,11 +37,14 @@ type UpdatableProjectFields struct { // AuthWebhookMethods is the methods that run the authorization webhook. AuthWebhookMethods *[]string `bson:"auth_webhook_methods,omitempty" validate:"omitempty,invalid_webhook_method"` + + // ClientDeactivateThreshold is the time after which clients in specific project are considered deactivate. + ClientDeactivateThreshold time.Duration `bson:"client_deactivate_threshold,omitempty" validate:"omitempty"` } // Validate validates the UpdatableProjectFields. func (i *UpdatableProjectFields) Validate() error { - if i.Name == nil && i.AuthWebhookURL == nil && i.AuthWebhookMethods == nil { + if i.Name == nil && i.AuthWebhookURL == nil && i.AuthWebhookMethods == nil && i.ClientDeactivateThreshold == 0 { return ErrEmptyProjectFields } diff --git a/api/types/updatable_project_fields_test.go b/api/types/updatable_project_fields_test.go index d3ef8f750..b4896674d 100644 --- a/api/types/updatable_project_fields_test.go +++ b/api/types/updatable_project_fields_test.go @@ -18,6 +18,7 @@ package types_test import ( "testing" + "time" "github.com/stretchr/testify/assert" @@ -33,10 +34,12 @@ func TestUpdatableProjectFields(t *testing.T) { string(types.AttachDocument), string(types.WatchDocuments), } + newClientDeactivateThreshold := 1 * time.Hour fields := &types.UpdatableProjectFields{ - Name: &newName, - AuthWebhookURL: &newAuthWebhookURL, - AuthWebhookMethods: &newAuthWebhookMethods, + Name: &newName, + AuthWebhookURL: &newAuthWebhookURL, + AuthWebhookMethods: &newAuthWebhookMethods, + ClientDeactivateThreshold: newClientDeactivateThreshold, } assert.NoError(t, fields.Validate()) @@ -49,8 +52,9 @@ func TestUpdatableProjectFields(t *testing.T) { assert.NoError(t, fields.Validate()) fields = &types.UpdatableProjectFields{ - Name: &newName, - AuthWebhookURL: &newAuthWebhookURL, + Name: &newName, + AuthWebhookURL: &newAuthWebhookURL, + ClientDeactivateThreshold: newClientDeactivateThreshold, } assert.NoError(t, fields.Validate()) @@ -59,9 +63,10 @@ func TestUpdatableProjectFields(t *testing.T) { "InvalidMethods", } fields = &types.UpdatableProjectFields{ - Name: &newName, - AuthWebhookURL: &newAuthWebhookURL, - AuthWebhookMethods: &newAuthWebhookMethods, + Name: &newName, + AuthWebhookURL: &newAuthWebhookURL, + AuthWebhookMethods: &newAuthWebhookMethods, + ClientDeactivateThreshold: newClientDeactivateThreshold, } assert.ErrorAs(t, fields.Validate(), &invalidFieldsError) }) diff --git a/api/yorkie/v1/resources.pb.go b/api/yorkie/v1/resources.pb.go index 2001d44a0..12581b59e 100644 --- a/api/yorkie/v1/resources.pb.go +++ b/api/yorkie/v1/resources.pb.go @@ -2017,17 +2017,18 @@ func (m *User) GetCreatedAt() *types.Timestamp { } type Project struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - PublicKey string `protobuf:"bytes,3,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` - SecretKey string `protobuf:"bytes,4,opt,name=secret_key,json=secretKey,proto3" json:"secret_key,omitempty"` - AuthWebhookUrl string `protobuf:"bytes,5,opt,name=auth_webhook_url,json=authWebhookUrl,proto3" json:"auth_webhook_url,omitempty"` - AuthWebhookMethods []string `protobuf:"bytes,6,rep,name=auth_webhook_methods,json=authWebhookMethods,proto3" json:"auth_webhook_methods,omitempty"` - CreatedAt *types.Timestamp `protobuf:"bytes,7,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` - UpdatedAt *types.Timestamp `protobuf:"bytes,8,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + PublicKey string `protobuf:"bytes,3,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` + SecretKey string `protobuf:"bytes,4,opt,name=secret_key,json=secretKey,proto3" json:"secret_key,omitempty"` + AuthWebhookUrl string `protobuf:"bytes,5,opt,name=auth_webhook_url,json=authWebhookUrl,proto3" json:"auth_webhook_url,omitempty"` + AuthWebhookMethods []string `protobuf:"bytes,6,rep,name=auth_webhook_methods,json=authWebhookMethods,proto3" json:"auth_webhook_methods,omitempty"` + ClientDeactivateThreshold int64 `protobuf:"varint,7,opt,name=client_deactivate_threshold,json=clientDeactivateThreshold,proto3" json:"client_deactivate_threshold,omitempty"` + CreatedAt *types.Timestamp `protobuf:"bytes,8,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + UpdatedAt *types.Timestamp `protobuf:"bytes,9,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Project) Reset() { *m = Project{} } @@ -2105,6 +2106,13 @@ func (m *Project) GetAuthWebhookMethods() []string { return nil } +func (m *Project) GetClientDeactivateThreshold() int64 { + if m != nil { + return m.ClientDeactivateThreshold + } + return 0 +} + func (m *Project) GetCreatedAt() *types.Timestamp { if m != nil { return m.CreatedAt @@ -2120,12 +2128,13 @@ func (m *Project) GetUpdatedAt() *types.Timestamp { } type UpdatableProjectFields struct { - Name *types.StringValue `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - AuthWebhookUrl *types.StringValue `protobuf:"bytes,2,opt,name=auth_webhook_url,json=authWebhookUrl,proto3" json:"auth_webhook_url,omitempty"` - AuthWebhookMethods *UpdatableProjectFields_AuthWebhookMethods `protobuf:"bytes,3,opt,name=auth_webhook_methods,json=authWebhookMethods,proto3" json:"auth_webhook_methods,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Name *types.StringValue `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + AuthWebhookUrl *types.StringValue `protobuf:"bytes,2,opt,name=auth_webhook_url,json=authWebhookUrl,proto3" json:"auth_webhook_url,omitempty"` + AuthWebhookMethods *UpdatableProjectFields_AuthWebhookMethods `protobuf:"bytes,3,opt,name=auth_webhook_methods,json=authWebhookMethods,proto3" json:"auth_webhook_methods,omitempty"` + ClientDeactivateThreshold int64 `protobuf:"varint,4,opt,name=client_deactivate_threshold,json=clientDeactivateThreshold,proto3" json:"client_deactivate_threshold,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *UpdatableProjectFields) Reset() { *m = UpdatableProjectFields{} } @@ -2182,6 +2191,13 @@ func (m *UpdatableProjectFields) GetAuthWebhookMethods() *UpdatableProjectFields return nil } +func (m *UpdatableProjectFields) GetClientDeactivateThreshold() int64 { + if m != nil { + return m.ClientDeactivateThreshold + } + return 0 +} + type UpdatableProjectFields_AuthWebhookMethods struct { Methods []string `protobuf:"bytes,1,rep,name=methods,proto3" json:"methods,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -2770,144 +2786,146 @@ func init() { func init() { proto.RegisterFile("yorkie/v1/resources.proto", fileDescriptor_36361b2f5d0f0896) } var fileDescriptor_36361b2f5d0f0896 = []byte{ - // 2177 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x59, 0xdf, 0x8f, 0xdb, 0x58, - 0xf5, 0x8f, 0x1d, 0xe7, 0x87, 0x4f, 0xa6, 0x9d, 0xf4, 0x4e, 0x7f, 0xa4, 0xd9, 0x76, 0x76, 0x9a, - 0x7e, 0xbf, 0x65, 0xb6, 0x85, 0x4c, 0x3b, 0xb4, 0x0b, 0x6c, 0xb5, 0x12, 0x9e, 0xc4, 0xdb, 0x4c, - 0x77, 0x9a, 0x8c, 0x9c, 0x4c, 0x97, 0x56, 0x42, 0x96, 0xc7, 0xbe, 0xed, 0x78, 0x27, 0xb1, 0xbd, - 0xb6, 0x93, 0x6d, 0x1e, 0x78, 0x41, 0xf0, 0x06, 0xef, 0xfc, 0x01, 0x48, 0x48, 0xfc, 0x07, 0xfb, - 0x80, 0x90, 0x78, 0x40, 0xbc, 0x01, 0x02, 0x89, 0x57, 0xe8, 0x3e, 0x20, 0x1e, 0x01, 0x89, 0x67, - 0x74, 0xef, 0xb5, 0x1d, 0xc7, 0x71, 0xb2, 0x69, 0xa8, 0x50, 0x97, 0x37, 0xdf, 0x7b, 0x3f, 0xe7, - 0xdc, 0xf3, 0xfb, 0x1e, 0xdf, 0x0b, 0x97, 0xc7, 0xb6, 0x7b, 0x6a, 0xe2, 0x9d, 0xd1, 0x9d, 0x1d, - 0x17, 0x7b, 0xf6, 0xd0, 0xd5, 0xb1, 0x57, 0x77, 0x5c, 0xdb, 0xb7, 0x91, 0xc8, 0x96, 0xea, 0xa3, - 0x3b, 0xd5, 0xb7, 0x9f, 0xdb, 0xf6, 0xf3, 0x3e, 0xde, 0xa1, 0x0b, 0xc7, 0xc3, 0x67, 0x3b, 0xbe, - 0x39, 0xc0, 0x9e, 0xaf, 0x0d, 0x1c, 0x86, 0xad, 0x6e, 0x26, 0x01, 0x9f, 0xba, 0x9a, 0xe3, 0x60, - 0x37, 0xe0, 0x55, 0xfb, 0x07, 0x07, 0xd0, 0x38, 0xd1, 0xac, 0xe7, 0xf8, 0x50, 0xd3, 0x4f, 0xd1, - 0x35, 0x58, 0x33, 0x6c, 0x7d, 0x38, 0xc0, 0x96, 0xaf, 0x9e, 0xe2, 0x71, 0x85, 0xdb, 0xe2, 0xb6, - 0x45, 0xa5, 0x14, 0xce, 0x7d, 0x88, 0xc7, 0xe8, 0x1e, 0x80, 0x7e, 0x82, 0xf5, 0x53, 0xc7, 0x36, - 0x2d, 0xbf, 0xc2, 0x6f, 0x71, 0xdb, 0xa5, 0xdd, 0x0b, 0xf5, 0x48, 0xa4, 0x7a, 0x23, 0x5a, 0x54, - 0x62, 0x40, 0x54, 0x85, 0xa2, 0x67, 0x69, 0x8e, 0x77, 0x62, 0xfb, 0x95, 0xec, 0x16, 0xb7, 0xbd, - 0xa6, 0x44, 0x63, 0x74, 0x0b, 0x0a, 0x3a, 0x95, 0xc1, 0xab, 0x08, 0x5b, 0xd9, 0xed, 0xd2, 0xee, - 0xb9, 0x29, 0x7e, 0x64, 0x45, 0x09, 0x11, 0x48, 0x82, 0x73, 0x03, 0xd3, 0x52, 0xbd, 0xb1, 0xa5, - 0x63, 0x43, 0xf5, 0x4d, 0xfd, 0x14, 0xfb, 0x95, 0xdc, 0x8c, 0x18, 0x3d, 0x73, 0x80, 0x7b, 0x74, - 0x51, 0x59, 0x1f, 0x98, 0x56, 0x97, 0xc2, 0xd9, 0x44, 0xed, 0x7b, 0x90, 0x67, 0x5c, 0xd1, 0x75, - 0xe0, 0x4d, 0x83, 0x6a, 0x59, 0xda, 0xdd, 0x98, 0xd9, 0x74, 0xbf, 0xa9, 0xf0, 0xa6, 0x81, 0x2a, - 0x50, 0x18, 0x60, 0xcf, 0xd3, 0x9e, 0x63, 0xaa, 0xae, 0xa8, 0x84, 0x43, 0x74, 0x17, 0xc0, 0x76, - 0xb0, 0xab, 0xf9, 0xa6, 0x6d, 0x79, 0x95, 0x2c, 0x95, 0xfd, 0x7c, 0x8c, 0x4d, 0x27, 0x5c, 0x54, - 0x62, 0xb8, 0xda, 0x0f, 0x39, 0x28, 0x86, 0x1b, 0xa0, 0xab, 0x00, 0x7a, 0xdf, 0x24, 0xf6, 0xf6, - 0xf0, 0x27, 0x54, 0x92, 0x33, 0x8a, 0xc8, 0x66, 0xba, 0xf8, 0x13, 0x74, 0x0d, 0xc0, 0xc3, 0xee, - 0x08, 0xbb, 0x74, 0x99, 0x6c, 0x9f, 0xdd, 0xe3, 0x6f, 0x73, 0x8a, 0xc8, 0x66, 0x09, 0xe4, 0x0a, - 0x14, 0xfa, 0xda, 0xc0, 0xb1, 0x5d, 0x66, 0x58, 0xb6, 0x1e, 0x4e, 0xa1, 0xcb, 0x50, 0xd4, 0x74, - 0xdf, 0x76, 0x55, 0xd3, 0xa8, 0x08, 0xd4, 0xee, 0x05, 0x3a, 0xde, 0x37, 0x6a, 0x9f, 0x9f, 0x07, - 0x31, 0x92, 0x10, 0x7d, 0x15, 0xb2, 0x1e, 0xf6, 0x03, 0x5b, 0x54, 0xd2, 0x94, 0xa8, 0x77, 0xb1, - 0xdf, 0xca, 0x28, 0x04, 0x46, 0xd0, 0x9a, 0x61, 0x04, 0xee, 0x4f, 0x47, 0x4b, 0x86, 0x41, 0xd0, - 0x9a, 0x61, 0xa0, 0x1d, 0x10, 0x06, 0xf6, 0x08, 0x53, 0xf9, 0x4a, 0xbb, 0x97, 0x53, 0xe1, 0x8f, - 0xec, 0x11, 0x6e, 0x65, 0x14, 0x0a, 0x44, 0xf7, 0x20, 0xef, 0x62, 0x4a, 0x22, 0x50, 0x92, 0xb7, - 0x52, 0x49, 0x14, 0x0a, 0x69, 0x65, 0x94, 0x00, 0x4c, 0xf6, 0xc1, 0x86, 0x19, 0x86, 0x43, 0xfa, - 0x3e, 0xb2, 0x61, 0x12, 0x2d, 0x28, 0x90, 0xec, 0xe3, 0xe1, 0x3e, 0xd6, 0xfd, 0x4a, 0x7e, 0xc1, - 0x3e, 0x5d, 0x0a, 0x21, 0xfb, 0x30, 0x30, 0xda, 0x85, 0x9c, 0xe7, 0x8f, 0xfb, 0xb8, 0x52, 0xa0, - 0x54, 0xd5, 0x74, 0x2a, 0x82, 0x68, 0x65, 0x14, 0x06, 0x45, 0xf7, 0xa1, 0x68, 0x5a, 0xba, 0x8b, - 0x35, 0x0f, 0x57, 0x8a, 0x94, 0xec, 0x6a, 0x2a, 0xd9, 0x7e, 0x00, 0x6a, 0x65, 0x94, 0x88, 0xa0, - 0xfa, 0x6b, 0x0e, 0xb2, 0x5d, 0xec, 0x93, 0xe0, 0x77, 0x34, 0x97, 0x44, 0x0b, 0x59, 0xf0, 0xb1, - 0xa1, 0x6a, 0xa1, 0xcb, 0xe6, 0x05, 0x3f, 0xc3, 0x37, 0x18, 0x5c, 0xf2, 0x51, 0x19, 0xb2, 0x24, - 0xb3, 0x59, 0x24, 0x93, 0x4f, 0xa2, 0xcd, 0x48, 0xeb, 0x0f, 0x43, 0xf7, 0x5c, 0x89, 0x31, 0x7a, - 0xd8, 0xed, 0xb4, 0xe5, 0x3e, 0x26, 0xb9, 0xdf, 0x35, 0x07, 0x4e, 0x1f, 0x2b, 0x0c, 0x8a, 0xde, - 0x85, 0x12, 0x7e, 0x81, 0xf5, 0x61, 0x20, 0x82, 0xb0, 0x48, 0x04, 0x08, 0x91, 0x92, 0x5f, 0xfd, - 0x27, 0x07, 0x59, 0xc9, 0x30, 0x5e, 0x87, 0x22, 0xef, 0xc3, 0xba, 0xe3, 0xe2, 0x51, 0x9c, 0x01, - 0xbf, 0x88, 0xc1, 0x19, 0x82, 0x9e, 0x90, 0xff, 0x37, 0xb5, 0xfe, 0x17, 0x07, 0x02, 0x89, 0xef, - 0x37, 0x40, 0xed, 0xbb, 0x00, 0x31, 0xca, 0xec, 0x22, 0x4a, 0x51, 0x8f, 0xa8, 0x56, 0x55, 0xfc, - 0x33, 0x0e, 0xf2, 0x2c, 0x4b, 0x5f, 0x87, 0xea, 0xd3, 0xb2, 0xf3, 0xab, 0xc9, 0x9e, 0x5d, 0x56, - 0xf6, 0x5f, 0x09, 0x20, 0x90, 0x62, 0xf1, 0x3a, 0x24, 0xbf, 0x09, 0xc2, 0x33, 0xd7, 0x1e, 0x04, - 0x32, 0x5f, 0x8c, 0x53, 0xe1, 0x17, 0x7e, 0xdb, 0x36, 0xf0, 0xa1, 0xed, 0x29, 0x14, 0x83, 0x6e, - 0x00, 0xef, 0xdb, 0x81, 0x98, 0xf3, 0x90, 0xbc, 0x6f, 0xa3, 0x13, 0xb8, 0x34, 0x91, 0x47, 0x1d, - 0x68, 0x8e, 0x7a, 0x3c, 0x56, 0x69, 0x6d, 0x0f, 0x4e, 0xd1, 0xdd, 0xb9, 0xf5, 0xaf, 0x1e, 0x49, - 0xf6, 0x48, 0x73, 0xf6, 0xc6, 0x12, 0x21, 0x92, 0x2d, 0xdf, 0x1d, 0x2b, 0x1b, 0xfa, 0xec, 0x0a, - 0x39, 0x00, 0x75, 0xdb, 0xf2, 0xb1, 0xc5, 0x2a, 0xab, 0xa8, 0x84, 0xc3, 0xa4, 0x6d, 0xf3, 0x4b, - 0xda, 0x16, 0xed, 0x03, 0x68, 0xbe, 0xef, 0x9a, 0xc7, 0x43, 0x1f, 0x7b, 0x95, 0x02, 0x15, 0xf7, - 0x9d, 0xf9, 0xe2, 0x4a, 0x11, 0x96, 0x49, 0x19, 0x23, 0xae, 0x7e, 0x17, 0x2a, 0xf3, 0xb4, 0x09, - 0x6b, 0x1d, 0x37, 0xa9, 0x75, 0xb7, 0xc2, 0xac, 0x5f, 0x18, 0x3d, 0x0c, 0xf3, 0x1e, 0xff, 0x4d, - 0xae, 0xfa, 0x3e, 0xac, 0x27, 0x76, 0x4f, 0xe1, 0x7a, 0x3e, 0xce, 0x55, 0x8c, 0x93, 0xff, 0x89, - 0x83, 0x3c, 0x3b, 0x3e, 0xde, 0xd4, 0x30, 0x5a, 0x35, 0xb5, 0xff, 0xc2, 0x43, 0x8e, 0x1e, 0x71, - 0x6f, 0xaa, 0x62, 0x0f, 0xa7, 0x62, 0x8c, 0xa5, 0xc4, 0xcd, 0xf9, 0x27, 0xf5, 0xa2, 0x20, 0x4b, - 0x1a, 0x29, 0xb7, 0xac, 0x91, 0xfe, 0xc3, 0xe8, 0xf9, 0x8c, 0x83, 0x62, 0xd8, 0x0f, 0xbc, 0x0e, - 0x33, 0xef, 0x4e, 0x47, 0xff, 0x2a, 0x67, 0xde, 0xb2, 0xe5, 0x73, 0x2f, 0x0f, 0xc2, 0xb1, 0x6d, - 0x8c, 0x6b, 0x7f, 0xe7, 0xe0, 0xdc, 0x0c, 0xf3, 0x44, 0x29, 0xe7, 0x96, 0x2c, 0xe5, 0xb7, 0xa1, - 0x48, 0xce, 0x92, 0x2f, 0x2e, 0xff, 0x05, 0x0a, 0x63, 0x47, 0x06, 0xeb, 0x0d, 0x97, 0x38, 0xee, - 0x02, 0xa0, 0xe4, 0xa3, 0x6d, 0x10, 0xfc, 0xb1, 0xc3, 0x9a, 0xcf, 0xb3, 0x53, 0x1d, 0xfd, 0x63, - 0x62, 0x93, 0xde, 0xd8, 0xc1, 0x0a, 0x45, 0x4c, 0x7c, 0x97, 0xa3, 0xbd, 0x35, 0x1b, 0xd4, 0x7e, - 0x5a, 0x82, 0x52, 0x4c, 0x67, 0xd4, 0x84, 0xd2, 0xc7, 0x9e, 0x6d, 0xa9, 0xf6, 0xf1, 0xc7, 0xa4, - 0xd7, 0x64, 0xea, 0x5e, 0x4b, 0xb7, 0x3e, 0xfd, 0xee, 0x50, 0x60, 0x2b, 0xa3, 0x00, 0xa1, 0x63, - 0x23, 0x24, 0x01, 0x1d, 0xa9, 0x9a, 0xeb, 0x6a, 0xe3, 0x40, 0xff, 0xad, 0x05, 0x4c, 0x24, 0x82, - 0x6b, 0x65, 0x14, 0x91, 0x50, 0xd1, 0x01, 0xfa, 0x36, 0x88, 0x8e, 0x6b, 0x0e, 0x4c, 0xdf, 0x8c, - 0xba, 0xf1, 0x79, 0x1c, 0x0e, 0x43, 0x1c, 0xe1, 0x10, 0x11, 0xa1, 0x3b, 0x20, 0xf8, 0xf8, 0x45, - 0x98, 0x02, 0x6f, 0xcd, 0x21, 0x26, 0xb9, 0x48, 0x9a, 0x6c, 0x02, 0x45, 0xef, 0x91, 0xe3, 0x63, - 0x68, 0xf9, 0xd8, 0x0d, 0x0e, 0x88, 0xcd, 0x39, 0x54, 0x0d, 0x86, 0x6a, 0x65, 0x94, 0x90, 0xa0, - 0xfa, 0x47, 0x0e, 0x60, 0x62, 0x10, 0xb4, 0x0d, 0x39, 0xcb, 0x36, 0xb0, 0x57, 0xe1, 0x68, 0x3a, - 0xa3, 0x18, 0x23, 0xa5, 0xd5, 0x23, 0xd9, 0xaf, 0x30, 0xc0, 0x8a, 0xbd, 0x42, 0x3c, 0xc0, 0xb2, - 0x2b, 0x04, 0x98, 0xb0, 0x5c, 0x80, 0x55, 0xff, 0xc0, 0x81, 0x18, 0xb9, 0x68, 0xa1, 0x56, 0x0f, - 0xa4, 0x2f, 0x8f, 0x56, 0x7f, 0xe3, 0x40, 0x8c, 0xc2, 0x26, 0x4a, 0x22, 0x6e, 0xf9, 0x24, 0xe2, - 0x63, 0x49, 0xb4, 0x62, 0xa7, 0x1a, 0xd7, 0x55, 0x58, 0x41, 0xd7, 0xdc, 0x92, 0xba, 0xfe, 0x96, - 0x03, 0x81, 0x44, 0x39, 0x7a, 0x67, 0xda, 0x79, 0x1b, 0x29, 0x27, 0xd2, 0x97, 0xc3, 0x7b, 0x7f, - 0xe5, 0xa0, 0x10, 0x64, 0xe0, 0xff, 0xb6, 0xef, 0xa2, 0xa3, 0xe9, 0x11, 0x14, 0x82, 0xaa, 0x91, - 0x72, 0x2a, 0xdf, 0x86, 0x02, 0x66, 0x75, 0x29, 0xa5, 0x2b, 0x89, 0x55, 0x2d, 0x25, 0x84, 0xd5, - 0x74, 0x28, 0x04, 0xe9, 0x8a, 0x6e, 0x80, 0x60, 0x91, 0x2a, 0xc9, 0x2a, 0x7d, 0x5a, 0x42, 0xd3, - 0xf5, 0x15, 0x36, 0x79, 0x0a, 0x6b, 0x61, 0x58, 0x91, 0xce, 0x62, 0x62, 0x7f, 0x2e, 0xd6, 0x3c, - 0x10, 0xbb, 0x0c, 0x1d, 0x63, 0xb9, 0x48, 0x0b, 0x80, 0x92, 0x5f, 0xfb, 0x3d, 0x0f, 0xc5, 0x90, - 0x39, 0xfa, 0xff, 0xd8, 0xd5, 0xd8, 0x85, 0x94, 0xa0, 0x0e, 0x2e, 0xc7, 0x52, 0x9b, 0x97, 0x15, - 0x8f, 0xdd, 0x7b, 0x50, 0x32, 0x2d, 0x4f, 0xa5, 0xbf, 0xb7, 0xc1, 0x75, 0xd5, 0xdc, 0xbd, 0x45, - 0xd3, 0xf2, 0x0e, 0x5d, 0x3c, 0xda, 0x37, 0x50, 0x63, 0xaa, 0xd1, 0xcb, 0xd1, 0x34, 0xbc, 0x9e, - 0x42, 0xb5, 0xf0, 0x37, 0xe2, 0xf1, 0x32, 0x9d, 0xda, 0xd7, 0xa6, 0xfb, 0xa7, 0x4b, 0x29, 0x9b, - 0x10, 0x26, 0xb1, 0x16, 0xae, 0xf6, 0x14, 0x60, 0x22, 0xf5, 0x8a, 0x6d, 0xcf, 0x45, 0xc8, 0xdb, - 0xcf, 0x9e, 0x79, 0x98, 0x79, 0x32, 0xa7, 0x04, 0xa3, 0xda, 0x00, 0x84, 0x23, 0x0f, 0xbb, 0xe8, - 0x6c, 0xe4, 0x2a, 0x91, 0xfa, 0xa4, 0x0a, 0xc5, 0xa1, 0x87, 0x5d, 0x4b, 0x1b, 0x84, 0x6e, 0x89, - 0xc6, 0xe8, 0x5b, 0x29, 0x99, 0x59, 0xad, 0xb3, 0x5b, 0xe2, 0x7a, 0x78, 0x4b, 0x4c, 0xe5, 0xa0, - 0xd7, 0xc8, 0x31, 0x31, 0x6a, 0xbf, 0xe0, 0xa1, 0x70, 0xe8, 0xda, 0xf4, 0x20, 0x4e, 0x6e, 0x89, - 0x40, 0x88, 0x6d, 0x47, 0xbf, 0xd1, 0x55, 0x00, 0x67, 0x78, 0xdc, 0x37, 0x75, 0x7a, 0x95, 0x9c, - 0xa5, 0x2b, 0x22, 0x9b, 0xf9, 0x10, 0x8f, 0xc9, 0xb2, 0x87, 0x75, 0x17, 0xb3, 0x9b, 0x66, 0x81, - 0x2d, 0xb3, 0x19, 0xb2, 0xbc, 0x0d, 0x65, 0x6d, 0xe8, 0x9f, 0xa8, 0x9f, 0xe2, 0xe3, 0x13, 0xdb, - 0x3e, 0x55, 0x87, 0x6e, 0x3f, 0xf8, 0xfb, 0x3c, 0x4b, 0xe6, 0x3f, 0x62, 0xd3, 0x47, 0x6e, 0x1f, - 0xdd, 0x86, 0xf3, 0x53, 0xc8, 0x01, 0xf6, 0x4f, 0x6c, 0xc3, 0xab, 0xe4, 0xb7, 0xb2, 0xdb, 0xa2, - 0x82, 0x62, 0xe8, 0x47, 0x6c, 0x25, 0x61, 0x84, 0xc2, 0x2b, 0x18, 0x81, 0x90, 0xc6, 0x32, 0xab, - 0xf8, 0xc5, 0xa4, 0x93, 0xf4, 0xfa, 0x39, 0x0f, 0x17, 0x8f, 0xc8, 0x48, 0x3b, 0xee, 0xe3, 0xc0, - 0x90, 0x1f, 0x98, 0xb8, 0x6f, 0x78, 0xe8, 0x76, 0x60, 0x3e, 0x2e, 0xe8, 0xcb, 0x93, 0xfc, 0xba, - 0xbe, 0x6b, 0x5a, 0xcf, 0x69, 0xed, 0x0d, 0x8c, 0xfb, 0x41, 0x8a, 0x79, 0xf8, 0x25, 0xa8, 0x93, - 0xc6, 0x7b, 0x36, 0xc7, 0x78, 0x2c, 0x32, 0xee, 0xc6, 0x62, 0x33, 0x5d, 0xf4, 0xba, 0x34, 0x63, - 0xde, 0x34, 0x93, 0x57, 0xeb, 0x80, 0x66, 0x91, 0xec, 0x6a, 0x9d, 0x6d, 0xc8, 0x51, 0x6f, 0x85, - 0xc3, 0xda, 0xf7, 0x79, 0x58, 0x6f, 0x06, 0xcf, 0x0e, 0xdd, 0xe1, 0x60, 0xa0, 0xb9, 0xe3, 0x99, - 0xa0, 0x9b, 0xbd, 0xca, 0x4c, 0xbe, 0x32, 0x88, 0xb1, 0x57, 0x86, 0x69, 0xa7, 0x0b, 0xaf, 0xe2, - 0xf4, 0xfb, 0x50, 0xd2, 0x74, 0x1d, 0x7b, 0x5e, 0xfc, 0x9c, 0x59, 0x44, 0x0b, 0x21, 0x7c, 0x26, - 0x62, 0xf2, 0xaf, 0x12, 0x31, 0x3f, 0xe2, 0xa0, 0x78, 0xe8, 0x62, 0x0f, 0x5b, 0x3a, 0x3d, 0x69, - 0xf5, 0xbe, 0xad, 0x9f, 0x52, 0x03, 0xe4, 0x14, 0x36, 0x20, 0xfd, 0x38, 0x71, 0x4b, 0x85, 0xa7, - 0x65, 0x2f, 0x7e, 0xa5, 0x1c, 0x12, 0xd6, 0x9b, 0x9a, 0xaf, 0xb1, 0x82, 0x47, 0xa1, 0xd5, 0x6f, - 0x80, 0x18, 0x4d, 0xbd, 0xca, 0xef, 0x68, 0x6d, 0x1f, 0xf2, 0x0d, 0xfa, 0x32, 0x11, 0xf3, 0xc4, - 0x1a, 0xf5, 0xc4, 0x0e, 0x14, 0x9d, 0x60, 0xbb, 0x20, 0x0a, 0x37, 0x52, 0x24, 0x51, 0x22, 0x50, - 0xed, 0x5d, 0x28, 0x30, 0x56, 0x1e, 0x7d, 0xfd, 0x61, 0x9f, 0x41, 0x0b, 0x35, 0xf5, 0xfa, 0x43, - 0x57, 0x94, 0x10, 0x51, 0x6b, 0x03, 0x4c, 0x1e, 0x98, 0x12, 0xaf, 0x23, 0x5c, 0xda, 0xeb, 0xc8, - 0xf4, 0xfb, 0x0a, 0x9f, 0x78, 0x5f, 0xa9, 0xfd, 0x80, 0x83, 0x52, 0xec, 0xe2, 0xe0, 0xf5, 0x16, - 0x68, 0xf4, 0x15, 0x58, 0x77, 0x71, 0x5f, 0x23, 0xed, 0xb0, 0x1a, 0x00, 0xb2, 0x14, 0x70, 0x36, - 0x9c, 0xee, 0xb0, 0x4a, 0xae, 0x03, 0x4c, 0x38, 0xc7, 0x5f, 0x74, 0xb8, 0xd9, 0x17, 0x9d, 0x2b, - 0x20, 0x1a, 0xb8, 0x4f, 0xba, 0x6c, 0xec, 0x86, 0x0a, 0x45, 0x13, 0x53, 0xef, 0x3d, 0xd9, 0xe9, - 0xf7, 0x9e, 0x1f, 0x73, 0x50, 0x6c, 0xda, 0xba, 0x3c, 0x22, 0x1e, 0xbc, 0x35, 0xd5, 0xe1, 0xc5, - 0x4f, 0xb2, 0x10, 0x12, 0x6b, 0xf2, 0x76, 0x80, 0xd5, 0x6d, 0xef, 0x24, 0xd8, 0x32, 0xd5, 0x49, - 0x13, 0x0c, 0xba, 0x0e, 0x67, 0xe2, 0xef, 0x88, 0xec, 0x6d, 0x4c, 0x54, 0xd6, 0x62, 0x0f, 0x89, - 0xde, 0xcd, 0x5f, 0xf2, 0x20, 0x46, 0xed, 0x24, 0xda, 0x80, 0xf5, 0xc7, 0xd2, 0xc1, 0x91, 0xac, - 0xf6, 0x9e, 0x1c, 0xca, 0x6a, 0xfb, 0xe8, 0xe0, 0xa0, 0x9c, 0x41, 0x17, 0x01, 0xc5, 0x26, 0xf7, - 0x3a, 0x9d, 0x03, 0x59, 0x6a, 0x97, 0xb9, 0xc4, 0xfc, 0x7e, 0xbb, 0x27, 0x3f, 0x90, 0x95, 0x32, - 0x9f, 0x60, 0x72, 0xd0, 0x69, 0x3f, 0x28, 0x67, 0xd1, 0x05, 0x38, 0x17, 0x9b, 0x6c, 0x76, 0x8e, - 0xf6, 0x0e, 0xe4, 0xb2, 0x90, 0x98, 0xee, 0xf6, 0x94, 0xfd, 0xf6, 0x83, 0x72, 0x0e, 0x9d, 0x87, - 0x72, 0x7c, 0xcb, 0x27, 0x3d, 0xb9, 0x5b, 0xce, 0x27, 0x18, 0x37, 0xa5, 0x9e, 0x5c, 0x2e, 0xa0, - 0x2a, 0x5c, 0x8c, 0x4d, 0x92, 0x76, 0x4d, 0xed, 0xec, 0x3d, 0x94, 0x1b, 0xbd, 0x72, 0x11, 0x5d, - 0x86, 0x0b, 0xc9, 0x35, 0x49, 0x51, 0xa4, 0x27, 0x65, 0x31, 0xc1, 0xab, 0x27, 0x7f, 0xa7, 0x57, - 0x86, 0x04, 0xaf, 0x40, 0x23, 0xb5, 0xd1, 0xee, 0x95, 0x4b, 0xe8, 0x12, 0x6c, 0x24, 0xb4, 0xa2, - 0x0b, 0x6b, 0x37, 0x7f, 0xc6, 0xc1, 0x5a, 0xdc, 0x5d, 0xe8, 0xff, 0x60, 0xab, 0xd9, 0x69, 0xa8, - 0xf2, 0x63, 0xb9, 0xdd, 0x0b, 0xd5, 0x6d, 0x1c, 0x3d, 0x92, 0xdb, 0xbd, 0xae, 0xda, 0x68, 0x49, - 0xed, 0x07, 0x72, 0xb3, 0x9c, 0x59, 0x88, 0xfa, 0x48, 0xea, 0x35, 0x5a, 0x72, 0xb3, 0xcc, 0xa1, - 0x1b, 0x50, 0x9b, 0x8b, 0x3a, 0x6a, 0x87, 0x38, 0x1e, 0x5d, 0x87, 0xb7, 0x13, 0xb8, 0x43, 0x45, - 0xee, 0xca, 0xed, 0x86, 0x1c, 0x6d, 0x99, 0xdd, 0xbb, 0xf5, 0x9b, 0x97, 0x9b, 0xdc, 0xef, 0x5e, - 0x6e, 0x72, 0x7f, 0x7e, 0xb9, 0xc9, 0xfd, 0xe4, 0xf3, 0xcd, 0x0c, 0x9c, 0x33, 0xf0, 0x28, 0x8c, - 0x21, 0xcd, 0x31, 0xeb, 0xa3, 0x3b, 0x87, 0xdc, 0x53, 0xa1, 0x7e, 0x7f, 0x74, 0xe7, 0x38, 0x4f, - 0xab, 0xe2, 0xd7, 0xff, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x1a, 0xf5, 0xec, 0x18, 0x04, 0x1f, 0x00, - 0x00, + // 2214 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x59, 0xdd, 0x8f, 0xdb, 0x58, + 0x15, 0x8f, 0x1d, 0xe7, 0xc3, 0x27, 0xb3, 0x9d, 0xf4, 0x4e, 0x3f, 0xd2, 0xb4, 0x9d, 0x9d, 0xa6, + 0x50, 0x66, 0x5b, 0xc8, 0xb4, 0x43, 0xbb, 0xc0, 0x56, 0x8b, 0xf0, 0x24, 0xde, 0x66, 0xba, 0xd3, + 0xcc, 0xc8, 0xc9, 0x74, 0x69, 0x25, 0x64, 0x79, 0xec, 0xdb, 0x8e, 0x77, 0x12, 0xdb, 0x6b, 0x3b, + 0xd9, 0xe6, 0x81, 0x17, 0x04, 0x6f, 0xf0, 0xce, 0x1f, 0x80, 0xc4, 0xdf, 0xb0, 0x4f, 0x48, 0x3c, + 0x20, 0xde, 0x00, 0x81, 0xc4, 0x2b, 0x74, 0x1f, 0x10, 0xbc, 0x01, 0x12, 0x6f, 0x48, 0xe8, 0xde, + 0x6b, 0x3b, 0x8e, 0xe3, 0xa4, 0x69, 0x18, 0xa1, 0x2e, 0x6f, 0xbe, 0xf7, 0xfe, 0xce, 0xb9, 0xe7, + 0xf3, 0x9e, 0xe3, 0x7b, 0xe1, 0xd2, 0xc8, 0x76, 0x4f, 0x4c, 0xbc, 0x35, 0xbc, 0xb3, 0xe5, 0x62, + 0xcf, 0x1e, 0xb8, 0x3a, 0xf6, 0xea, 0x8e, 0x6b, 0xfb, 0x36, 0x12, 0xd9, 0x52, 0x7d, 0x78, 0xa7, + 0xfa, 0xf6, 0x73, 0xdb, 0x7e, 0xde, 0xc3, 0x5b, 0x74, 0xe1, 0x68, 0xf0, 0x6c, 0xcb, 0x37, 0xfb, + 0xd8, 0xf3, 0xb5, 0xbe, 0xc3, 0xb0, 0xd5, 0xf5, 0x24, 0xe0, 0x53, 0x57, 0x73, 0x1c, 0xec, 0x06, + 0xbc, 0x6a, 0xff, 0xe0, 0x00, 0x1a, 0xc7, 0x9a, 0xf5, 0x1c, 0x1f, 0x68, 0xfa, 0x09, 0xba, 0x06, + 0x2b, 0x86, 0xad, 0x0f, 0xfa, 0xd8, 0xf2, 0xd5, 0x13, 0x3c, 0xaa, 0x70, 0x1b, 0xdc, 0xa6, 0xa8, + 0x94, 0xc2, 0xb9, 0x0f, 0xf1, 0x08, 0xdd, 0x03, 0xd0, 0x8f, 0xb1, 0x7e, 0xe2, 0xd8, 0xa6, 0xe5, + 0x57, 0xf8, 0x0d, 0x6e, 0xb3, 0xb4, 0x7d, 0xbe, 0x1e, 0x89, 0x54, 0x6f, 0x44, 0x8b, 0x4a, 0x0c, + 0x88, 0xaa, 0x50, 0xf4, 0x2c, 0xcd, 0xf1, 0x8e, 0x6d, 0xbf, 0x92, 0xdd, 0xe0, 0x36, 0x57, 0x94, + 0x68, 0x8c, 0x6e, 0x41, 0x41, 0xa7, 0x32, 0x78, 0x15, 0x61, 0x23, 0xbb, 0x59, 0xda, 0x3e, 0x3b, + 0xc1, 0x8f, 0xac, 0x28, 0x21, 0x02, 0x49, 0x70, 0xb6, 0x6f, 0x5a, 0xaa, 0x37, 0xb2, 0x74, 0x6c, + 0xa8, 0xbe, 0xa9, 0x9f, 0x60, 0xbf, 0x92, 0x9b, 0x12, 0xa3, 0x6b, 0xf6, 0x71, 0x97, 0x2e, 0x2a, + 0xab, 0x7d, 0xd3, 0xea, 0x50, 0x38, 0x9b, 0xa8, 0x7d, 0x1f, 0xf2, 0x8c, 0x2b, 0xba, 0x0e, 0xbc, + 0x69, 0x50, 0x2d, 0x4b, 0xdb, 0x6b, 0x53, 0x9b, 0xee, 0x36, 0x15, 0xde, 0x34, 0x50, 0x05, 0x0a, + 0x7d, 0xec, 0x79, 0xda, 0x73, 0x4c, 0xd5, 0x15, 0x95, 0x70, 0x88, 0xee, 0x02, 0xd8, 0x0e, 0x76, + 0x35, 0xdf, 0xb4, 0x2d, 0xaf, 0x92, 0xa5, 0xb2, 0x9f, 0x8b, 0xb1, 0xd9, 0x0f, 0x17, 0x95, 0x18, + 0xae, 0xf6, 0x23, 0x0e, 0x8a, 0xe1, 0x06, 0xe8, 0x2a, 0x80, 0xde, 0x33, 0x89, 0xbd, 0x3d, 0xfc, + 0x09, 0x95, 0xe4, 0x2d, 0x45, 0x64, 0x33, 0x1d, 0xfc, 0x09, 0xba, 0x06, 0xe0, 0x61, 0x77, 0x88, + 0x5d, 0xba, 0x4c, 0xb6, 0xcf, 0xee, 0xf0, 0xb7, 0x39, 0x45, 0x64, 0xb3, 0x04, 0x72, 0x05, 0x0a, + 0x3d, 0xad, 0xef, 0xd8, 0x2e, 0x33, 0x2c, 0x5b, 0x0f, 0xa7, 0xd0, 0x25, 0x28, 0x6a, 0xba, 0x6f, + 0xbb, 0xaa, 0x69, 0x54, 0x04, 0x6a, 0xf7, 0x02, 0x1d, 0xef, 0x1a, 0xb5, 0xcf, 0xcf, 0x81, 0x18, + 0x49, 0x88, 0xbe, 0x0a, 0x59, 0x0f, 0xfb, 0x81, 0x2d, 0x2a, 0x69, 0x4a, 0xd4, 0x3b, 0xd8, 0x6f, + 0x65, 0x14, 0x02, 0x23, 0x68, 0xcd, 0x30, 0x02, 0xf7, 0xa7, 0xa3, 0x25, 0xc3, 0x20, 0x68, 0xcd, + 0x30, 0xd0, 0x16, 0x08, 0x7d, 0x7b, 0x88, 0xa9, 0x7c, 0xa5, 0xed, 0x4b, 0xa9, 0xf0, 0x47, 0xf6, + 0x10, 0xb7, 0x32, 0x0a, 0x05, 0xa2, 0x7b, 0x90, 0x77, 0x31, 0x25, 0x11, 0x28, 0xc9, 0xe5, 0x54, + 0x12, 0x85, 0x42, 0x5a, 0x19, 0x25, 0x00, 0x93, 0x7d, 0xb0, 0x61, 0x86, 0xe1, 0x90, 0xbe, 0x8f, + 0x6c, 0x98, 0x44, 0x0b, 0x0a, 0x24, 0xfb, 0x78, 0xb8, 0x87, 0x75, 0xbf, 0x92, 0x9f, 0xb3, 0x4f, + 0x87, 0x42, 0xc8, 0x3e, 0x0c, 0x8c, 0xb6, 0x21, 0xe7, 0xf9, 0xa3, 0x1e, 0xae, 0x14, 0x28, 0x55, + 0x35, 0x9d, 0x8a, 0x20, 0x5a, 0x19, 0x85, 0x41, 0xd1, 0x7d, 0x28, 0x9a, 0x96, 0xee, 0x62, 0xcd, + 0xc3, 0x95, 0x22, 0x25, 0xbb, 0x9a, 0x4a, 0xb6, 0x1b, 0x80, 0x5a, 0x19, 0x25, 0x22, 0xa8, 0xfe, + 0x8a, 0x83, 0x6c, 0x07, 0xfb, 0x24, 0xf8, 0x1d, 0xcd, 0x25, 0xd1, 0x42, 0x16, 0x7c, 0x6c, 0xa8, + 0x5a, 0xe8, 0xb2, 0x59, 0xc1, 0xcf, 0xf0, 0x0d, 0x06, 0x97, 0x7c, 0x54, 0x86, 0x2c, 0xc9, 0x6c, + 0x16, 0xc9, 0xe4, 0x93, 0x68, 0x33, 0xd4, 0x7a, 0x83, 0xd0, 0x3d, 0x57, 0x62, 0x8c, 0x1e, 0x76, + 0xf6, 0xdb, 0x72, 0x0f, 0x93, 0xdc, 0xef, 0x98, 0x7d, 0xa7, 0x87, 0x15, 0x06, 0x45, 0xef, 0x42, + 0x09, 0xbf, 0xc0, 0xfa, 0x20, 0x10, 0x41, 0x98, 0x27, 0x02, 0x84, 0x48, 0xc9, 0xaf, 0xfe, 0x93, + 0x83, 0xac, 0x64, 0x18, 0xa7, 0xa1, 0xc8, 0xfb, 0xb0, 0xea, 0xb8, 0x78, 0x18, 0x67, 0xc0, 0xcf, + 0x63, 0xf0, 0x16, 0x41, 0x8f, 0xc9, 0xff, 0x97, 0x5a, 0xff, 0x8b, 0x03, 0x81, 0xc4, 0xf7, 0x1b, + 0xa0, 0xf6, 0x5d, 0x80, 0x18, 0x65, 0x76, 0x1e, 0xa5, 0xa8, 0x47, 0x54, 0xcb, 0x2a, 0xfe, 0x19, + 0x07, 0x79, 0x96, 0xa5, 0xa7, 0xa1, 0xfa, 0xa4, 0xec, 0xfc, 0x72, 0xb2, 0x67, 0x17, 0x95, 0xfd, + 0x97, 0x02, 0x08, 0xe4, 0xb0, 0x38, 0x0d, 0xc9, 0x6f, 0x82, 0xf0, 0xcc, 0xb5, 0xfb, 0x81, 0xcc, + 0x17, 0xe2, 0x54, 0xf8, 0x85, 0xdf, 0xb6, 0x0d, 0x7c, 0x60, 0x7b, 0x0a, 0xc5, 0xa0, 0x1b, 0xc0, + 0xfb, 0x76, 0x20, 0xe6, 0x2c, 0x24, 0xef, 0xdb, 0xe8, 0x18, 0x2e, 0x8e, 0xe5, 0x51, 0xfb, 0x9a, + 0xa3, 0x1e, 0x8d, 0x54, 0x7a, 0xb6, 0x07, 0x55, 0x74, 0x7b, 0xe6, 0xf9, 0x57, 0x8f, 0x24, 0x7b, + 0xa4, 0x39, 0x3b, 0x23, 0x89, 0x10, 0xc9, 0x96, 0xef, 0x8e, 0x94, 0x35, 0x7d, 0x7a, 0x85, 0x14, + 0x40, 0xdd, 0xb6, 0x7c, 0x6c, 0xb1, 0x93, 0x55, 0x54, 0xc2, 0x61, 0xd2, 0xb6, 0xf9, 0x05, 0x6d, + 0x8b, 0x76, 0x01, 0x34, 0xdf, 0x77, 0xcd, 0xa3, 0x81, 0x8f, 0xbd, 0x4a, 0x81, 0x8a, 0xfb, 0xce, + 0x6c, 0x71, 0xa5, 0x08, 0xcb, 0xa4, 0x8c, 0x11, 0x57, 0xbf, 0x07, 0x95, 0x59, 0xda, 0x84, 0x67, + 0x1d, 0x37, 0x3e, 0xeb, 0x6e, 0x85, 0x59, 0x3f, 0x37, 0x7a, 0x18, 0xe6, 0x3d, 0xfe, 0x9b, 0x5c, + 0xf5, 0x7d, 0x58, 0x4d, 0xec, 0x9e, 0xc2, 0xf5, 0x5c, 0x9c, 0xab, 0x18, 0x27, 0xff, 0x23, 0x07, + 0x79, 0x56, 0x3e, 0xde, 0xd4, 0x30, 0x5a, 0x36, 0xb5, 0xff, 0xcc, 0x43, 0x8e, 0x96, 0xb8, 0x37, + 0x55, 0xb1, 0x87, 0x13, 0x31, 0xc6, 0x52, 0xe2, 0xe6, 0xec, 0x4a, 0x3d, 0x2f, 0xc8, 0x92, 0x46, + 0xca, 0x2d, 0x6a, 0xa4, 0xff, 0x32, 0x7a, 0x3e, 0xe3, 0xa0, 0x18, 0xf6, 0x03, 0xa7, 0x61, 0xe6, + 0xed, 0xc9, 0xe8, 0x5f, 0xa6, 0xe6, 0x2d, 0x7a, 0x7c, 0xee, 0xe4, 0x41, 0x38, 0xb2, 0x8d, 0x51, + 0xed, 0xef, 0x1c, 0x9c, 0x9d, 0x62, 0x9e, 0x38, 0xca, 0xb9, 0x05, 0x8f, 0xf2, 0xdb, 0x50, 0x24, + 0xb5, 0xe4, 0xd5, 0xc7, 0x7f, 0x81, 0xc2, 0x58, 0xc9, 0x60, 0xbd, 0xe1, 0x02, 0xe5, 0x2e, 0x00, + 0x4a, 0x3e, 0xda, 0x04, 0xc1, 0x1f, 0x39, 0xac, 0xf9, 0x3c, 0x33, 0xd1, 0xd1, 0x3f, 0x26, 0x36, + 0xe9, 0x8e, 0x1c, 0xac, 0x50, 0xc4, 0xd8, 0x77, 0x39, 0xda, 0x5b, 0xb3, 0x41, 0xed, 0x67, 0x25, + 0x28, 0xc5, 0x74, 0x46, 0x4d, 0x28, 0x7d, 0xec, 0xd9, 0x96, 0x6a, 0x1f, 0x7d, 0x4c, 0x7a, 0x4d, + 0xa6, 0xee, 0xb5, 0x74, 0xeb, 0xd3, 0xef, 0x7d, 0x0a, 0x6c, 0x65, 0x14, 0x20, 0x74, 0x6c, 0x84, + 0x24, 0xa0, 0x23, 0x55, 0x73, 0x5d, 0x6d, 0x14, 0xe8, 0xbf, 0x31, 0x87, 0x89, 0x44, 0x70, 0xad, + 0x8c, 0x22, 0x12, 0x2a, 0x3a, 0x40, 0xdf, 0x01, 0xd1, 0x71, 0xcd, 0xbe, 0xe9, 0x9b, 0x51, 0x37, + 0x3e, 0x8b, 0xc3, 0x41, 0x88, 0x23, 0x1c, 0x22, 0x22, 0x74, 0x07, 0x04, 0x1f, 0xbf, 0x08, 0x53, + 0xe0, 0xf2, 0x0c, 0x62, 0x92, 0x8b, 0xa4, 0xc9, 0x26, 0x50, 0xf4, 0x1e, 0x29, 0x1f, 0x03, 0xcb, + 0xc7, 0x6e, 0x50, 0x20, 0xd6, 0x67, 0x50, 0x35, 0x18, 0xaa, 0x95, 0x51, 0x42, 0x82, 0xea, 0x1f, + 0x38, 0x80, 0xb1, 0x41, 0xd0, 0x26, 0xe4, 0x2c, 0xdb, 0xc0, 0x5e, 0x85, 0xa3, 0xe9, 0x8c, 0x62, + 0x8c, 0x94, 0x56, 0x97, 0x64, 0xbf, 0xc2, 0x00, 0x4b, 0xf6, 0x0a, 0xf1, 0x00, 0xcb, 0x2e, 0x11, + 0x60, 0xc2, 0x62, 0x01, 0x56, 0xfd, 0x3d, 0x07, 0x62, 0xe4, 0xa2, 0xb9, 0x5a, 0x3d, 0x90, 0xbe, + 0x38, 0x5a, 0xfd, 0x95, 0x03, 0x31, 0x0a, 0x9b, 0x28, 0x89, 0xb8, 0xc5, 0x93, 0x88, 0x8f, 0x25, + 0xd1, 0x92, 0x9d, 0x6a, 0x5c, 0x57, 0x61, 0x09, 0x5d, 0x73, 0x0b, 0xea, 0xfa, 0x1b, 0x0e, 0x04, + 0x12, 0xe5, 0xe8, 0x9d, 0x49, 0xe7, 0xad, 0xa5, 0x54, 0xa4, 0x2f, 0x86, 0xf7, 0xfe, 0xc2, 0x41, + 0x21, 0xc8, 0xc0, 0xff, 0x6f, 0xdf, 0x45, 0xa5, 0xe9, 0x11, 0x14, 0x82, 0x53, 0x23, 0xa5, 0x2a, + 0xdf, 0x86, 0x02, 0x66, 0xe7, 0x52, 0x4a, 0x57, 0x12, 0x3b, 0xb5, 0x94, 0x10, 0x56, 0xd3, 0xa1, + 0x10, 0xa4, 0x2b, 0xba, 0x01, 0x82, 0x45, 0x4e, 0x49, 0x76, 0xd2, 0xa7, 0x25, 0x34, 0x5d, 0x5f, + 0x62, 0x93, 0xa7, 0xb0, 0x12, 0x86, 0x15, 0xe9, 0x2c, 0xc6, 0xf6, 0xe7, 0x62, 0xcd, 0x03, 0xb1, + 0xcb, 0xc0, 0x31, 0x16, 0x8b, 0xb4, 0x00, 0x28, 0xf9, 0xb5, 0xdf, 0xf1, 0x50, 0x0c, 0x99, 0xa3, + 0x2f, 0xc7, 0xae, 0xc6, 0xce, 0xa7, 0x04, 0x75, 0x70, 0x39, 0x96, 0xda, 0xbc, 0x2c, 0x59, 0x76, + 0xef, 0x41, 0xc9, 0xb4, 0x3c, 0x95, 0xfe, 0xde, 0x06, 0xd7, 0x55, 0x33, 0xf7, 0x16, 0x4d, 0xcb, + 0x3b, 0x70, 0xf1, 0x70, 0xd7, 0x40, 0x8d, 0x89, 0x46, 0x2f, 0x47, 0xd3, 0xf0, 0x7a, 0x0a, 0xd5, + 0xdc, 0xdf, 0x88, 0xc7, 0x8b, 0x74, 0x6a, 0x5f, 0x9b, 0xec, 0x9f, 0x2e, 0xa6, 0x6c, 0x42, 0x98, + 0xc4, 0x5a, 0xb8, 0xda, 0x53, 0x80, 0xb1, 0xd4, 0x4b, 0xb6, 0x3d, 0x17, 0x20, 0x6f, 0x3f, 0x7b, + 0xe6, 0x61, 0xe6, 0xc9, 0x9c, 0x12, 0x8c, 0x6a, 0x7d, 0x10, 0x0e, 0x3d, 0xec, 0xa2, 0x33, 0x91, + 0xab, 0x44, 0xea, 0x93, 0x2a, 0x14, 0x07, 0x1e, 0x76, 0x2d, 0xad, 0x1f, 0xba, 0x25, 0x1a, 0xa3, + 0x6f, 0xa5, 0x64, 0x66, 0xb5, 0xce, 0x6e, 0x89, 0xeb, 0xe1, 0x2d, 0x31, 0x95, 0x83, 0x5e, 0x23, + 0xc7, 0xc4, 0xa8, 0xfd, 0x9b, 0x87, 0xc2, 0x81, 0x6b, 0xd3, 0x42, 0x9c, 0xdc, 0x12, 0x81, 0x10, + 0xdb, 0x8e, 0x7e, 0xa3, 0xab, 0x00, 0xce, 0xe0, 0xa8, 0x67, 0xea, 0xf4, 0x2a, 0x39, 0x4b, 0x57, + 0x44, 0x36, 0xf3, 0x21, 0x1e, 0x91, 0x65, 0x0f, 0xeb, 0x2e, 0x66, 0x37, 0xcd, 0x02, 0x5b, 0x66, + 0x33, 0x64, 0x79, 0x13, 0xca, 0xda, 0xc0, 0x3f, 0x56, 0x3f, 0xc5, 0x47, 0xc7, 0xb6, 0x7d, 0xa2, + 0x0e, 0xdc, 0x5e, 0xf0, 0xf7, 0x79, 0x86, 0xcc, 0x7f, 0xc4, 0xa6, 0x0f, 0xdd, 0x1e, 0xba, 0x0d, + 0xe7, 0x26, 0x90, 0x7d, 0xec, 0x1f, 0xdb, 0x86, 0x57, 0xc9, 0x6f, 0x64, 0x37, 0x45, 0x05, 0xc5, + 0xd0, 0x8f, 0xd8, 0x0a, 0xfa, 0x36, 0x5c, 0x0e, 0x2e, 0x5d, 0x0d, 0xac, 0xe9, 0xbe, 0x39, 0xd4, + 0x7c, 0xac, 0xfa, 0xc7, 0x2e, 0xf6, 0x8e, 0xed, 0x9e, 0x41, 0x6f, 0xf5, 0xb2, 0xca, 0x25, 0x06, + 0x69, 0x46, 0x88, 0x6e, 0x08, 0x48, 0x18, 0xb1, 0xf8, 0x1a, 0x46, 0x24, 0xa4, 0xb1, 0xcc, 0x14, + 0x5f, 0x4d, 0x3a, 0x4e, 0xcf, 0xbf, 0xf1, 0x70, 0xe1, 0x90, 0x8c, 0xb4, 0xa3, 0x1e, 0x0e, 0x1c, + 0xf1, 0x81, 0x89, 0x7b, 0x86, 0x87, 0x6e, 0x07, 0xe6, 0xe7, 0x82, 0xbe, 0x3e, 0xc9, 0xaf, 0xe3, + 0xbb, 0xa6, 0xf5, 0x9c, 0x9e, 0xdd, 0x81, 0x73, 0x3e, 0x48, 0x31, 0x2f, 0xbf, 0x00, 0x75, 0xd2, + 0xf8, 0xcf, 0x66, 0x18, 0x9f, 0x45, 0xd6, 0xdd, 0x58, 0x6c, 0xa7, 0x8b, 0x5e, 0x97, 0xa6, 0xdc, + 0xb3, 0x8c, 0xcb, 0x84, 0x57, 0xb8, 0xac, 0x5a, 0x07, 0x34, 0xbd, 0x13, 0xbb, 0xda, 0x67, 0x02, + 0x73, 0x34, 0x5a, 0xc2, 0x61, 0xed, 0x07, 0x3c, 0xac, 0x36, 0x83, 0x67, 0x8f, 0xce, 0xa0, 0xdf, + 0xd7, 0xdc, 0xd1, 0x54, 0xd0, 0x4f, 0x5f, 0xa5, 0x26, 0x5f, 0x39, 0xc4, 0xd8, 0x2b, 0xc7, 0x64, + 0xd0, 0x08, 0xaf, 0x13, 0x34, 0xf7, 0xa1, 0xa4, 0xe9, 0x3a, 0xf6, 0xbc, 0x78, 0x9d, 0x9b, 0x47, + 0x0b, 0x21, 0x7c, 0x2a, 0xe2, 0xf2, 0xaf, 0x13, 0x71, 0x3f, 0xe6, 0xa0, 0x78, 0xe0, 0x62, 0x0f, + 0x5b, 0x3a, 0xad, 0xf4, 0x7a, 0xcf, 0xd6, 0x4f, 0xa8, 0x01, 0x72, 0x0a, 0x1b, 0x90, 0xff, 0x01, + 0xe2, 0xd6, 0x0a, 0x4f, 0x8f, 0xdd, 0xf8, 0x95, 0x76, 0x48, 0x58, 0x6f, 0x6a, 0xbe, 0xc6, 0x0e, + 0x5c, 0x0a, 0xad, 0x7e, 0x03, 0xc4, 0x68, 0xea, 0x75, 0x7e, 0x87, 0x6b, 0xbb, 0x90, 0x6f, 0x50, + 0x07, 0xc7, 0x3c, 0xb1, 0x42, 0x3d, 0xb1, 0x05, 0x45, 0x27, 0xd8, 0x2e, 0x88, 0xe2, 0xb5, 0x14, + 0x49, 0x94, 0x08, 0x54, 0x7b, 0x17, 0x0a, 0x8c, 0x95, 0x47, 0x5f, 0x9f, 0xd8, 0x67, 0xd0, 0xc2, + 0x4d, 0xbc, 0x3e, 0xd1, 0x15, 0x25, 0x44, 0xd4, 0xda, 0x00, 0xe3, 0x07, 0xae, 0xc4, 0xeb, 0x0c, + 0x97, 0xf6, 0x3a, 0x33, 0xf9, 0xbe, 0xc3, 0x27, 0xde, 0x77, 0x6a, 0x3f, 0xe4, 0xa0, 0x14, 0xbb, + 0xb8, 0x38, 0xdd, 0x02, 0x81, 0xbe, 0x02, 0xab, 0x2e, 0xee, 0x69, 0xa4, 0x1d, 0x57, 0x03, 0x40, + 0x96, 0x02, 0xce, 0x84, 0xd3, 0xfb, 0xac, 0x92, 0xe8, 0x00, 0x63, 0xce, 0xf1, 0x17, 0x25, 0x6e, + 0xfa, 0x45, 0xe9, 0x0a, 0x88, 0x06, 0xee, 0x91, 0x2e, 0x1f, 0xbb, 0xa1, 0x42, 0xd1, 0xc4, 0xc4, + 0x7b, 0x53, 0x76, 0xf2, 0xbd, 0xe9, 0x27, 0x1c, 0x14, 0x9b, 0xb6, 0x2e, 0x0f, 0x89, 0x07, 0x6f, + 0x4d, 0x74, 0x98, 0xf1, 0x4a, 0x1a, 0x42, 0x62, 0x4d, 0xe6, 0x16, 0xb0, 0xba, 0xe1, 0x1d, 0x07, + 0x5b, 0xa6, 0x3a, 0x69, 0x8c, 0x41, 0xd7, 0xe1, 0xad, 0xf8, 0x3b, 0x26, 0x7b, 0x9b, 0x13, 0x95, + 0x95, 0xd8, 0x43, 0xa6, 0x77, 0xf3, 0x17, 0x3c, 0x88, 0x51, 0x3b, 0x8b, 0xd6, 0x60, 0xf5, 0xb1, + 0xb4, 0x77, 0x28, 0xab, 0xdd, 0x27, 0x07, 0xb2, 0xda, 0x3e, 0xdc, 0xdb, 0x2b, 0x67, 0xd0, 0x05, + 0x40, 0xb1, 0xc9, 0x9d, 0xfd, 0xfd, 0x3d, 0x59, 0x6a, 0x97, 0xb9, 0xc4, 0xfc, 0x6e, 0xbb, 0x2b, + 0x3f, 0x90, 0x95, 0x32, 0x9f, 0x60, 0xb2, 0xb7, 0xdf, 0x7e, 0x50, 0xce, 0xa2, 0xf3, 0x70, 0x36, + 0x36, 0xd9, 0xdc, 0x3f, 0xdc, 0xd9, 0x93, 0xcb, 0x42, 0x62, 0xba, 0xd3, 0x55, 0x76, 0xdb, 0x0f, + 0xca, 0x39, 0x74, 0x0e, 0xca, 0xf1, 0x2d, 0x9f, 0x74, 0xe5, 0x4e, 0x39, 0x9f, 0x60, 0xdc, 0x94, + 0xba, 0x72, 0xb9, 0x80, 0xaa, 0x70, 0x21, 0x36, 0x49, 0xda, 0x45, 0x75, 0x7f, 0xe7, 0xa1, 0xdc, + 0xe8, 0x96, 0x8b, 0xe8, 0x12, 0x9c, 0x4f, 0xae, 0x49, 0x8a, 0x22, 0x3d, 0x29, 0x8b, 0x09, 0x5e, + 0x5d, 0xf9, 0xbb, 0xdd, 0x32, 0x24, 0x78, 0x05, 0x1a, 0xa9, 0x8d, 0x76, 0xb7, 0x5c, 0x42, 0x17, + 0x61, 0x2d, 0xa1, 0x15, 0x5d, 0x58, 0xb9, 0xf9, 0x73, 0x0e, 0x56, 0xe2, 0xee, 0x42, 0x5f, 0x82, + 0x8d, 0xe6, 0x7e, 0x43, 0x95, 0x1f, 0xcb, 0xed, 0x6e, 0xa8, 0x6e, 0xe3, 0xf0, 0x91, 0xdc, 0xee, + 0x76, 0xd4, 0x46, 0x4b, 0x6a, 0x3f, 0x90, 0x9b, 0xe5, 0xcc, 0x5c, 0xd4, 0x47, 0x52, 0xb7, 0xd1, + 0x92, 0x9b, 0x65, 0x0e, 0xdd, 0x80, 0xda, 0x4c, 0xd4, 0x61, 0x3b, 0xc4, 0xf1, 0xe8, 0x3a, 0xbc, + 0x9d, 0xc0, 0x1d, 0x28, 0x72, 0x47, 0x6e, 0x37, 0xe4, 0x68, 0xcb, 0xec, 0xce, 0xad, 0x5f, 0xbf, + 0x5c, 0xe7, 0x7e, 0xfb, 0x72, 0x9d, 0xfb, 0xd3, 0xcb, 0x75, 0xee, 0xa7, 0x9f, 0xaf, 0x67, 0xe0, + 0xac, 0x81, 0x87, 0x61, 0x0c, 0x69, 0x8e, 0x59, 0x1f, 0xde, 0x39, 0xe0, 0x9e, 0x0a, 0xf5, 0xfb, + 0xc3, 0x3b, 0x47, 0x79, 0x7a, 0x2a, 0x7e, 0xfd, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x58, 0xfb, + 0xda, 0x75, 0x84, 0x1f, 0x00, 0x00, } func (m *ChangePack) Marshal() (dAtA []byte, err error) { @@ -4911,7 +4929,7 @@ func (m *Project) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintResources(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x42 + dAtA[i] = 0x4a } if m.CreatedAt != nil { { @@ -4923,7 +4941,12 @@ func (m *Project) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintResources(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x3a + dAtA[i] = 0x42 + } + if m.ClientDeactivateThreshold != 0 { + i = encodeVarintResources(dAtA, i, uint64(m.ClientDeactivateThreshold)) + i-- + dAtA[i] = 0x38 } if len(m.AuthWebhookMethods) > 0 { for iNdEx := len(m.AuthWebhookMethods) - 1; iNdEx >= 0; iNdEx-- { @@ -4996,6 +5019,11 @@ func (m *UpdatableProjectFields) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.ClientDeactivateThreshold != 0 { + i = encodeVarintResources(dAtA, i, uint64(m.ClientDeactivateThreshold)) + i-- + dAtA[i] = 0x20 + } if m.AuthWebhookMethods != nil { { size, err := m.AuthWebhookMethods.MarshalToSizedBuffer(dAtA[:i]) @@ -6366,6 +6394,9 @@ func (m *Project) Size() (n int) { n += 1 + l + sovResources(uint64(l)) } } + if m.ClientDeactivateThreshold != 0 { + n += 1 + sovResources(uint64(m.ClientDeactivateThreshold)) + } if m.CreatedAt != nil { l = m.CreatedAt.Size() n += 1 + l + sovResources(uint64(l)) @@ -6398,6 +6429,9 @@ func (m *UpdatableProjectFields) Size() (n int) { l = m.AuthWebhookMethods.Size() n += 1 + l + sovResources(uint64(l)) } + if m.ClientDeactivateThreshold != 0 { + n += 1 + sovResources(uint64(m.ClientDeactivateThreshold)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -11961,6 +11995,25 @@ func (m *Project) Unmarshal(dAtA []byte) error { m.AuthWebhookMethods = append(m.AuthWebhookMethods, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientDeactivateThreshold", wireType) + } + m.ClientDeactivateThreshold = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ClientDeactivateThreshold |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CreatedAt", wireType) } @@ -11996,7 +12049,7 @@ func (m *Project) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 8: + case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field UpdatedAt", wireType) } @@ -12191,6 +12244,25 @@ func (m *UpdatableProjectFields) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientDeactivateThreshold", wireType) + } + m.ClientDeactivateThreshold = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ClientDeactivateThreshold |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipResources(dAtA[iNdEx:]) diff --git a/api/yorkie/v1/resources.proto b/api/yorkie/v1/resources.proto index 4745b8354..f9c71b9a5 100644 --- a/api/yorkie/v1/resources.proto +++ b/api/yorkie/v1/resources.proto @@ -214,8 +214,9 @@ message Project { string secret_key = 4; string auth_webhook_url = 5; repeated string auth_webhook_methods = 6; - google.protobuf.Timestamp created_at = 7; - google.protobuf.Timestamp updated_at = 8; + int64 client_deactivate_threshold = 7; + google.protobuf.Timestamp created_at = 8; + google.protobuf.Timestamp updated_at = 9; } message UpdatableProjectFields { @@ -226,6 +227,7 @@ message UpdatableProjectFields { google.protobuf.StringValue name = 1; google.protobuf.StringValue auth_webhook_url = 2; AuthWebhookMethods auth_webhook_methods = 3; + int64 client_deactivate_threshold = 4; } message DocumentSummary { diff --git a/cmd/yorkie/server.go b/cmd/yorkie/server.go index c1d8159bc..fa3ffe6ec 100644 --- a/cmd/yorkie/server.go +++ b/cmd/yorkie/server.go @@ -39,9 +39,9 @@ var ( flagConfPath string flagLogLevel string - adminTokenDuration time.Duration - housekeepingInterval time.Duration - housekeepingDeactivateThreshold time.Duration + adminTokenDuration time.Duration + housekeepingInterval time.Duration + clientDeactivateThreshold time.Duration mongoConnectionURI string mongoConnectionTimeout time.Duration @@ -67,12 +67,14 @@ func newServerCmd() *cobra.Command { Short: "Start Yorkie server", RunE: func(cmd *cobra.Command, args []string) error { conf.Backend.AdminTokenDuration = adminTokenDuration.String() + + conf.Backend.ClientDeactivateThreshold = clientDeactivateThreshold.String() + conf.Backend.AuthWebhookMaxWaitInterval = authWebhookMaxWaitInterval.String() conf.Backend.AuthWebhookCacheAuthTTL = authWebhookCacheAuthTTL.String() conf.Backend.AuthWebhookCacheUnauthTTL = authWebhookCacheUnauthTTL.String() conf.Housekeeping.Interval = housekeepingInterval.String() - conf.Housekeeping.DeactivateThreshold = housekeepingDeactivateThreshold.String() if mongoConnectionURI != "" { conf.Mongo = &mongo.Config{ @@ -224,17 +226,11 @@ func init() { server.DefaultHousekeepingInterval, "housekeeping interval between housekeeping runs", ) - cmd.Flags().DurationVar( - &housekeepingDeactivateThreshold, - "housekeeping-deactivate-threshold", - server.DefaultHousekeepingDeactivateThreshold, - "time after which clients are considered deactivate", - ) cmd.Flags().IntVar( - &conf.Housekeeping.CandidatesLimit, - "housekeeping-candidates-limit", - server.DefaultHousekeepingCandidateLimit, - "candidates limit for a single housekeeping run", + &conf.Housekeeping.CandidatesLimitPerProject, + "housekeeping-candidates-limit-per-project", + server.DefaultHousekeepingCandidatesLimitPerProject, + "candidates limit per project for a single housekeeping run", ) cmd.Flags().StringVar( &mongoConnectionURI, @@ -321,6 +317,12 @@ func init() { "Whether to use the default project. Even if public key is not provided from the client, "+ "the default project will be used for the request.", ) + cmd.Flags().DurationVar( + &clientDeactivateThreshold, + "client-deactivate-threshold", + server.DefaultClientDeactivateThreshold, + "Deactivate threshold of clients in specific project for housekeeping.", + ) cmd.Flags().Int64Var( &conf.Backend.SnapshotThreshold, "backend-snapshot-threshold", diff --git a/server/backend/backend.go b/server/backend/backend.go index 977708188..c49dc2264 100644 --- a/server/backend/backend.go +++ b/server/backend/backend.go @@ -137,6 +137,7 @@ func New( context.Background(), conf.AdminUser, conf.AdminPassword, + conf.ParseClientDeactivateThreshold(), ) if err != nil { return nil, err diff --git a/server/backend/config.go b/server/backend/config.go index 18d75b3c6..c436c7d24 100644 --- a/server/backend/config.go +++ b/server/backend/config.go @@ -41,6 +41,9 @@ type Config struct { // we are using server as single-tenant mode, this should be set to true. UseDefaultProject bool `yaml:"UseDefaultProject"` + // ClientDeactivateThreshold is deactivate threshold of clients in specific project for housekeeping. + ClientDeactivateThreshold string `yaml:"ClientDeactivateThreshold"` + // SnapshotThreshold is the threshold that determines if changes should be // sent with snapshot when the number of changes is greater than this value. SnapshotThreshold int64 `yaml:"SnapshotThreshold"` @@ -69,6 +72,14 @@ type Config struct { // Validate validates this config. func (c *Config) Validate() error { + if _, err := time.ParseDuration(c.ClientDeactivateThreshold); err != nil { + return fmt.Errorf( + `invalid argument "%s" for "--client-deactivate-threshold" flag: %w`, + c.ClientDeactivateThreshold, + err, + ) + } + if _, err := time.ParseDuration(c.AuthWebhookMaxWaitInterval); err != nil { return fmt.Errorf( `invalid argument "%s" for "--auth-webhook-max-wait-interval" flag: %w`, @@ -96,6 +107,16 @@ func (c *Config) Validate() error { return nil } +// ParseClientDeactivateThreshold returns client deactivate threshold. +func (c *Config) ParseClientDeactivateThreshold() time.Duration { + result, err := time.ParseDuration(c.ClientDeactivateThreshold) + if err != nil { + panic(err) + } + + return result +} + // ParseAdminTokenDuration returns admin token duration. func (c *Config) ParseAdminTokenDuration() time.Duration { result, err := time.ParseDuration(c.AdminTokenDuration) diff --git a/server/backend/config_test.go b/server/backend/config_test.go index 1316ff4ec..63830a984 100644 --- a/server/backend/config_test.go +++ b/server/backend/config_test.go @@ -27,12 +27,17 @@ import ( func TestConfig(t *testing.T) { t.Run("validate test", func(t *testing.T) { validConf := backend.Config{ + ClientDeactivateThreshold: "1h", AuthWebhookMaxWaitInterval: "0ms", AuthWebhookCacheAuthTTL: "10s", AuthWebhookCacheUnauthTTL: "10s", } assert.NoError(t, validConf.Validate()) + conf1 := validConf + conf1.ClientDeactivateThreshold = "5" + assert.Error(t, conf1.Validate()) + conf2 := validConf conf2.AuthWebhookMaxWaitInterval = "5" assert.Error(t, conf2.Validate()) diff --git a/server/backend/database/database.go b/server/backend/database/database.go index 08f388729..543276497 100644 --- a/server/backend/database/database.go +++ b/server/backend/database/database.go @@ -84,6 +84,7 @@ type Database interface { ctx context.Context, username, password string, + clientDeactivateThreshold gotime.Duration, ) (*UserInfo, *ProjectInfo, error) // CreateProjectInfo creates a new project. @@ -91,9 +92,10 @@ type Database interface { ctx context.Context, name string, owner types.ID, + clientDeactivateThreshold gotime.Duration, ) (*ProjectInfo, error) - // ListProjectInfos returns all projects. + // ListProjectInfos returns all project infos owned by owner. ListProjectInfos(ctx context.Context, owner types.ID) ([]*ProjectInfo, error) // UpdateProjectInfo updates the project. @@ -133,8 +135,7 @@ type Database interface { // FindDeactivateCandidates finds the housekeeping candidates. FindDeactivateCandidates( ctx context.Context, - deactivateThreshold gotime.Duration, - candidatesLimit int, + candidatesLimitPerProject int, ) ([]*ClientInfo, error) // FindDocInfoByKey finds the document of the given key. diff --git a/server/backend/database/memory/database.go b/server/backend/database/memory/database.go index bc902e79a..034b55688 100644 --- a/server/backend/database/memory/database.go +++ b/server/backend/database/memory/database.go @@ -117,13 +117,14 @@ func (d *DB) EnsureDefaultUserAndProject( ctx context.Context, username, password string, + clientDeactivateThreshold gotime.Duration, ) (*database.UserInfo, *database.ProjectInfo, error) { user, err := d.ensureDefaultUserInfo(ctx, username, password) if err != nil { return nil, nil, err } - project, err := d.ensureDefaultProjectInfo(ctx, user.ID) + project, err := d.ensureDefaultProjectInfo(ctx, user.ID, clientDeactivateThreshold) if err != nil { return nil, nil, err } @@ -168,6 +169,7 @@ func (d *DB) ensureDefaultUserInfo( func (d *DB) ensureDefaultProjectInfo( ctx context.Context, defaultUserID types.ID, + defaultClientDeactivateThreshold gotime.Duration, ) (*database.ProjectInfo, error) { txn := d.db.Txn(true) defer txn.Abort() @@ -179,7 +181,7 @@ func (d *DB) ensureDefaultProjectInfo( var info *database.ProjectInfo if raw == nil { - info = database.NewProjectInfo(database.DefaultProjectName, defaultUserID) + info = database.NewProjectInfo(database.DefaultProjectName, defaultUserID, defaultClientDeactivateThreshold) info.ID = database.DefaultProjectID if err := txn.Insert(tblProjects, info); err != nil { return nil, fmt.Errorf("insert project: %w", err) @@ -197,6 +199,7 @@ func (d *DB) CreateProjectInfo( ctx context.Context, name string, owner types.ID, + clientDeactivateThreshold gotime.Duration, ) (*database.ProjectInfo, error) { txn := d.db.Txn(true) defer txn.Abort() @@ -211,7 +214,7 @@ func (d *DB) CreateProjectInfo( return nil, fmt.Errorf("%s: %w", name, database.ErrProjectAlreadyExists) } - info := database.NewProjectInfo(name, owner) + info := database.NewProjectInfo(name, owner, clientDeactivateThreshold) info.ID = newID() if err := txn.Insert(tblProjects, info); err != nil { return nil, fmt.Errorf("insert project: %w", err) @@ -221,7 +224,34 @@ func (d *DB) CreateProjectInfo( return info, nil } -// ListProjectInfos returns all projects. +// ListAllProjectInfos returns all project infos. +func (d *DB) listAllProjectInfos( + ctx context.Context, +) ([]*database.ProjectInfo, error) { + txn := d.db.Txn(false) + defer txn.Abort() + + // TODO(krapie): txn.Get() loads all projects in memory, + // which will cause performance issue as number of projects in DB grows. + // Therefore, pagination of projects is needed to avoid this issue. + iter, err := txn.Get( + tblProjects, + "id", + ) + if err != nil { + return nil, fmt.Errorf("fetch all projects: %w", err) + } + + var infos []*database.ProjectInfo + for raw := iter.Next(); raw != nil; raw = iter.Next() { + info := raw.(*database.ProjectInfo).DeepCopy() + infos = append(infos, info) + } + + return infos, nil +} + +// ListProjectInfos returns all project infos owned by owner. func (d *DB) ListProjectInfos( ctx context.Context, owner types.ID, @@ -524,21 +554,22 @@ func (d *DB) UpdateClientInfoAfterPushPull( return nil } -// FindDeactivateCandidates finds the clients that need housekeeping. -func (d *DB) FindDeactivateCandidates( +// findDeactivateCandidatesPerProject finds the clients that need housekeeping per project. +func (d *DB) findDeactivateCandidatesPerProject( ctx context.Context, - inactiveThreshold gotime.Duration, + project *database.ProjectInfo, candidatesLimit int, ) ([]*database.ClientInfo, error) { txn := d.db.Txn(false) defer txn.Abort() - offset := gotime.Now().Add(-inactiveThreshold) + offset := gotime.Now().Add(-project.ClientDeactivateThreshold) var infos []*database.ClientInfo iterator, err := txn.ReverseLowerBound( tblClients, - "status_updated_at", + "project_id_status_updated_at", + project.ID.String(), database.ClientActivated, offset, ) @@ -559,6 +590,29 @@ func (d *DB) FindDeactivateCandidates( return infos, nil } +// FindDeactivateCandidates finds the clients that need housekeeping. +func (d *DB) FindDeactivateCandidates( + ctx context.Context, + candidatesLimitPerProject int, +) ([]*database.ClientInfo, error) { + projects, err := d.listAllProjectInfos(ctx) + if err != nil { + return nil, err + } + + var candidates []*database.ClientInfo + for _, project := range projects { + infos, err := d.findDeactivateCandidatesPerProject(ctx, project, candidatesLimitPerProject) + if err != nil { + return nil, err + } + + candidates = append(candidates, infos...) + } + + return candidates, nil +} + // FindDocInfoByKeyAndOwner finds the document of the given key. If the // createDocIfNotExist condition is true, create the document if it does not // exist. diff --git a/server/backend/database/memory/database_test.go b/server/backend/database/memory/database_test.go index c3904eea1..6467155d6 100644 --- a/server/backend/database/memory/database_test.go +++ b/server/backend/database/memory/database_test.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "testing" + gotime "time" "github.com/stretchr/testify/assert" @@ -42,6 +43,7 @@ func TestDB(t *testing.T) { dummyOwnerID := types.ID("000000000000000000000000") otherOwnerID := types.ID("000000000000000000000001") notExistsID := types.ID("000000000000000000000000") + clientDeactivateThreshold := 1 * gotime.Hour t.Run("activate/deactivate client test", func(t *testing.T) { // try to deactivate the client with not exists ID. @@ -77,11 +79,11 @@ func TestDB(t *testing.T) { t.Run("project test", func(t *testing.T) { suffixes := []int{0, 1, 2} for _, suffix := range suffixes { - _, err = db.CreateProjectInfo(ctx, fmt.Sprintf("%s-%d", t.Name(), suffix), dummyOwnerID) + _, err = db.CreateProjectInfo(ctx, fmt.Sprintf("%s-%d", t.Name(), suffix), dummyOwnerID, clientDeactivateThreshold) assert.NoError(t, err) } - _, err = db.CreateProjectInfo(ctx, t.Name(), otherOwnerID) + _, err = db.CreateProjectInfo(ctx, t.Name(), otherOwnerID, clientDeactivateThreshold) assert.NoError(t, err) // Lists all projects that the dummyOwnerID is the owner. @@ -89,7 +91,7 @@ func TestDB(t *testing.T) { assert.NoError(t, err) assert.Len(t, projects, len(suffixes)) - _, err = db.CreateProjectInfo(ctx, t.Name(), dummyOwnerID) + _, err = db.CreateProjectInfo(ctx, t.Name(), dummyOwnerID, clientDeactivateThreshold) assert.NoError(t, err) project, err := db.FindProjectInfoByName(ctx, dummyOwnerID, t.Name()) @@ -344,7 +346,7 @@ func TestDB(t *testing.T) { }) t.Run("FindProjectInfoByID test", func(t *testing.T) { - info, err := db.CreateProjectInfo(ctx, t.Name(), dummyOwnerID) + info, err := db.CreateProjectInfo(ctx, t.Name(), dummyOwnerID, clientDeactivateThreshold) assert.NoError(t, err) _, err = db.FindProjectInfoByName(ctx, dummyOwnerID, info.Name) @@ -363,19 +365,21 @@ func TestDB(t *testing.T) { string(types.AttachDocument), string(types.WatchDocuments), } + newClientDeactivateThreshold := 1 * gotime.Hour - info, err := db.CreateProjectInfo(ctx, t.Name(), dummyOwnerID) + info, err := db.CreateProjectInfo(ctx, t.Name(), dummyOwnerID, clientDeactivateThreshold) assert.NoError(t, err) - _, err = db.CreateProjectInfo(ctx, existName, dummyOwnerID) + _, err = db.CreateProjectInfo(ctx, existName, dummyOwnerID, clientDeactivateThreshold) assert.NoError(t, err) id := info.ID // 01. Update all fields test fields := &types.UpdatableProjectFields{ - Name: &newName, - AuthWebhookURL: &newAuthWebhookURL, - AuthWebhookMethods: &newAuthWebhookMethods, + Name: &newName, + AuthWebhookURL: &newAuthWebhookURL, + AuthWebhookMethods: &newAuthWebhookMethods, + ClientDeactivateThreshold: newClientDeactivateThreshold, } assert.NoError(t, fields.Validate()) res, err := db.UpdateProjectInfo(ctx, dummyOwnerID, id, fields) @@ -386,6 +390,7 @@ func TestDB(t *testing.T) { assert.Equal(t, newName, updateInfo.Name) assert.Equal(t, newAuthWebhookURL, updateInfo.AuthWebhookURL) assert.Equal(t, newAuthWebhookMethods, updateInfo.AuthWebhookMethods) + assert.Equal(t, newClientDeactivateThreshold, updateInfo.ClientDeactivateThreshold) // 02. Update name field test fields = &types.UpdatableProjectFields{ @@ -400,6 +405,7 @@ func TestDB(t *testing.T) { assert.NotEqual(t, newName, updateInfo.Name) assert.Equal(t, newAuthWebhookURL, updateInfo.AuthWebhookURL) assert.Equal(t, newAuthWebhookMethods, updateInfo.AuthWebhookMethods) + assert.Equal(t, newClientDeactivateThreshold, updateInfo.ClientDeactivateThreshold) // 03. Update authWebhookURL test newAuthWebhookURL2 := newAuthWebhookURL + "2" @@ -415,13 +421,30 @@ func TestDB(t *testing.T) { assert.Equal(t, newName2, updateInfo.Name) assert.NotEqual(t, newAuthWebhookURL, updateInfo.AuthWebhookURL) assert.Equal(t, newAuthWebhookMethods, updateInfo.AuthWebhookMethods) + assert.Equal(t, newClientDeactivateThreshold, updateInfo.ClientDeactivateThreshold) - // 04. Duplicated name test + // 04. Update clientDeactivateThreshold test + clientDeactivateThreshold2 := clientDeactivateThreshold + 1*gotime.Hour + fields = &types.UpdatableProjectFields{ + ClientDeactivateThreshold: clientDeactivateThreshold2, + } + assert.NoError(t, fields.Validate()) + res, err = db.UpdateProjectInfo(ctx, dummyOwnerID, id, fields) + assert.NoError(t, err) + updateInfo, err = db.FindProjectInfoByID(ctx, id) + assert.NoError(t, err) + assert.Equal(t, res, updateInfo) + assert.Equal(t, newName2, updateInfo.Name) + assert.Equal(t, newAuthWebhookURL2, updateInfo.AuthWebhookURL) + assert.Equal(t, newAuthWebhookMethods, updateInfo.AuthWebhookMethods) + assert.NotEqual(t, newClientDeactivateThreshold, updateInfo.ClientDeactivateThreshold) + + // 05. Duplicated name test fields = &types.UpdatableProjectFields{Name: &existName} _, err = db.UpdateProjectInfo(ctx, dummyOwnerID, id, fields) assert.ErrorIs(t, err, database.ErrProjectNameAlreadyExists) - // 05. OwnerID not match test + // 06. OwnerID not match test fields = &types.UpdatableProjectFields{Name: &existName} _, err = db.UpdateProjectInfo(ctx, otherOwnerID, id, fields) assert.ErrorIs(t, err, database.ErrProjectNotFound) diff --git a/server/backend/database/memory/housekeeping_test.go b/server/backend/database/memory/housekeeping_test.go index 5589b0d0f..178543f69 100644 --- a/server/backend/database/memory/housekeeping_test.go +++ b/server/backend/database/memory/housekeeping_test.go @@ -28,38 +28,39 @@ import ( "github.com/stretchr/testify/assert" monkey "github.com/undefinedlabs/go-mpatch" - "github.com/yorkie-team/yorkie/server/backend/database" "github.com/yorkie-team/yorkie/server/backend/database/memory" ) func TestHousekeeping(t *testing.T) { memdb, err := memory.New() assert.NoError(t, err) - projectID := database.DefaultProjectID t.Run("housekeeping test", func(t *testing.T) { ctx := context.Background() + clientDeactivateThreshold := 23 * gotime.Hour + _, project, err := memdb.EnsureDefaultUserAndProject(ctx, "test", "test", clientDeactivateThreshold) + assert.NoError(t, err) + yesterday := gotime.Now().Add(-24 * gotime.Hour) patch, err := monkey.PatchMethod(gotime.Now, func() gotime.Time { return yesterday }) if err != nil { log.Fatal(err) } - clientA, err := memdb.ActivateClient(ctx, projectID, fmt.Sprintf("%s-A", t.Name())) + clientA, err := memdb.ActivateClient(ctx, project.ID, fmt.Sprintf("%s-A", t.Name())) assert.NoError(t, err) - clientB, err := memdb.ActivateClient(ctx, projectID, fmt.Sprintf("%s-B", t.Name())) + clientB, err := memdb.ActivateClient(ctx, project.ID, fmt.Sprintf("%s-B", t.Name())) assert.NoError(t, err) err = patch.Unpatch() if err != nil { log.Fatal(err) } - clientC, err := memdb.ActivateClient(ctx, projectID, fmt.Sprintf("%s-C", t.Name())) + clientC, err := memdb.ActivateClient(ctx, project.ID, fmt.Sprintf("%s-C", t.Name())) assert.NoError(t, err) candidates, err := memdb.FindDeactivateCandidates( ctx, - gotime.Hour, 10, ) assert.NoError(t, err) diff --git a/server/backend/database/memory/indexes.go b/server/backend/database/memory/indexes.go index e357a5f30..497dcb945 100644 --- a/server/backend/database/memory/indexes.go +++ b/server/backend/database/memory/indexes.go @@ -93,10 +93,11 @@ var schema = &memdb.DBSchema{ }, }, }, - "status_updated_at": { - Name: "status_updated_at", + "project_id_status_updated_at": { + Name: "project_id_status_updated_at", Indexer: &memdb.CompoundIndex{ Indexes: []memdb.Indexer{ + &memdb.StringFieldIndex{Field: "ProjectID"}, &memdb.StringFieldIndex{Field: "Status"}, &memdb.TimeFieldIndex{Field: "UpdatedAt"}, }, diff --git a/server/backend/database/mongo/client.go b/server/backend/database/mongo/client.go index 96827632b..095b19dac 100644 --- a/server/backend/database/mongo/client.go +++ b/server/backend/database/mongo/client.go @@ -95,13 +95,14 @@ func (c *Client) EnsureDefaultUserAndProject( ctx context.Context, username, password string, + clientDeactivateThreshold gotime.Duration, ) (*database.UserInfo, *database.ProjectInfo, error) { userInfo, err := c.ensureDefaultUserInfo(ctx, username, password) if err != nil { return nil, nil, err } - projectInfo, err := c.ensureDefaultProjectInfo(ctx, userInfo.ID) + projectInfo, err := c.ensureDefaultProjectInfo(ctx, userInfo.ID, clientDeactivateThreshold) if err != nil { return nil, nil, err } @@ -157,8 +158,9 @@ func (c *Client) ensureDefaultUserInfo( func (c *Client) ensureDefaultProjectInfo( ctx context.Context, defaultUserID types.ID, + defaultClientDeactivateThreshold gotime.Duration, ) (*database.ProjectInfo, error) { - candidate := database.NewProjectInfo(database.DefaultProjectName, defaultUserID) + candidate := database.NewProjectInfo(database.DefaultProjectName, defaultUserID, defaultClientDeactivateThreshold) candidate.ID = database.DefaultProjectID encodedID, err := encodeID(candidate.ID) if err != nil { @@ -173,11 +175,12 @@ func (c *Client) ensureDefaultProjectInfo( "_id": encodedID, }, bson.M{ "$setOnInsert": bson.M{ - "name": candidate.Name, - "owner": encodedDefaultUserID, - "public_key": candidate.PublicKey, - "secret_key": candidate.SecretKey, - "created_at": candidate.CreatedAt, + "name": candidate.Name, + "owner": encodedDefaultUserID, + "client_deactivate_threshold": candidate.ClientDeactivateThreshold, + "public_key": candidate.PublicKey, + "secret_key": candidate.SecretKey, + "created_at": candidate.CreatedAt, }, }, options.Update().SetUpsert(true)) if err != nil { @@ -204,19 +207,21 @@ func (c *Client) CreateProjectInfo( ctx context.Context, name string, owner types.ID, + clientDeactivateThreshold gotime.Duration, ) (*database.ProjectInfo, error) { encodedOwner, err := encodeID(owner) if err != nil { return nil, err } - info := database.NewProjectInfo(name, owner) + info := database.NewProjectInfo(name, owner, clientDeactivateThreshold) result, err := c.collection(colProjects).InsertOne(ctx, bson.M{ - "name": info.Name, - "owner": encodedOwner, - "public_key": info.PublicKey, - "secret_key": info.SecretKey, - "created_at": info.CreatedAt, + "name": info.Name, + "owner": encodedOwner, + "client_deactivate_threshold": info.ClientDeactivateThreshold, + "public_key": info.PublicKey, + "secret_key": info.SecretKey, + "created_at": info.CreatedAt, }) if err != nil { if mongo.IsDuplicateKeyError(err) { @@ -231,7 +236,28 @@ func (c *Client) CreateProjectInfo( return info, nil } -// ListProjectInfos returns all project infos. +// ListAllProjectInfos returns all project infos. +func (c *Client) listAllProjectInfos( + ctx context.Context, +) ([]*database.ProjectInfo, error) { + // TODO(krapie): Find(ctx, bson.D{{}}) loads all projects in memory, + // which will cause performance issue as number of projects in DB grows. + // Therefore, pagination of projects is needed to avoid this issue. + cursor, err := c.collection(colProjects).Find(ctx, bson.D{{}}) + if err != nil { + logging.From(ctx).Error(err) + return nil, fmt.Errorf("fetch all project infos: %w", err) + } + + var infos []*database.ProjectInfo + if err := cursor.All(ctx, &infos); err != nil { + return nil, fmt.Errorf("fetch all project infos: %w", err) + } + + return infos, nil +} + +// ListProjectInfos returns all project infos owned by owner. func (c *Client) ListProjectInfos( ctx context.Context, owner types.ID, @@ -590,16 +616,17 @@ func (c *Client) UpdateClientInfoAfterPushPull( return nil } -// FindDeactivateCandidates finds the clients that need housekeeping. -func (c *Client) FindDeactivateCandidates( +// findDeactivateCandidatesPerProject finds the clients that need housekeeping per project. +func (c *Client) findDeactivateCandidatesPerProject( ctx context.Context, - deactivateThreshold gotime.Duration, + project *database.ProjectInfo, candidatesLimit int, ) ([]*database.ClientInfo, error) { cursor, err := c.collection(colClients).Find(ctx, bson.M{ - "status": database.ClientActivated, + "project_id": project.ID, + "status": database.ClientActivated, "updated_at": bson.M{ - "$lte": gotime.Now().Add(-deactivateThreshold), + "$lte": gotime.Now().Add(-project.ClientDeactivateThreshold), }, }, options.Find().SetLimit(int64(candidatesLimit))) @@ -615,6 +642,29 @@ func (c *Client) FindDeactivateCandidates( return clientInfos, nil } +// FindDeactivateCandidates finds the clients that need housekeeping. +func (c *Client) FindDeactivateCandidates( + ctx context.Context, + candidatesLimitPerProject int, +) ([]*database.ClientInfo, error) { + projects, err := c.listAllProjectInfos(ctx) + if err != nil { + return nil, err + } + + var candidates []*database.ClientInfo + for _, project := range projects { + clientInfos, err := c.findDeactivateCandidatesPerProject(ctx, project, candidatesLimitPerProject) + if err != nil { + return nil, err + } + + candidates = append(candidates, clientInfos...) + } + + return candidates, nil +} + // FindDocInfoByKeyAndOwner finds the document of the given key. If the // createDocIfNotExist condition is true, create the document if it does not // exist. diff --git a/server/backend/database/mongo/client_test.go b/server/backend/database/mongo/client_test.go index 7b8772c05..fd37fda1f 100644 --- a/server/backend/database/mongo/client_test.go +++ b/server/backend/database/mongo/client_test.go @@ -19,6 +19,7 @@ package mongo_test import ( "context" "testing" + "time" "github.com/stretchr/testify/assert" @@ -43,12 +44,13 @@ func TestClient(t *testing.T) { dummyOwnerID := types.ID("000000000000000000000000") otherOwnerID := types.ID("000000000000000000000001") + clientDeactivateThreshold := 1 * time.Hour t.Run("UpdateProjectInfo test", func(t *testing.T) { - info, err := cli.CreateProjectInfo(ctx, t.Name(), dummyOwnerID) + info, err := cli.CreateProjectInfo(ctx, t.Name(), dummyOwnerID, clientDeactivateThreshold) assert.NoError(t, err) existName := "already" - _, err = cli.CreateProjectInfo(ctx, existName, dummyOwnerID) + _, err = cli.CreateProjectInfo(ctx, existName, dummyOwnerID, clientDeactivateThreshold) assert.NoError(t, err) id := info.ID @@ -58,12 +60,14 @@ func TestClient(t *testing.T) { string(types.AttachDocument), string(types.WatchDocuments), } + newClientDeactivateThreshold := 2 * time.Hour // update total project_field fields := &types.UpdatableProjectFields{ - Name: &newName, - AuthWebhookURL: &newAuthWebhookURL, - AuthWebhookMethods: &newAuthWebhookMethods, + Name: &newName, + AuthWebhookURL: &newAuthWebhookURL, + AuthWebhookMethods: &newAuthWebhookMethods, + ClientDeactivateThreshold: newClientDeactivateThreshold, } err = fields.Validate() @@ -78,6 +82,7 @@ func TestClient(t *testing.T) { assert.Equal(t, newName, updateInfo.Name) assert.Equal(t, newAuthWebhookURL, updateInfo.AuthWebhookURL) assert.Equal(t, newAuthWebhookMethods, updateInfo.AuthWebhookMethods) + assert.Equal(t, newClientDeactivateThreshold, updateInfo.ClientDeactivateThreshold) // update one field newName2 := newName + "2" @@ -97,6 +102,7 @@ func TestClient(t *testing.T) { assert.NotEqual(t, newName, updateInfo.Name) assert.Equal(t, newAuthWebhookURL, updateInfo.AuthWebhookURL) assert.Equal(t, newAuthWebhookMethods, updateInfo.AuthWebhookMethods) + assert.Equal(t, newClientDeactivateThreshold, updateInfo.ClientDeactivateThreshold) // check duplicate name error fields = &types.UpdatableProjectFields{Name: &existName} @@ -105,9 +111,9 @@ func TestClient(t *testing.T) { }) t.Run("FindProjectInfoByName test", func(t *testing.T) { - info1, err := cli.CreateProjectInfo(ctx, t.Name(), dummyOwnerID) + info1, err := cli.CreateProjectInfo(ctx, t.Name(), dummyOwnerID, clientDeactivateThreshold) assert.NoError(t, err) - _, err = cli.CreateProjectInfo(ctx, t.Name(), otherOwnerID) + _, err = cli.CreateProjectInfo(ctx, t.Name(), otherOwnerID, clientDeactivateThreshold) assert.NoError(t, err) info2, err := cli.FindProjectInfoByName(ctx, dummyOwnerID, t.Name()) diff --git a/server/backend/database/project_info.go b/server/backend/database/project_info.go index be0225862..1bc9efae8 100644 --- a/server/backend/database/project_info.go +++ b/server/backend/database/project_info.go @@ -53,6 +53,10 @@ type ProjectInfo struct { // AuthWebhookMethods is the methods that run the authorization webhook. AuthWebhookMethods []string `bson:"auth_webhook_methods"` + // ClientDeactivateThreshold is the time after which clients in + // specific project are considered deactivate for housekeeping. + ClientDeactivateThreshold time.Duration `bson:"client_deactivate_threshold"` + // CreatedAt is the time when the project was created. CreatedAt time.Time `bson:"created_at"` @@ -61,10 +65,11 @@ type ProjectInfo struct { } // NewProjectInfo creates a new ProjectInfo of the given name. -func NewProjectInfo(name string, owner types.ID) *ProjectInfo { +func NewProjectInfo(name string, owner types.ID, clientDeactivateThreshold time.Duration) *ProjectInfo { return &ProjectInfo{ - Name: name, - Owner: owner, + Name: name, + Owner: owner, + ClientDeactivateThreshold: clientDeactivateThreshold, // TODO(hackerwins): Use random generated Key. PublicKey: xid.New().String(), SecretKey: xid.New().String(), @@ -75,15 +80,16 @@ func NewProjectInfo(name string, owner types.ID) *ProjectInfo { // ToProjectInfo converts the given types.Project to ProjectInfo. func ToProjectInfo(project *types.Project) *ProjectInfo { return &ProjectInfo{ - ID: project.ID, - Name: project.Name, - Owner: project.Owner, - PublicKey: project.PublicKey, - SecretKey: project.SecretKey, - AuthWebhookURL: project.AuthWebhookURL, - AuthWebhookMethods: project.AuthWebhookMethods, - CreatedAt: project.CreatedAt, - UpdatedAt: project.UpdatedAt, + ID: project.ID, + Name: project.Name, + Owner: project.Owner, + PublicKey: project.PublicKey, + SecretKey: project.SecretKey, + AuthWebhookURL: project.AuthWebhookURL, + AuthWebhookMethods: project.AuthWebhookMethods, + ClientDeactivateThreshold: project.ClientDeactivateThreshold, + CreatedAt: project.CreatedAt, + UpdatedAt: project.UpdatedAt, } } @@ -94,15 +100,16 @@ func (i *ProjectInfo) DeepCopy() *ProjectInfo { } return &ProjectInfo{ - ID: i.ID, - Name: i.Name, - Owner: i.Owner, - PublicKey: i.PublicKey, - SecretKey: i.SecretKey, - AuthWebhookURL: i.AuthWebhookURL, - AuthWebhookMethods: i.AuthWebhookMethods, - CreatedAt: i.CreatedAt, - UpdatedAt: i.UpdatedAt, + ID: i.ID, + Name: i.Name, + Owner: i.Owner, + PublicKey: i.PublicKey, + SecretKey: i.SecretKey, + AuthWebhookURL: i.AuthWebhookURL, + AuthWebhookMethods: i.AuthWebhookMethods, + ClientDeactivateThreshold: i.ClientDeactivateThreshold, + CreatedAt: i.CreatedAt, + UpdatedAt: i.UpdatedAt, } } @@ -117,19 +124,23 @@ func (i *ProjectInfo) UpdateFields(fields *types.UpdatableProjectFields) { if fields.AuthWebhookMethods != nil { i.AuthWebhookMethods = *fields.AuthWebhookMethods } + if fields.ClientDeactivateThreshold != 0 { + i.ClientDeactivateThreshold = time.Duration(fields.ClientDeactivateThreshold) + } } // ToProject converts the ProjectInfo to the Project. func (i *ProjectInfo) ToProject() *types.Project { return &types.Project{ - ID: i.ID, - Name: i.Name, - Owner: i.Owner, - AuthWebhookURL: i.AuthWebhookURL, - AuthWebhookMethods: i.AuthWebhookMethods, - PublicKey: i.PublicKey, - SecretKey: i.SecretKey, - CreatedAt: i.CreatedAt, - UpdatedAt: i.UpdatedAt, + ID: i.ID, + Name: i.Name, + Owner: i.Owner, + AuthWebhookURL: i.AuthWebhookURL, + AuthWebhookMethods: i.AuthWebhookMethods, + ClientDeactivateThreshold: i.ClientDeactivateThreshold, + PublicKey: i.PublicKey, + SecretKey: i.SecretKey, + CreatedAt: i.CreatedAt, + UpdatedAt: i.UpdatedAt, } } diff --git a/server/backend/database/project_info_test.go b/server/backend/database/project_info_test.go index adc6ab671..8178cab31 100644 --- a/server/backend/database/project_info_test.go +++ b/server/backend/database/project_info_test.go @@ -18,6 +18,7 @@ package database_test import ( "testing" + "time" "github.com/stretchr/testify/assert" @@ -28,11 +29,13 @@ import ( func TestProjectInfo(t *testing.T) { t.Run("update fields test", func(t *testing.T) { dummyOwnerID := types.ID("000000000000000000000000") - project := database.NewProjectInfo(t.Name(), dummyOwnerID) + clientDeactivateThreshold := 1 * time.Hour + project := database.NewProjectInfo(t.Name(), dummyOwnerID, clientDeactivateThreshold) testName := "testName" testURL := "testUrl" testMethods := []string{"testMethod"} + testClientDeactivateThreshold := 2 * time.Hour project.UpdateFields(&types.UpdatableProjectFields{Name: &testName}) assert.Equal(t, testName, project.Name) @@ -43,5 +46,10 @@ func TestProjectInfo(t *testing.T) { project.UpdateFields(&types.UpdatableProjectFields{AuthWebhookMethods: &testMethods}) assert.Equal(t, testMethods, project.AuthWebhookMethods) assert.Equal(t, dummyOwnerID, project.Owner) + + project.UpdateFields(&types.UpdatableProjectFields{ + ClientDeactivateThreshold: testClientDeactivateThreshold, + }) + assert.Equal(t, testClientDeactivateThreshold, project.ClientDeactivateThreshold) }) } diff --git a/server/backend/housekeeping/housekeeping.go b/server/backend/housekeeping/housekeeping.go index ce6bfff2f..d08abcbdc 100644 --- a/server/backend/housekeeping/housekeeping.go +++ b/server/backend/housekeeping/housekeeping.go @@ -39,12 +39,8 @@ type Config struct { // Interval is the time between housekeeping runs. Interval string `yaml:"Interval"` - // deactivateThreshold is the time after which clients are considered - // deactivate. - DeactivateThreshold string `yaml:"DeactivateThreshold"` - - // CandidatesLimit is the maximum number of candidates to be returned. - CandidatesLimit int `yaml:"CandidatesLimit"` + // CandidatesLimitPerProject is the maximum number of candidates to be returned per project. + CandidatesLimitPerProject int `yaml:"CandidatesLimitPerProject"` } // Validate validates the configuration. @@ -57,14 +53,6 @@ func (c *Config) Validate() error { ) } - if _, err := time.ParseDuration(c.DeactivateThreshold); err != nil { - return fmt.Errorf( - `invalid argument %s for "--housekeeping-deactivate-threshold" flag: %w`, - c.Interval, - err, - ) - } - return nil } @@ -75,9 +63,8 @@ type Housekeeping struct { database database.Database coordinator sync.Coordinator - interval time.Duration - deactivateThreshold time.Duration - candidatesLimit int + interval time.Duration + candidatesLimitPerProject int ctx context.Context cancelFunc context.CancelFunc @@ -111,20 +98,14 @@ func New( return nil, fmt.Errorf("parse interval %s: %w", conf.Interval, err) } - deactivateThreshold, err := time.ParseDuration(conf.DeactivateThreshold) - if err != nil { - return nil, fmt.Errorf("parse deactivate threshold %s: %w", conf.DeactivateThreshold, err) - } - ctx, cancelFunc := context.WithCancel(context.Background()) return &Housekeeping{ database: database, coordinator: coordinator, - interval: interval, - deactivateThreshold: deactivateThreshold, - candidatesLimit: conf.CandidatesLimit, + interval: interval, + candidatesLimitPerProject: conf.CandidatesLimitPerProject, ctx: ctx, cancelFunc: cancelFunc, @@ -180,8 +161,7 @@ func (h *Housekeeping) deactivateCandidates(ctx context.Context) error { candidates, err := h.database.FindDeactivateCandidates( ctx, - h.deactivateThreshold, - h.candidatesLimit, + h.candidatesLimitPerProject, ) if err != nil { return err diff --git a/server/config.go b/server/config.go index 8144ee26a..6a2ccf219 100644 --- a/server/config.go +++ b/server/config.go @@ -42,9 +42,8 @@ const ( DefaultAdminPort = 11103 - DefaultHousekeepingInterval = time.Minute - DefaultHousekeepingDeactivateThreshold = 7 * 24 * time.Hour - DefaultHousekeepingCandidateLimit = 500 + DefaultHousekeepingInterval = time.Minute + DefaultHousekeepingCandidatesLimitPerProject = 500 DefaultMongoConnectionURI = "mongodb://localhost:27017" DefaultMongoConnectionTimeout = 5 * time.Second @@ -56,6 +55,7 @@ const ( DefaultSecretKey = "yorkie-secret" DefaultAdminTokenDuration = 7 * 24 * time.Hour DefaultUseDefaultProject = true + DefaultClientDeactivateThreshold = 24 * time.Hour DefaultSnapshotThreshold = 500 DefaultSnapshotInterval = 1000 DefaultSnapshotWithPurgingChanges = false @@ -179,6 +179,10 @@ func (c *Config) ensureDefaultValue() { c.Backend.AdminTokenDuration = DefaultAdminTokenDuration.String() } + if c.Backend.ClientDeactivateThreshold == "" { + c.Backend.ClientDeactivateThreshold = DefaultClientDeactivateThreshold.String() + } + if c.Backend.SnapshotThreshold == 0 { c.Backend.SnapshotThreshold = DefaultSnapshotThreshold } @@ -248,11 +252,11 @@ func newConfig(port int, profilingPort int) *Config { Port: DefaultAdminPort, }, Housekeeping: &housekeeping.Config{ - Interval: DefaultHousekeepingInterval.String(), - DeactivateThreshold: DefaultHousekeepingDeactivateThreshold.String(), - CandidatesLimit: DefaultHousekeepingCandidateLimit, + Interval: DefaultHousekeepingInterval.String(), + CandidatesLimitPerProject: DefaultHousekeepingCandidatesLimitPerProject, }, Backend: &backend.Config{ + ClientDeactivateThreshold: DefaultClientDeactivateThreshold.String(), SnapshotThreshold: DefaultSnapshotThreshold, SnapshotInterval: DefaultSnapshotInterval, SnapshotWithPurgingChanges: DefaultSnapshotWithPurgingChanges, diff --git a/server/config.sample.yml b/server/config.sample.yml index be647a027..63596c26f 100644 --- a/server/config.sample.yml +++ b/server/config.sample.yml @@ -30,11 +30,8 @@ Housekeeping: # Interval is the time between housekeeping runs (default: 1m). Interval: 1m - # DeactivateThreshold is the time after which clients are considered deactivate (default: 7d). - DeactivateThreshold: 7d - - # CandidatesLimit is the maximum number of candidates to be returned (default: 100). - CandidatesLimit: 100 + # CandidatesLimitPerProject is the maximum number of candidates to be returned per project (default: 100). + CandidatesLimitPerProject: 100 # Backend is the configuration for the backend of Yorkie. Backend: @@ -42,6 +39,9 @@ Backend: # If public key is not provided from the client, the default project will be # used. If we are using server as single-tenant mode, this should be set to true. UseDefaultProject: true + + # ClientDeactivateThreshold is deactivate threshold of clients in specific project for housekeeping. + ClientDeactivateThreshold: "24h" # SnapshotThreshold is the threshold that determines if changes should be # sent with snapshot when the number of changes is greater than this value. diff --git a/server/config_test.go b/server/config_test.go index 2d6bc353a..e11c9888a 100644 --- a/server/config_test.go +++ b/server/config_test.go @@ -65,6 +65,10 @@ func TestNewConfigFromFile(t *testing.T) { assert.Equal(t, conf.Backend.SnapshotInterval, int64(server.DefaultSnapshotInterval)) assert.Equal(t, conf.Backend.AuthWebhookMaxRetries, uint64(server.DefaultAuthWebhookMaxRetries)) + ClientDeactivateThreshold, err := time.ParseDuration(conf.Backend.ClientDeactivateThreshold) + assert.NoError(t, err) + assert.Equal(t, ClientDeactivateThreshold, server.DefaultClientDeactivateThreshold) + authWebhookMaxWaitInterval, err := time.ParseDuration(conf.Backend.AuthWebhookMaxWaitInterval) assert.NoError(t, err) assert.Equal(t, authWebhookMaxWaitInterval, server.DefaultAuthWebhookMaxWaitInterval) diff --git a/server/projects/projects.go b/server/projects/projects.go index 288b860f5..33d129ea5 100644 --- a/server/projects/projects.go +++ b/server/projects/projects.go @@ -32,7 +32,7 @@ func CreateProject( owner types.ID, name string, ) (*types.Project, error) { - info, err := be.DB.CreateProjectInfo(ctx, name, owner) + info, err := be.DB.CreateProjectInfo(ctx, name, owner, be.Config.ParseClientDeactivateThreshold()) if err != nil { return nil, err } diff --git a/server/rpc/server_test.go b/server/rpc/server_test.go index e9eb20073..f0d7717d0 100644 --- a/server/rpc/server_test.go +++ b/server/rpc/server_test.go @@ -79,9 +79,8 @@ func TestMain(m *testing.M) { DialTimeout: helper.ETCDDialTimeout.String(), LockLeaseTime: helper.ETCDLockLeaseTime.String(), }, &housekeeping.Config{ - Interval: helper.HousekeepingInterval.String(), - DeactivateThreshold: helper.HousekeepingDeactivateThreshold.String(), - CandidatesLimit: helper.HousekeepingCandidatesLimit, + Interval: helper.HousekeepingInterval.String(), + CandidatesLimitPerProject: helper.HousekeepingCandidatesLimitPerProject, }, testAdminAddr, met) if err != nil { log.Fatal(err) diff --git a/test/bench/grpc_bench_test.go b/test/bench/grpc_bench_test.go index d98dbb253..ea760fdd4 100644 --- a/test/bench/grpc_bench_test.go +++ b/test/bench/grpc_bench_test.go @@ -25,6 +25,7 @@ import ( "strings" "sync" "testing" + "time" "github.com/stretchr/testify/assert" @@ -108,14 +109,16 @@ func benchmarkUpdateProject(ctx context.Context, b *testing.B, cnt int, adminCli for _, m := range types.AuthMethods() { authWebhookMethods = append(authWebhookMethods, string(m)) } + clientDeactivateThreshold := 1 * time.Hour _, err := adminCli.UpdateProject( ctx, database.DefaultProjectID.String(), &types.UpdatableProjectFields{ - Name: &name, - AuthWebhookURL: &authWebhookURL, - AuthWebhookMethods: &authWebhookMethods, + Name: &name, + AuthWebhookURL: &authWebhookURL, + AuthWebhookMethods: &authWebhookMethods, + ClientDeactivateThreshold: clientDeactivateThreshold, }, ) assert.NoError(b, err) diff --git a/test/helper/helper.go b/test/helper/helper.go index 2745b5f2c..32f31d0f5 100644 --- a/test/helper/helper.go +++ b/test/helper/helper.go @@ -50,12 +50,12 @@ var ( AdminPort = 21103 - AdminUser = server.DefaultAdminUser - AdminPassword = server.DefaultAdminPassword - HousekeepingInterval = 1 * gotime.Second - HousekeepingDeactivateThreshold = 1 * gotime.Minute - HousekeepingCandidatesLimit = 10 + AdminUser = server.DefaultAdminUser + AdminPassword = server.DefaultAdminPassword + HousekeepingInterval = 1 * gotime.Second + HousekeepingCandidatesLimitPerProject = 10 + ClientDeactivateThreshold = 10 * gotime.Second SnapshotThreshold = int64(10) SnapshotWithPurgingChanges = false AuthWebhookMaxWaitInterval = 3 * gotime.Millisecond @@ -125,9 +125,8 @@ func TestConfig() *server.Config { Port: AdminPort + portOffset, }, Housekeeping: &housekeeping.Config{ - Interval: HousekeepingInterval.String(), - DeactivateThreshold: HousekeepingDeactivateThreshold.String(), - CandidatesLimit: HousekeepingCandidatesLimit, + Interval: HousekeepingInterval.String(), + CandidatesLimitPerProject: HousekeepingCandidatesLimitPerProject, }, Backend: &backend.Config{ AdminUser: server.DefaultAdminUser, @@ -135,6 +134,7 @@ func TestConfig() *server.Config { SecretKey: server.DefaultSecretKey, AdminTokenDuration: server.DefaultAdminTokenDuration.String(), UseDefaultProject: true, + ClientDeactivateThreshold: server.DefaultClientDeactivateThreshold.String(), SnapshotThreshold: SnapshotThreshold, SnapshotWithPurgingChanges: SnapshotWithPurgingChanges, AuthWebhookMaxWaitInterval: AuthWebhookMaxWaitInterval.String(),