Skip to content

Commit

Permalink
Introduce VersionVector (#1047)
Browse files Browse the repository at this point in the history
This change introduces Lamport Synced Version Vector to resolve defects in the 
existing garbage collection system that used syncedSeq. Key improvements include:

- Added Version Vector implementation with Lamport timestamp support
- Implemented database storage and update mechanisms for version vectors
- Created min version vector computation for safe garbage collection
- Added handling for detached client's version vectors to prevent memory leaks
- Updated change ID generation to incorporate version vector information

The Version Vector ensures all changes are properly synchronized across replicas
before garbage collection occurs, improving system reliability and reducing
memory waste from detached clients.

---------

Co-authored-by: Youngteac Hong <[email protected]>
  • Loading branch information
JOOHOJANG and hackerwins authored Oct 23, 2024
1 parent ee6fcf5 commit 3cbd7bf
Show file tree
Hide file tree
Showing 62 changed files with 4,024 additions and 1,653 deletions.
6 changes: 6 additions & 0 deletions admin/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,10 +329,16 @@ func (c *Client) ListChangeSummaries(
return nil, err
}

vector, err := converter.FromVersionVector(snapshotMeta.Msg.VersionVector)
if err != nil {
return nil, err
}

newDoc, err := document.NewInternalDocumentFromSnapshot(
key,
seq,
snapshotMeta.Msg.Lamport,
vector,
snapshotMeta.Msg.Snapshot,
)

Expand Down
40 changes: 37 additions & 3 deletions api/converter/from_pb.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,19 +97,27 @@ func FromChangePack(pbPack *api.ChangePack) (*change.Pack, error) {
return nil, err
}

versionVector, err := FromVersionVector(pbPack.VersionVector)
if err != nil {
return nil, err
}

minSyncedTicket, err := fromTimeTicket(pbPack.MinSyncedTicket)
if err != nil {
return nil, err
}

return &change.Pack{
pack := &change.Pack{
DocumentKey: key.Key(pbPack.DocumentKey),
Checkpoint: fromCheckpoint(pbPack.Checkpoint),
Changes: changes,
Snapshot: pbPack.Snapshot,
MinSyncedTicket: minSyncedTicket,
IsRemoved: pbPack.IsRemoved,
}, nil
VersionVector: versionVector,
MinSyncedTicket: minSyncedTicket,
}

return pack, nil
}

