@@ -39,6 +39,7 @@ const (
3939 cmdExpire = "EXPIRE"
4040 cmdFlushAll = "FLUSHALL"
4141 cmdFlushDB = "FLUSHDB"
42+ cmdFlushLegacy = "FLUSHLEGACY"
4243 cmdGet = "GET"
4344 cmdGetDel = "GETDEL"
4445 cmdHDel = "HDEL"
@@ -113,6 +114,7 @@ const (
113114const (
114115 redisLatestCommitTimeout = 5 * time .Second
115116 redisDispatchTimeout = 10 * time .Second
117+ redisFlushLegacyTimeout = 10 * time .Minute
116118 redisRelayPublishTimeout = 2 * time .Second
117119 redisTraceArgLimit = 6
118120 redisTraceArgMaxLen = 96
@@ -170,6 +172,7 @@ var argsLen = map[string]int{
170172 cmdExpire : - 3 ,
171173 cmdFlushAll : 1 ,
172174 cmdFlushDB : 1 ,
175+ cmdFlushLegacy : 1 ,
173176 cmdGet : 2 ,
174177 cmdGetDel : 2 ,
175178 cmdHDel : - 3 ,
@@ -358,6 +361,7 @@ func NewRedisServer(listen net.Listener, redisAddr string, store store.MVCCStore
358361 cmdExpire : r .expire ,
359362 cmdFlushAll : r .flushall ,
360363 cmdFlushDB : r .flushdb ,
364+ cmdFlushLegacy : r .flushlegacy ,
361365 cmdGet : r .get ,
362366 cmdGetDel : r .getdel ,
363367 cmdHDel : r .hdel ,
@@ -692,7 +696,7 @@ func (r *RedisServer) loadRedisSetState(key []byte, readTS uint64, returnOld boo
692696 return state , nil
693697 }
694698
695- oldValue , err := r .readValueAt (key , readTS )
699+ oldValue , err := r .readRedisStringAt (key , readTS )
696700 if err != nil && ! errors .Is (err , store .ErrKeyNotFound ) {
697701 return redisSetState {}, err
698702 }
@@ -709,7 +713,7 @@ func (r *RedisServer) replaceWithStringTxn(ctx context.Context, key, value []byt
709713 }
710714 elems = append (elems , delElems ... )
711715 }
712- elems = append (elems , & kv.Elem [kv.OP ]{Op : kv .Put , Key : key , Value : bytes .Clone (value )})
716+ elems = append (elems , & kv.Elem [kv.OP ]{Op : kv .Put , Key : redisStrKey ( key ) , Value : bytes .Clone (value )})
713717 if ttl != nil {
714718 elems = append (elems , & kv.Elem [kv.OP ]{Op : kv .Put , Key : redisTTLKey (key ), Value : encodeRedisTTL (* ttl )})
715719 } else {
@@ -955,7 +959,7 @@ func (r *RedisServer) get(conn redcon.Conn, cmd redcon.Command) {
955959 return
956960 }
957961
958- v , err := r .readValueAt (key , readTS )
962+ v , err := r .readRedisStringAt (key , readTS )
959963 if err != nil {
960964 if errors .Is (err , store .ErrKeyNotFound ) {
961965 conn .WriteNull ()
@@ -1423,28 +1427,51 @@ func (t *txnContext) trackTypeReadKeys(key []byte) {
14231427 redisZSetKey (key ),
14241428 redisStreamKey (key ),
14251429 redisHLLKey (key ),
1426- key ,
1430+ redisStrKey (key ),
1431+ key , // legacy bare key for fallback reads
14271432 redisTTLKey (key ),
14281433 } {
14291434 t .trackReadKey (readKey )
14301435 }
14311436}
14321437
14331438func (t * txnContext ) load (key []byte ) (* txnValue , error ) {
1434- k := string (key )
1439+ // If the key is already an internal key (e.g., !redis|hash|...,
1440+ // !lst|..., !txn|..., !ddb|..., !s3|..., !dist|...), use it as-is.
1441+ // Otherwise, it's a bare user key for a string value — prefix it.
1442+ storageKey := key
1443+ userKey := extractRedisInternalUserKey (key )
1444+ if ! isKnownInternalKey (key ) {
1445+ storageKey = redisStrKey (key )
1446+ userKey = key
1447+ }
1448+ k := string (storageKey )
14351449 if tv , ok := t .working [k ]; ok {
14361450 return tv , nil
14371451 }
1438- t .trackReadKey (key )
1439- if userKey := extractRedisInternalUserKey (key ); userKey != nil {
1452+ t .trackReadKey (storageKey )
1453+ if ! isKnownInternalKey (key ) {
1454+ // Track the bare key too for conflict detection on legacy fallback reads.
1455+ t .trackReadKey (key )
1456+ }
1457+ if userKey != nil {
14401458 t .trackReadKey (redisTTLKey (userKey ))
1441- } else if ! bytes .HasPrefix (key , []byte (redisTTLPrefix )) {
1442- t .trackReadKey (redisTTLKey (key ))
14431459 }
14441460 tv := & txnValue {}
1445- val , err := t .server .readValueAt (key , t .startTS )
1446- if err != nil && ! errors .Is (err , store .ErrKeyNotFound ) {
1447- return nil , errors .WithStack (err )
1461+ var val []byte
1462+ if ! isKnownInternalKey (key ) {
1463+ // For bare user string keys, use the fallback-aware reader.
1464+ var err error
1465+ val , err = t .server .readRedisStringAt (key , t .startTS )
1466+ if err != nil && ! errors .Is (err , store .ErrKeyNotFound ) {
1467+ return nil , errors .WithStack (err )
1468+ }
1469+ } else {
1470+ var err error
1471+ val , err = t .server .readValueAt (storageKey , t .startTS )
1472+ if err != nil && ! errors .Is (err , store .ErrKeyNotFound ) {
1473+ return nil , errors .WithStack (err )
1474+ }
14481475 }
14491476 tv .raw = val
14501477 tv .loaded = true
@@ -1565,7 +1592,7 @@ func (t *txnContext) stagedListType(key string) (redisValueType, bool) {
15651592}
15661593
15671594func (t * txnContext ) stagedStringType (key string ) (redisValueType , bool ) {
1568- tv , ok := t .working [key ]
1595+ tv , ok := t .working [string ( redisStrKey ([] byte ( key ))) ]
15691596 if ! ok {
15701597 return redisTypeNone , false
15711598 }
@@ -1811,6 +1838,16 @@ func (t *txnContext) stageKeyDeletion(key []byte) (redisResult, error) {
18111838 iv .deleted = true
18121839 iv .dirty = true
18131840 }
1841+ // Mark legacy bare string key for deletion. We bypass load() here
1842+ // because load() auto-prefixes bare keys to !redis|str|.
1843+ // Track the bare key in the read set for conflict detection.
1844+ t .trackReadKey (key )
1845+ bareK := string (key )
1846+ if _ , ok := t .working [bareK ]; ! ok {
1847+ t .working [bareK ] = & txnValue {}
1848+ }
1849+ t .working [bareK ].deleted = true
1850+ t .working [bareK ].dirty = true
18141851 return redisResult {typ : resultInt , integer : 1 }, nil
18151852}
18161853
@@ -1908,12 +1945,12 @@ func (t *txnContext) buildKeyElems() []*kv.Elem[kv.OP] {
19081945 if ! tv .dirty {
19091946 continue
19101947 }
1911- key := []byte (k )
1948+ storageKey := []byte (k )
19121949 if tv .deleted {
1913- elems = append (elems , & kv.Elem [kv.OP ]{Op : kv .Del , Key : key })
1950+ elems = append (elems , & kv.Elem [kv.OP ]{Op : kv .Del , Key : storageKey })
19141951 continue
19151952 }
1916- elems = append (elems , & kv.Elem [kv.OP ]{Op : kv .Put , Key : key , Value : tv .raw })
1953+ elems = append (elems , & kv.Elem [kv.OP ]{Op : kv .Put , Key : storageKey , Value : tv .raw })
19171954 }
19181955 return elems
19191956}
0 commit comments