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 May 27, 2024
1 parent e3c7be0 commit 5e5dccc
Show file tree
Hide file tree
Showing 6 changed files with 261 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) {

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

type cacheMap[K comparable, V any] struct {
m map[K]V
//cache ??
}

func newCacheMap[K comparable, V any]() cacheMap[K, V] {
return cacheMap[K, V]{map[K]V{}}
}

func (m cacheMap[K, V]) add(key K, object V) {
m.m[key] = object
}

func (m cacheMap[K, V]) get(key K) (V, bool) {
object, found := m.m[key]
return object, found
}

func (m cacheMap[K, V]) has(key K) bool {
_, found := m.m[key]
return found
}
58 changes: 58 additions & 0 deletions grade/internal/infrastructure/seedwork/identity/map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package identity

import "errors"

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

type IdentityMap[K comparable, V any] struct {
manageable cacheMap[K, V]
isolation IsolationStrategy[K, V]
}

func NewIdentityMap[K comparable, V any]() *IdentityMap[K, V] {
manageable := newCacheMap[K, V]()
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) {
im.isolation.add(key, object)

//if _, found := im.objects[key]; found {
// return false, ErrObjectAlreadyWatched
//}

//im.objects[key] = object
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]) 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]{}
default:
im.isolation = &serializableStrategy[K, V]{manageable: im.manageable}
}
}
72 changes: 72 additions & 0 deletions grade/internal/infrastructure/seedwork/identity/map_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package identity

import (
"testing"

"github.com/stretchr/testify/assert"
)

type object struct {
id int
value string
}

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

tests := []struct {
name string
testCase func(t *testing.T)
}{
{
name: "Базовый успешный кейс",
testCase: func(t *testing.T) {
m := NewIdentityMap[int, object]()
first := object{1, "1"}

status, err := m.Add(first.id, first)
assert.NoError(t, err)
assert.Equal(t, true, status)

obj, err := m.Get(1)
assert.NoError(t, err)
assert.Equal(t, object{1, "1"}, obj)
},
},
{
name: "Повторное помещение объекта",
testCase: func(t *testing.T) {
m := NewIdentityMap[int, object]()
first := object{1, "1"}

status, err := m.Add(first.id, first)

assert.NoError(t, err)
assert.Equal(t, true, status)

status, err = m.Add(first.id, first)

assert.Equal(t, err, ErrObjectAlreadyWatched)
assert.Equal(t, false, status)
},
},
{
name: "Получение не отслеживаемого объекта",
testCase: func(t *testing.T) {
m := NewIdentityMap[int, object]()

obj, err := m.Get(1)

assert.Equal(t, ErrObjectNotFound, err)
assert.Equal(t, object{}, obj)
},
},
}

for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
tt.testCase(t)
})
}
}
85 changes: 85 additions & 0 deletions grade/internal/infrastructure/seedwork/identity/strategy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package identity

import "errors"

type IsolationLevel uint

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

var ErrDeniedOperationForStrategy = errors.New("")

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

///////

type readUncommittedStrategy[K comparable, V any] struct {
manageable cacheMap[K, V]
}

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

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 {
readUncommittedStrategy[K, V]
}

// //
type repeatableReadsStrategy[K comparable, V any] struct {
manageable cacheMap[K, V]
}

func (r *repeatableReadsStrategy[K, V]) add(key K, object V) {
if object != nil {
r.manageable.add(key, object)
}
}

func (r *repeatableReadsStrategy[K, V]) get(key K) (V, error) {
object, found := 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 cacheMap[K, V]
}

func (s serializableStrategy[K, V]) add(key K, object V) {
//TODO implement me
panic("implement me")
}

func (s serializableStrategy[K, V]) get(key K) (V, error) {
//obj = self._identity_map().do_get(key)
//if isinstance(obj, NonexistentObject):
//raise ObjectDoesNotExist()
//return obj

s.manageable.get(ke)
}

func (s serializableStrategy[K, V]) has(key K) bool {
return s.manageable.has(key)
}
5 changes: 2 additions & 3 deletions grade/internal/infrastructure/seedwork/session/pgx_session.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package session

import (
"strings"

"database/sql"
"strings"

"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
Expand All @@ -24,7 +23,7 @@ type PgxSession struct {
}

func (s *PgxSession) Atomic(callback session.SessionCallback) error {
// TODO: Add support for SavePoint:
// TODO: add support for SavePoint:
// https://github.com/golang/go/issues/7898#issuecomment-580080390
if s.db == nil {
return errors.New("savePoint is not currently supported")
Expand Down

0 comments on commit 5e5dccc

Please sign in to comment.