Skip to content

Commit

Permalink
First steps in IdentityMap implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Ivan Sushkov committed Jun 3, 2024
1 parent 32485fb commit 8cd785e
Show file tree
Hide file tree
Showing 11 changed files with 817 additions and 3 deletions.
20 changes: 20 additions & 0 deletions grade/internal/domain/specialist/specialist_reconstitutor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package specialist

import "testing"

func TestSpecialistReconstitutor(t *testing.T) {
t.Parallel()

tests := []struct {
name string
}{
{},
}

for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {

})
}
}
63 changes: 63 additions & 0 deletions grade/internal/infrastructure/seedwork/identity/map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package identity

import (
"errors"

"github.com/emacsway/grade/grade/pkg/collections"
)

var (
ErrObjectAlreadyWatched = errors.New("")
ErrObjectNotFound = errors.New("")
)

type IdentityMap[K comparable, V any] struct {
manageable collections.ReplacingMap[K, V]

Check failure on line 15 in grade/internal/infrastructure/seedwork/identity/map.go

View workflow job for this annotation

GitHub Actions / lint

`manageable` is unused (structcheck)
isolation IsolationStrategy[K, V]

Check failure on line 16 in grade/internal/infrastructure/seedwork/identity/map.go

View workflow job for this annotation

GitHub Actions / lint

`isolation` is unused (structcheck)
}

func NewIdentityMap[K comparable, V any](size uint) *IdentityMap[K, V] {
manageable := collections.NewReplacingMap[K, V](size)
isolation := serializableStrategy[K, V]{manageable: manageable}

return &IdentityMap[K, V]{
manageable: manageable,
isolation: &isolation,
}
}

func (im *IdentityMap[K, V]) Add(key K, object V) (bool, error) {
if err := im.isolation.add(key, object); err != nil {
return false, err
}

return true, nil
}

func (im *IdentityMap[K, V]) Get(key K) (object V, err error) {
return im.isolation.get(key)
}

func (im *IdentityMap[K, V]) Has(key K) bool {
return im.isolation.has(key)
}

func (im *IdentityMap[K, V]) SetSize(size uint) {
im.manageable.SetSize(size)
}

func (im *IdentityMap[K, V]) SetIsolationLevel(level IsolationLevel) {

switch level {
case ReadUncommitted:
im.isolation = &readUncommittedStrategy[K, V]{manageable: im.manageable}
case RepeatableReads:
im.isolation = &repeatableReadsStrategy[K, V]{manageable: im.manageable}
case Serializable:
im.isolation = &serializableStrategy[K, V]{manageable: im.manageable}
case ReadCommitted:
im.isolation = &readCommittedStrategy[K, V]{manageable: im.manageable}
default:
im.isolation = &serializableStrategy[K, V]{manageable: im.manageable}
}
}
91 changes: 91 additions & 0 deletions grade/internal/infrastructure/seedwork/identity/strategy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package identity

import (
"errors"

"github.com/emacsway/grade/grade/pkg/collections"
)

type IsolationLevel uint

const (
ReadUncommitted IsolationLevel = iota
ReadCommitted = iota
RepeatableReads = iota
Serializable = iota
)

var (
ErrNonexistentObject = errors.New("")
ErrDeniedOperationForStrategy = errors.New("")
)

type IsolationStrategy[K comparable, V any] interface {
add(key K, object V) error
get(key K) (V, error)
has(key K) bool
}

type readUncommittedStrategy[K comparable, V any] struct {
manageable collections.ReplacingMap[K, V]

Check failure on line 30 in grade/internal/infrastructure/seedwork/identity/strategy.go

View workflow job for this annotation

GitHub Actions / lint

`manageable` is unused (structcheck)
}

func (r *readUncommittedStrategy[K, V]) add(key K, object V) error {
return nil
}

func (r *readUncommittedStrategy[K, V]) get(key K) (object V, err error) {
return object, ErrDeniedOperationForStrategy
}

func (r *readUncommittedStrategy[K, V]) has(key K) bool {
return false
}

type readCommittedStrategy[K comparable, V any] struct {
manageable collections.ReplacingMap[K, V]
}

func (r *readCommittedStrategy[K, V]) add(key K, object V) error {
return nil
}

func (r *readCommittedStrategy[K, V]) get(key K) (object V, err error) {
return object, nil
}

func (r *readCommittedStrategy[K, V]) has(key K) bool {
return false
}

type repeatableReadsStrategy[K comparable, V any] struct {
manageable collections.ReplacingMap[K, V]
}

func (r *repeatableReadsStrategy[K, V]) add(key K, object V) error {
return nil
}

func (r *repeatableReadsStrategy[K, V]) get(key K) (V, error) {
return r.manageable.Get(key)
}

func (r *repeatableReadsStrategy[K, V]) has(key K) bool {
return r.manageable.Has(key)
}

type serializableStrategy[K comparable, V any] struct {
manageable collections.ReplacingMap[K, V]

Check failure on line 78 in grade/internal/infrastructure/seedwork/identity/strategy.go

View workflow job for this annotation

GitHub Actions / lint

`manageable` is unused (structcheck)
}

