[discuss]Abstract Cache Operations for Better Extensibility #81

Open
opened 2026-02-16 00:19:39 -05:00 by yindo · 2 comments
Owner

Originally created by @AkisAya on GitHub (Apr 9, 2025).

now cahce related operations are tightly coupled with Redis(single instance ), we can abstract the operation make cache extensible(memory. redis cluster and so on)

possible abstraction can be like this

type Cache interface {
	Close() error

	Store(key string, value any, expire time.Duration) error

	store(key string, value any, expire time.Duration) error

	Get(key string) ([]byte, error)

	get(key string) ([]byte, error)

	GetString(key string) (string, error)

	Del(key string) error

	del(key string) error

	Exist(key string) (int64, error)

	Increase(key string) (int64, error)

	Decrease(key string) (int64, error)

	SetExpire(key string, expire time.Duration) error

	SetMapField(key string, v map[string]any) error
	SetMapOneField(key string, field string, value any) error
	GetMapField(key string, field string) ([]byte, error)
	GetMapFieldString(key string, field string) (string, error)
	DelMapField(key string, field string) error
	GetMap(key string) (map[string]string, error)

	ScanKeys(match string) ([]string, error)
	ScanKeysAsync(match string, fn func([]string) error) error
	ScanMap(key string, match string) (map[string]string, error)
	ScanMapAsync(key string, match string, fn func(map[string]string) error) error

	SetNX(key string, value any, expire time.Duration) (bool, error)

	Lock(key string, expire time.Duration, tryLockTimeout time.Duration) error
	Unlock(key string) error
	Expire(key string, expire time.Duration) (bool, error)

	Transaction(fn func(txp TxPipe) error) error

	Publish(channel string, message any) error
	Subscribe(channel string) (<-chan []byte, func())
}

type TxPipe interface {
	Store(key string, value any, expire time.Duration) error
	SetNX(key string, value any, expire time.Duration) (bool, error)
}

Cache interface for cache operations and TxPipe for Transation releatd cache ops.

or maybe split Cache to BasicCache / PubSub / Lock interfaces and TxPipe is BasicCache

and since Interface methods doesn't support generic mehods, so we need provide a wrapper to do generic things. like below

func Get[T any](key string) (*T, error) {
	return get[T](serialKey(key))
}

func get[T any](key string) (*T, error) {
	if cacheInstance == nil {
		return nil, ErrDBNotInit
	}

	// Get raw value as bytes from the cache
	bytes, err := cacheInstance.get(key)
	if err != nil {
		return nil, err
	}

	// Unmarshal bytes to the requested type
	result, err := parser.UnmarshalCBOR[T](bytes)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

Originally created by @AkisAya on GitHub (Apr 9, 2025). now cahce related operations are tightly coupled with Redis(single instance ), we can abstract the operation make cache extensible(memory. redis cluster and so on) possible abstraction can be like this ```go type Cache interface { Close() error Store(key string, value any, expire time.Duration) error store(key string, value any, expire time.Duration) error Get(key string) ([]byte, error) get(key string) ([]byte, error) GetString(key string) (string, error) Del(key string) error del(key string) error Exist(key string) (int64, error) Increase(key string) (int64, error) Decrease(key string) (int64, error) SetExpire(key string, expire time.Duration) error SetMapField(key string, v map[string]any) error SetMapOneField(key string, field string, value any) error GetMapField(key string, field string) ([]byte, error) GetMapFieldString(key string, field string) (string, error) DelMapField(key string, field string) error GetMap(key string) (map[string]string, error) ScanKeys(match string) ([]string, error) ScanKeysAsync(match string, fn func([]string) error) error ScanMap(key string, match string) (map[string]string, error) ScanMapAsync(key string, match string, fn func(map[string]string) error) error SetNX(key string, value any, expire time.Duration) (bool, error) Lock(key string, expire time.Duration, tryLockTimeout time.Duration) error Unlock(key string) error Expire(key string, expire time.Duration) (bool, error) Transaction(fn func(txp TxPipe) error) error Publish(channel string, message any) error Subscribe(channel string) (<-chan []byte, func()) } type TxPipe interface { Store(key string, value any, expire time.Duration) error SetNX(key string, value any, expire time.Duration) (bool, error) } ``` `Cache` interface for cache operations and `TxPipe` for Transation releatd cache ops. or maybe split `Cache` to `BasicCache / PubSub / Lock ` interfaces and `TxPipe` is `BasicCache` and since Interface methods doesn't support generic mehods, so we need provide a wrapper to do generic things. like below ```go func Get[T any](key string) (*T, error) { return get[T](serialKey(key)) } func get[T any](key string) (*T, error) { if cacheInstance == nil { return nil, ErrDBNotInit } // Get raw value as bytes from the cache bytes, err := cacheInstance.get(key) if err != nil { return nil, err } // Unmarshal bytes to the requested type result, err := parser.UnmarshalCBOR[T](bytes) if err != nil { return nil, err } return &result, nil } ```
Author
Owner

@AkisAya commented on GitHub (Apr 9, 2025):

related issues, #147
basically, this related issue needs to deal how to create a sentinel redis client and much effort is needed to deal with pubsub implemention correctly

@AkisAya commented on GitHub (Apr 9, 2025): related issues, #147 basically, this related issue needs to deal how to create a sentinel redis client and much effort is needed to deal with pubsub implemention correctly
Author
Owner

@Yeuoly commented on GitHub (Apr 10, 2025):

It's reasonable to make it friendly to extensibility, but just like current storage layer, it's different to manage it's robust, especially it's different to do the tests in CI pipeline

For the pubsub part, I agree with you, splitting them into serval interfaces is good idea

@Yeuoly commented on GitHub (Apr 10, 2025): It's reasonable to make it friendly to extensibility, but just like current storage layer, it's different to manage it's robust, especially it's different to do the tests in CI pipeline For the pubsub part, I agree with you, splitting them into serval interfaces is good idea
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langgenius/dify-plugin-daemon#81