Skip to content

Commit

Permalink
disabled archived table for logs
Browse files Browse the repository at this point in the history
  • Loading branch information
latolukasz committed Jan 24, 2024
1 parent 43e3103 commit 072e40d
Show file tree
Hide file tree
Showing 8 changed files with 413 additions and 98 deletions.
28 changes: 28 additions & 0 deletions entity_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ type entitySchema struct {
uniqueIndices map[string][]string
references map[string]referenceDefinition
cachedReferences map[string]referenceDefinition
indexes map[string]indexDefinition
cachedIndexes map[string]indexDefinition
options map[string]any
cacheAll bool
hasLocalCache bool
Expand Down Expand Up @@ -280,6 +282,8 @@ func (e *entitySchema) init(registry *registry, entityType reflect.Type) error {
e.options = make(map[string]any)
e.references = make(map[string]referenceDefinition)
e.cachedReferences = make(map[string]referenceDefinition)
e.indexes = make(map[string]indexDefinition)
e.cachedIndexes = make(map[string]indexDefinition)
e.mapBindToScanPointer = mapBindToScanPointer{}
e.mapPointerToValue = mapPointerToValue{}
e.mysqlPoolCode = e.getTag("mysql", "default", DefaultPoolCode)
Expand Down Expand Up @@ -399,6 +403,30 @@ func (e *entitySchema) init(registry *registry, entityType reflect.Type) error {
if err != nil {
return err
}
for indexName, indexColumns := range indices {
where := ""
for i := 0; i < len(indexColumns); i++ {
if i > 0 {
where += " AND "
}
where += "`" + indexColumns[i+1] + "`=?"
}
cached := false
tags, hasTag := e.tags[indexColumns[1]]
if hasTag {
cached = tags["cached"] == "true"
}
columnsList := make([]string, len(indexColumns))
for j := 0; j < len(indexColumns); j++ {
columnsList[j] = indexColumns[j+1]
}

definition := indexDefinition{Where: where, Cached: cached, Columns: columnsList}
e.indexes[indexName] = definition
if cached {
e.cachedIndexes[indexName] = definition
}
}
for _, plugin := range registry.plugins {
pluginInterfaceValidateEntitySchema, isInterface := plugin.(PluginInterfaceValidateEntitySchema)
if isInterface {
Expand Down
12 changes: 6 additions & 6 deletions flush.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ func (orm *ormImplementation) handleDeletes(async bool, schema *entitySchema, op
refColumn := columnName
if schema.hasLocalCache {
orm.flushPostActions = append(orm.flushPostActions, func(_ ORM) {
lc.removeReference(orm, refColumn, id.(uint64))
lc.removeList(orm, refColumn, id.(uint64))
})
}
idAsString := strconv.FormatUint(id.(uint64), 10)
Expand All @@ -186,7 +186,7 @@ func (orm *ormImplementation) handleDeletes(async bool, schema *entitySchema, op
if schema.cacheAll {
if schema.hasLocalCache {
orm.flushPostActions = append(orm.flushPostActions, func(_ ORM) {
lc.removeReference(orm, cacheAllFakeReferenceKey, 0)
lc.removeList(orm, cacheAllFakeReferenceKey, 0)
})
}
redisSetKey := schema.cacheKey + ":" + cacheAllFakeReferenceKey
Expand Down Expand Up @@ -356,7 +356,7 @@ func (orm *ormImplementation) handleInserts(async bool, schema *entitySchema, op
refColumn := columnName
if schema.hasLocalCache {
orm.flushPostActions = append(orm.flushPostActions, func(_ ORM) {
lc.removeReference(orm, refColumn, id.(uint64))
lc.removeList(orm, refColumn, id.(uint64))
})
}
redisSetKey := schema.cacheKey + ":" + refColumn + ":" + strconv.FormatUint(id.(uint64), 10)
Expand All @@ -365,7 +365,7 @@ func (orm *ormImplementation) handleInserts(async bool, schema *entitySchema, op
if schema.cacheAll {
if schema.hasLocalCache {
orm.flushPostActions = append(orm.flushPostActions, func(_ ORM) {
lc.removeReference(orm, cacheAllFakeReferenceKey, 0)
lc.removeList(orm, cacheAllFakeReferenceKey, 0)
})
}
redisSetKey := schema.cacheKey + ":" + cacheAllFakeReferenceKey
Expand Down Expand Up @@ -555,7 +555,7 @@ func (orm *ormImplementation) handleUpdates(async bool, schema *entitySchema, op
if oldAsInt > 0 {
if schema.hasLocalCache {
orm.flushPostActions = append(orm.flushPostActions, func(_ ORM) {
schema.localCache.removeReference(orm, refColumn, oldAsInt)
schema.localCache.removeList(orm, refColumn, oldAsInt)
})
}
redisSetKey := schema.cacheKey + ":" + refColumn + ":" + strconv.FormatUint(oldAsInt, 10)
Expand All @@ -564,7 +564,7 @@ func (orm *ormImplementation) handleUpdates(async bool, schema *entitySchema, op
if newAsInt > 0 {
if schema.hasLocalCache {
orm.flushPostActions = append(orm.flushPostActions, func(_ ORM) {
schema.localCache.removeReference(orm, refColumn, newAsInt)
schema.localCache.removeList(orm, refColumn, newAsInt)
})
}
redisSetKey := schema.cacheKey + ":" + refColumn + ":" + strconv.FormatUint(newAsInt, 10)
Expand Down
3 changes: 1 addition & 2 deletions get_all.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,5 @@ func GetAll[E any](orm ORM) EntityIterator[E] {
if !schema.cacheAll {
return Search[E](orm, allEntitiesWhere, nil)
}
lc, hasLocalCache := schema.GetLocalCache()
return getCachedList[E](orm, cacheAllFakeReferenceKey, 0, hasLocalCache, lc, schema, schema)
return getCachedByReference[E](orm, cacheAllFakeReferenceKey, 0, schema)
}
172 changes: 172 additions & 0 deletions get_by_index.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package beeorm

import (
"fmt"
"hash/fnv"
"reflect"
"slices"
"strconv"

jsoniter "github.com/json-iterator/go"
)

type indexDefinition struct {
Cached bool
Columns []string
Where string
}

func (d indexDefinition) CreteWhere(hasNil bool, attributes []any) Where {
if !hasNil {
return NewWhere(d.Where, attributes...)
}
query := ""
newAttributes := make([]any, 0)
for i, column := range d.Columns {
if i > 0 {
query += " AND "
}
if attributes[i] == nil {
query += "`" + column + "` IS NULL"
} else {
query += "`" + column + "`=?"
newAttributes = append(newAttributes, attributes[i])
}
}
return NewWhere(query, newAttributes)
}

func GetByIndex[E any](orm ORM, indexName string, attributes ...any) EntityIterator[E] {
var e E
schema := orm.(*ormImplementation).engine.registry.entitySchemas[reflect.TypeOf(e)]
if schema == nil {
panic(fmt.Errorf("entity '%T' is not registered", e))
}
def, has := schema.indexes[indexName]
if !has {
panic(fmt.Errorf("unknow index name `%s`", indexName))
}
if len(attributes) != len(def.Columns) {
panic(fmt.Errorf("invalid attributes length, %d is required, %d provided", len(def.Columns), len(attributes)))
}
hasNil := false
for i, attribute := range attributes {
if attribute == nil {
hasNil = true
continue
}
setter := schema.fieldBindSetters[def.Columns[i]]
bind, err := setter(attribute)
if err != nil {
panic(err)
}
attributes[i] = bind
}
if !def.Cached {
return Search[E](orm, def.CreteWhere(hasNil, attributes), nil)
}
return getCachedByColumns[E](orm, indexName, def, schema, attributes, hasNil)
}

func getCachedByColumns[E any](orm ORM, indexName string, index indexDefinition, schema *entitySchema, attributes []any, hasNil bool) EntityIterator[E] {
attributesHash, err := jsoniter.ConfigFastest.Marshal(attributes)
hash := fnv.New64()
_, err = hash.Write(attributesHash)
checkError(err)
bindID := hash.Sum64()
if schema.hasLocalCache {
fromCache, hasInCache := schema.localCache.getList(orm, indexName, bindID)
if hasInCache {
if fromCache == cacheNilValue {
return &emptyResultsIterator[E]{}
}

if schema.hasLocalCache {
results := &entityIterator[E]{index: -1}
results.rows = fromCache.([]*E)
return results
}
return GetByIDs[E](orm, fromCache.([]uint64)...)
}
}
rc := orm.Engine().Redis(schema.getForcedRedisCode())
redisSetKey := schema.cacheKey + ":" + indexName + strconv.FormatUint(bindID, 10)
fromRedis := rc.SMembers(orm, redisSetKey)
if len(fromRedis) > 0 {
ids := make([]uint64, len(fromRedis))
k := 0
hasValidValue := false
for _, value := range fromRedis {
if value == redisValidSetValue {
hasValidValue = true
continue
} else if value == cacheNilValue {
continue
}
ids[k], _ = strconv.ParseUint(value, 10, 64)
k++
}
if hasValidValue {
if k == 0 {
if schema.hasLocalCache {
schema.localCache.setList(orm, indexName, bindID, cacheNilValue)
}
return &emptyResultsIterator[E]{}
}
ids = ids[0:k]
slices.Sort(ids)
values := GetByIDs[E](orm, ids...)
if schema.hasLocalCache {
if values.Len() == 0 {
schema.localCache.setList(orm, indexName, bindID, cacheNilValue)
} else {
if schema.hasLocalCache {
schema.localCache.setList(orm, indexName, bindID, values.All())
} else {
schema.localCache.setList(orm, indexName, bindID, ids)
}
}
}
return values
}
}
if schema.hasLocalCache {
ids := SearchIDs[E](orm, index.CreteWhere(hasNil, attributes), nil)
if len(ids) == 0 {
schema.localCache.setList(orm, indexName, bindID, cacheNilValue)
rc.SAdd(orm, redisSetKey, cacheNilValue)
return &emptyResultsIterator[E]{}
}
idsForRedis := make([]any, len(ids))
for i, value := range ids {
idsForRedis[i] = strconv.FormatUint(value, 10)
}
p := orm.RedisPipeLine(rc.GetCode())
p.Del(redisSetKey)
p.SAdd(redisSetKey, redisValidSetValue)
p.SAdd(redisSetKey, idsForRedis...)
p.Exec(orm)
values := GetByIDs[E](orm, ids...)
if schema.hasLocalCache {
schema.localCache.setList(orm, indexName, bindID, values.All())
} else {
schema.localCache.setList(orm, indexName, bindID, ids)
}
return values
}
values := Search[E](orm, index.CreteWhere(hasNil, attributes), nil)
if values.Len() == 0 {
rc.SAdd(orm, redisSetKey, redisValidSetValue, cacheNilValue)
} else {
idsForRedis := make([]any, values.Len()+1)
idsForRedis[0] = redisValidSetValue
i := 0
for values.Next() {
idsForRedis[i+1] = strconv.FormatUint(reflect.ValueOf(values.Entity()).Elem().Field(0).Uint(), 10)
i++
}
values.Reset()
rc.SAdd(orm, redisSetKey, idsForRedis...)
}
return values
}
107 changes: 107 additions & 0 deletions get_by_index_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package beeorm

import (
"testing"
"time"

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

type getByIndexEntity struct {
ID uint64 `orm:"localCache;redisCache"`
Name string `orm:"index=Name"`
Age uint32 `orm:"index=Age;cached"`
Born *time.Time `orm:"index=Age:2"`
}

func TestGetByIndexNoCache(t *testing.T) {
testGetByIndex(t, false, false)
}

func TestGetByIndexLocalCache(t *testing.T) {
testGetByIndex(t, true, false)
}

func TestGetByIndexRedisCache(t *testing.T) {
testGetByIndex(t, false, true)
}

func TestGetByIndexLocalRedisCache(t *testing.T) {
testGetByIndex(t, true, true)
}

func testGetByIndex(t *testing.T, local, redis bool) {
var entity *getByIndexEntity
orm := PrepareTables(t, NewRegistry(), entity)
schema := GetEntitySchema[getByIndexEntity](orm)
schema.DisableCache(!local, !redis)

loggerDB := &MockLogHandler{}
orm.RegisterQueryLogger(loggerDB, true, false, false)

now := time.Now().UTC()
nextWeek := now.Add(time.Hour * 24 * 7)
// getting missing rows
rows := GetByIndex[getByIndexEntity](orm, "Age", 23, now)
assert.Equal(t, 0, rows.Len())
loggerDB.Clear()
rows = GetByIndex[getByIndexEntity](orm, "Age", 23, now)
assert.Equal(t, 0, rows.Len())
assert.Len(t, loggerDB.Logs, 0)

var entities []*getByIndexEntity
for i := 0; i < 10; i++ {
entity = NewEntity[getByIndexEntity](orm)
entity.Age = 10
if i >= 5 {
entity.Name = "Test Name"
entity.Age = 18
entity.Born = &now
}
if i >= 8 {
entity.Name = "Test Name 2"
entity.Age = 40
entity.Born = &nextWeek
}
entities = append(entities, entity)
}
assert.NoError(t, orm.Flush())

rows = GetByIndex[getByIndexEntity](orm, "Name", nil)
assert.Equal(t, 5, rows.Len())
rows.Next()
e := rows.Entity()
assert.Equal(t, entities[0].ID, e.ID)

rows = GetByIndex[getByIndexEntity](orm, "Name", "Test name")
assert.Equal(t, 3, rows.Len())
rows.Next()
e = rows.Entity()
assert.Equal(t, entities[5].ID, e.ID)

rows = GetByIndex[getByIndexEntity](orm, "Age", 10, nil)
assert.Equal(t, 5, rows.Len())
rows.Next()
e = rows.Entity()
assert.Equal(t, entities[0].ID, e.ID)
loggerDB.Clear()
rows = GetByIndex[getByIndexEntity](orm, "Age", 10, nil)
assert.Equal(t, 5, rows.Len())
if local || redis {
assert.Len(t, loggerDB.Logs, 0)
}
loggerDB.Clear()

rows = GetByIndex[getByIndexEntity](orm, "Age", 18, now)
assert.Equal(t, 3, rows.Len())
rows.Next()
e = rows.Entity()
loggerDB.Clear()
assert.Equal(t, entities[5].ID, e.ID)
rows = GetByIndex[getByIndexEntity](orm, "Age", 18, now)
assert.Equal(t, 3, rows.Len())
if local || redis {
assert.Len(t, loggerDB.Logs, 0)
}
loggerDB.Clear()
}
Loading

0 comments on commit 072e40d

Please sign in to comment.