func (s *serializableStrategy[K, V]) add(key K, object V) error {
return nil
}

func (s *serializableStrategy[K, V]) get(key K) (V, error) {
return s.manageable.Get(key)
}

func (s *serializableStrategy[K, V]) has(key K) bool {
return s.manageable.Has(key)
}
180 changes: 180 additions & 0 deletions grade/internal/infrastructure/seedwork/session/identity_map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
package session

import "errors"

type IsolationLevel int

type NonexistentObject struct{}

var (
ErrNonexistentObject = errors.New("")
ErrUnknownKey = errors.New("")
)

const (
ReadUncommittedLevel IsolationLevel = iota
ReadCommittedLevel
RepeatableReadsLevel
SerializableLevel
)

type IsolationStrategy[K any] interface {
add(key K, value any) error
get(key K) (any, error)
has(key K) (bool, error)
}

type SerializableStrategy[K comparable] struct {
identityMap *IdentityMapImpl[K]
}

func (s *SerializableStrategy[K]) add(key K, value any) error {
if value == nil {
value = NonexistentObject{}
}

s.identityMap.doAdd(key, value)
return nil
}

func (s *SerializableStrategy[K]) get(key K) (any, error) {

object := s.identityMap.doGet(key)
if _, ok := object.(NonexistentObject); ok || object == nil {
return nil, ErrNonexistentObject
}

return object, nil
}

func (s *SerializableStrategy[K]) has(key K) (bool, error) {
return s.identityMap.doHas(key), nil
}

type ReadUncommittedStrategy[K comparable] struct {
identityMap *IdentityMapImpl[K]
}

func (s *ReadUncommittedStrategy[K]) add(key K, value any) error {
return nil
}

func (s *ReadUncommittedStrategy[K]) get(key K) (any, error) {
return nil, ErrUnknownKey
}

func (s *ReadUncommittedStrategy[K]) has(key K) (bool, error) {
return false, nil
}

type ReadCommittedStrategy[K comparable] struct {
identityMap *IdentityMapImpl[K]
}

func (s *ReadCommittedStrategy[K]) add(key K, value any) error {
return nil
}

func (s *ReadCommittedStrategy[K]) get(key K) (any, error) {
return nil, ErrUnknownKey
}

func (s *ReadCommittedStrategy[K]) has(key K) (bool, error) {
return false, nil
}

type RepeatableReadsStrategy[K comparable] struct {
identityMap *IdentityMapImpl[K]
}

func (s *RepeatableReadsStrategy[K]) add(key K, value any) error {
if value != nil {
s.identityMap.doAdd(key, value)
}

return nil
}

func (s *RepeatableReadsStrategy[K]) get(key K) (any, error) {
object := s.identityMap.doGet(key)
if _, ok := object.(NonexistentObject); ok || object == nil {
return nil, ErrNonexistentObject
}

return object, nil
}

func (s *RepeatableReadsStrategy[K]) has(key K) (bool, error) {
if !s.identityMap.doHas(key) {
return false, ErrUnknownKey
}

object := s.identityMap.doGet(key)
_, ok := object.(NonexistentObject)

return ok, nil
}

type IdentityMapImpl[K comparable] struct {
alive map[K]any
strategy IsolationStrategy[K]
}

func NewIdentityMap[K comparable](isolation IsolationLevel) IdentityMap[K] {
identity := &IdentityMapImpl[K]{
alive: map[K]any{},
}

identity.SetIsolationLevel(isolation)
return identity
}

func (i *IdentityMapImpl[K]) Get(key K) (any, error) {
return i.strategy.get(key)
}

func (i *IdentityMapImpl[K]) Add(key K, value any) error {
return i.strategy.add(key, value)
}

func (i *IdentityMapImpl[K]) Has(key K) (bool, error) {
return i.strategy.has(key)
}

func (i *IdentityMapImpl[K]) Clear() {
i.alive = map[K]any{}
}

func (i *IdentityMapImpl[K]) Remove(key K) {
if _, found := i.alive[key]; !found {
return
}

delete(i.alive, key)
}

func (i *IdentityMapImpl[K]) SetIsolationLevel(isolation IsolationLevel) {
switch isolation {
case SerializableLevel:
i.strategy = &SerializableStrategy[K]{i}
case ReadUncommittedLevel:
i.strategy = &ReadUncommittedStrategy[K]{i}
case ReadCommittedLevel:
i.strategy = &ReadCommittedStrategy[K]{i}
case RepeatableReadsLevel:
i.strategy = &RepeatableReadsStrategy[K]{i}
}
}

func (i *IdentityMapImpl[K]) doAdd(key K, value any) {
i.alive[key] = value
}

func (i *IdentityMapImpl[K]) doGet(key K) any {
return i.alive[key]
}

func (i *IdentityMapImpl[K]) doHas(key K) bool {
_, found := i.alive[key]
return found
}
Loading

0 comments on commit 8cd785e

Please sign in to comment.