func fromCheckpoint(pbCheckpoint *api.Checkpoint) change.Checkpoint {
Expand Down Expand Up @@ -147,14 +155,40 @@ func fromChangeID(id *api.ChangeID) (change.ID, error) {
if err != nil {
return change.InitialID, err
}

vector, err := FromVersionVector(id.VersionVector)
if err != nil {
return change.InitialID, err
}

return change.NewID(
id.ClientSeq,
id.ServerSeq,
id.Lamport,
actorID,
vector,
), nil
}

// FromVersionVector converts the given Protobuf formats to model format.
func FromVersionVector(pbVersionVector *api.VersionVector) (time.VersionVector, error) {
versionVector := make(time.VersionVector)
// TODO(hackerwins): Old clients do not send VersionVector. We should remove this later.
if pbVersionVector == nil {
return versionVector, nil
}

for id, lamport := range pbVersionVector.Vector {
actorID, err := time.ActorIDFromHex(id)
if err != nil {
return nil, err
}
versionVector.Set(actorID, lamport)
}

return versionVector, nil
}

// FromDocumentID converts the given Protobuf formats to model format.
func FromDocumentID(pbID string) (types.ID, error) {
id := types.ID(pbID)
Expand Down
45 changes: 39 additions & 6 deletions api/converter/to_pb.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,18 @@ func ToChangePack(pack *change.Pack) (*api.ChangePack, error) {
return nil, err
}

pbVersionVector, err := ToVersionVector(pack.VersionVector)
if err != nil {
return nil, err
}

return &api.ChangePack{
DocumentKey: pack.DocumentKey.String(),
Checkpoint: ToCheckpoint(pack.Checkpoint),
Changes: pbChanges,
Snapshot: pack.Snapshot,
MinSyncedTicket: ToTimeTicket(pack.MinSyncedTicket),
VersionVector: pbVersionVector,
IsRemoved: pack.IsRemoved,
}, nil
}
Expand All @@ -155,13 +161,35 @@ func ToCheckpoint(cp change.Checkpoint) *api.Checkpoint {
}

// ToChangeID converts the given model format to Protobuf format.
func ToChangeID(id change.ID) *api.ChangeID {
func ToChangeID(id change.ID) (*api.ChangeID, error) {
pbVersionVector, err := ToVersionVector(id.VersionVector())
if err != nil {
return nil, err
}
return &api.ChangeID{
ClientSeq: id.ClientSeq(),
ServerSeq: id.ServerSeq(),
Lamport: id.Lamport(),
ActorId: id.ActorID().Bytes(),
ClientSeq: id.ClientSeq(),
ServerSeq: id.ServerSeq(),
Lamport: id.Lamport(),
ActorId: id.ActorID().Bytes(),
VersionVector: pbVersionVector,
}, nil
}

// ToVersionVector converts the given model format to Protobuf format.
func ToVersionVector(vector time.VersionVector) (*api.VersionVector, error) {
pbVersionVector := make(map[string]int64)
for actor, clock := range vector {
id, err := time.ActorIDFromBytes(actor[:])
if err != nil {
return nil, err
}

pbVersionVector[id.String()] = clock
}

return &api.VersionVector{
Vector: pbVersionVector,
}, nil
}

// ToDocEventType converts the given model format to Protobuf format.
Expand Down Expand Up @@ -243,8 +271,13 @@ func ToChanges(changes []*change.Change) ([]*api.Change, error) {
return nil, err
}

pbChangeID, err := ToChangeID(c.ID())
if err != nil {
return nil, err
}

pbChanges = append(pbChanges, &api.Change{
Id: ToChangeID(c.ID()),
Id: pbChangeID,
Message: c.Message(),
Operations: pbOperations,
PresenceChange: ToPresenceChange(c.PresenceChange()),
Expand Down
41 changes: 41 additions & 0 deletions api/docs/yorkie/v1/admin.openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,12 @@ components:
- type: string
- type: number
title: server_seq
versionVector:
$ref: '#/components/schemas/yorkie.v1.VersionVector'
additionalProperties: false
description: ""
title: version_vector
type: object
title: ChangeID
type: object
yorkie.v1.ChangePasswordRequest:
Expand Down Expand Up @@ -965,6 +971,12 @@ components:
format: byte
title: snapshot
type: string
versionVector:
$ref: '#/components/schemas/yorkie.v1.VersionVector'
additionalProperties: false
description: ""
title: version_vector
type: object
title: GetSnapshotMetaResponse
type: object
yorkie.v1.JSONElementSimple:
Expand Down Expand Up @@ -2226,6 +2238,35 @@ components:
- 13
title: ValueType
type: string
yorkie.v1.VersionVector:
additionalProperties: false
description: ""
properties:
vector:
additionalProperties: false
description: ""
title: vector
type: object
title: VersionVector
type: object
yorkie.v1.VersionVector.VectorEntry:
additionalProperties: false
description: ""
properties:
key:
additionalProperties: false
description: ""
title: key
type: string
value:
additionalProperties: false
description: ""
oneOf:
- type: string
- type: number
title: value
title: VectorEntry
type: object
securitySchemes:
ApiKeyAuth:
in: header
Expand Down
41 changes: 41 additions & 0 deletions api/docs/yorkie/v1/resources.openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,12 @@ components:
- type: string
- type: number
title: server_seq
versionVector:
$ref: '#/components/schemas/yorkie.v1.VersionVector'
additionalProperties: false
description: ""
title: version_vector
type: object
title: ChangeID
type: object
yorkie.v1.ChangePack:
Expand Down Expand Up @@ -263,6 +269,12 @@ components:
format: byte
title: snapshot
type: string
versionVector:
$ref: '#/components/schemas/yorkie.v1.VersionVector'
additionalProperties: false
description: ""
title: version_vector
type: object
title: ChangePack
type: object
yorkie.v1.Checkpoint:
Expand Down Expand Up @@ -1752,6 +1764,35 @@ components:
- 13
title: ValueType
type: string
yorkie.v1.VersionVector:
additionalProperties: false
description: ""
properties:
vector:
additionalProperties: false
description: ""
title: vector
type: object
title: VersionVector
type: object
yorkie.v1.VersionVector.VectorEntry:
additionalProperties: false
description: ""
properties:
key:
additionalProperties: false
description: ""
title: key
type: string
value:
additionalProperties: false
description: ""
oneOf:
- type: string
- type: number
title: value
title: VectorEntry
type: object
securitySchemes:
ApiKeyAuth:
in: header
Expand Down
41 changes: 41 additions & 0 deletions api/docs/yorkie/v1/yorkie.openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,12 @@ components:
- type: string
- type: number
title: server_seq
versionVector:
$ref: '#/components/schemas/yorkie.v1.VersionVector'
additionalProperties: false
description: ""
title: version_vector
type: object
title: ChangeID
type: object
yorkie.v1.ChangePack:
Expand Down Expand Up @@ -471,6 +477,12 @@ components:
format: byte
title: snapshot
type: string
versionVector:
$ref: '#/components/schemas/yorkie.v1.VersionVector'
additionalProperties: false
description: ""
title: version_vector
type: object
title: ChangePack
type: object
yorkie.v1.Checkpoint:
Expand Down Expand Up @@ -1549,6 +1561,35 @@ components:
- 13
title: ValueType
type: string
yorkie.v1.VersionVector:
additionalProperties: false
description: ""
properties:
vector:
additionalProperties: false
description: ""
title: vector
type: object
title: VersionVector
type: object
yorkie.v1.VersionVector.VectorEntry:
additionalProperties: false
description: ""
properties:
key:
additionalProperties: false
description: ""
title: key
type: string
value:
additionalProperties: false
description: ""
oneOf:
- type: string
- type: number
title: value
title: VectorEntry
type: object
yorkie.v1.WatchDocumentRequest:
additionalProperties: false
description: ""
Expand Down
Loading

0 comments on commit 3cbd7bf

Please sign in to comment.