Repository: gorse-io/gorse

Issue:
使用快速上手的docker-compose.yaml部署服务导入数据后后台日志报错
导入github.sql数据到mysql后
使用docker compose logs master ,报错信息如下

{"level":"error","ts":1763626746.303389,"msg":"failed to load ranking dataset","error":"json: cannot unmarshal object into Go value of type []string","errorVerbose":"json: cannot unmarshal object into Go value of type []string\ngithub.com/zhenghaoz/gorse/storage/data.(*SQLDatabase).GetItemStream.func1:534: \ngithub.com/zhenghaoz/gorse/master.(*Master).LoadDataFromDatabase:1525: \ngithub.com/zhenghaoz/gorse/master.(*Master).runLoadDatasetTask:85: "}

<img width="3174" height="1162" alt="Image" src="https://github.com/user-attachments/assets/ac404a67-ec8a-4f21-b25e-b76f169470d4" />

Hints from issue comments:
@zhenghaoz: 把Docker镜像版本改成nightly 试试@ygsun: 可以了，这个nightly算是0.5的还是0.4的版本？@Issues-translate-bot:  
> Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑‍🤝‍🧑👫🧑🏿‍🤝‍🧑🏻👩🏾‍🤝‍👨🏿👬🏿         
----    

Okay, is this nightly version 0.5 or 0.4?  
      @zhenghaoz: 目前在进行0.5发布的一些收尾工作，很多材料会被修复@Issues-translate-bot:  
> Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑‍🤝‍🧑👫🧑🏿‍🤝‍🧑🏻👩🏾‍🤝‍👨🏿👬🏿         
----    

We are currently undergoing some finishing work for the 0.5 release, and many materials will be repaired.  
      @ygsun: goroutine 1 [running]:
worker-1  | github.com/gorse-io/gorse/storage/cache.decodeCategory({0x0?, 0x0?})
worker-1  |     /src/storage/cache/redis.go:453 +0x88
worker-1  | github.com/gorse-io/gorse/storage/cache.decodeCategories({0x0?, 0x1?})
worker-1  |     /src/storage/cache/redis.go:470 +0x91
worker-1  | github.com/gorse-io/gorse/storage/cache.(*Redis).SearchScores(0xc0002f73e0, {0x3e01e28, 0x57f6940}, {0x3b10f7d?, 0x425697?}, {0xc00c14bf38, 0x8}, {0x0, 0x0, 0x0}, ...)
worker-1  |     /src/storage/cache/redis.go:261 +0x82e
worker-1  | github.com/gorse-io/gorse/logics.(*Recommender).recommendCollaborative(0xc00af3b0e0, {0x3e01e28, 0x57f6940})
worker-1  |     /src/logics/recommend.go:209 +0x94
worker-1  | github.com/gorse-io/gorse/logics.(*Recommender).RecommendSequential(0xc00af3b0e0, {0x3e01e28, 0x57f6940}, {0x0?, 0x10?, 0x3e23c40?}, 0x0, {0xc000251500, 0x5, 0x8})
worker-1  |     /src/logics/recommend.go:126 +0x122
worker-1  | github.com/gorse-io/gorse/worker.(*Pipeline).Recommend.func2(0x38a55e0?, 0x56a1470?)
worker-1  |     /src/worker/pipeline.go:149 +0x685
worker-1  | github.com/gorse-io/gorse/common/parallel.Parallel(0xaf7, 0xc00d7762e0?, 0xc000223540)
worker-1  |     /src/common/parallel/parallel.go:39 +0xeb
worker-1  | github.com/gorse-io/gorse/worker.(*Pipeline).Recommend(0xc0006028c0, {0xc00d800000, 0xaf7, 0xcaa}, 0xc00d7762b0)
worker-1  |     /src/worker/pipeline.go:101 +0x5d9
worker-1  | github.com/gorse-io/gorse/worker.(*Worker).Serve.func1()
worker-1  |     /src/worker/worker.go:371 +0x3a5
worker-1  | github.com/gorse-io/gorse/worker.(*Worker).Serve(0xc0006028c0)
worker-1  |     /src/worker/worker.go:390 +0xa1c
worker-1  | main.init.func1(0x56c5be0, {0x3987a44?, 0x4?, 0x3987a08?})
worker-1  |     /src/cmd/gorse-worker/main.go:67 +0x7de
worker-1  | github.com/spf13/cobra.(*Command).execute(0x56c5be0, {0xc0001c60e0, 0xc, 0xc})
worker-1  |     /go/pkg/mod/github.com/spf13/cobra@v1.10.1/command.go:1019 +0xae7
worker-1  | github.com/spf13/cobra.(*Command).ExecuteC(0x56c5be0)
worker-1  |     /go/pkg/mod/github.com/spf13/cobra@v1.10.1/command.go:1148 +0x465
worker-1  | github.com/spf13/cobra.(*Command).Execute(...)
worker-1  |     /go/pkg/mod/github.com/spf13/cobra@v1.10.1/command.go:1071
worker-1  | main.main()
worker-1  |     /src/cmd/gorse-worker/main.go:87 +0x26
worker-1  | {"level":"info","ts":1763630917.7577186,"msg":"start meta sync","meta_timeout":10}
worker-1  | {"level":"info","ts":1763630917.770957,"msg":"connect data store","database":"mysql://xxxxx:xxxxxxxxxx@tcp(mysql:3306)/gorse"}
worker-1  | {"level":"info","ts":1763630917.7736235,"msg":"connect cache store","database":"redis://:@redis:6379"}
worker-1  | {"level":"info","ts":1763630917.7736812,"msg":"connect blob store via master"}
worker-1  | {"level":"info","ts":1763630917.7736845,"msg":"new ranking model found","old_version":0,"new_version":1763630739}
worker-1  | {"level":"info","ts":1763630917.7736897,"msg":"new click model found","old_version":0,"new_version":1763630780}
worker-1  | {"level":"info","ts":1763630917.7737029,"msg":"start pull collaborative filtering model"}
worker-1  | {"level":"info","ts":1763630918.6410842,"msg":"synced collaborative filtering model","id":1763630739}
worker-1  | {"level":"info","ts":1763630918.641145,"msg":"start pull click model"}
worker-1  | {"level":"info","ts":1763630918.7284646,"msg":"synced click-through rate model","version":1763630780}
worker-1  | {"level":"info","ts":1763630918.7325764,"msg":"ranking recommendation","n_working_users":2807,"n_jobs":1,"cache_size":100}
worker-1  | panic: runtime error: slice bounds out of range [1:0]


worker组件不定时闪退@zhenghaoz: 可能是一个bug，你把cache store切换到MySQL试试@Issues-translate-bot:  
> Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑‍🤝‍🧑👫🧑🏿‍🤝‍🧑🏻👩🏾‍🤝‍👨🏿👬🏿         
----    

It may be a bug. Try switching the cache store to MySQL.  
      @ygsun: 老样子呢，我想问下有没有0.4.16版本的示例数据下载呢？@Issues-translate-bot:  
> Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑‍🤝‍🧑👫🧑🏿‍🤝‍🧑🏻👩🏾‍🤝‍👨🏿👬🏿         
----    

As usual, I would like to ask if there is any sample data download for version 0.4.16?  
      

Solution patch:
diff --git a/storage/cache/redis.go b/storage/cache/redis.go
index 1adb81b..046607c 100644
--- a/storage/cache/redis.go
+++ b/storage/cache/redis.go
@@ -180,8 +180,6 @@ func (r *Redis) Delete(ctx context.Context, key string) error {
 	return r.client.Del(ctx, r.Key(key)).Err()
 }
 
-
-
 func (r *Redis) Push(ctx context.Context, name string, message string) error {
 	_, err := r.client.ZAdd(ctx, r.Key(name), redis.Z{Member: message, Score: float64(time.Now().UnixNano())}).Result()
 	return err
@@ -465,6 +463,9 @@ func encodeCategories(categories []string) string {
 }
 
 func decodeCategories(s string) ([]string, error) {
+	if s == "" {
+		return []string{}, nil
+	}
 	var categories []string
 	for _, category := range strings.Split(s, ";") {
 		category, err := decodeCategory(category)



Test patch:
diff --git a/storage/cache/redis_test.go b/storage/cache/redis_test.go
index 03a2345..020f323 100644
--- a/storage/cache/redis_test.go
+++ b/storage/cache/redis_test.go
@@ -110,6 +110,18 @@ func TestRedis(t *testing.T) {
 	suite.Run(t, new(RedisTestSuite))
 }
 
+func TestEncodeDecodeCategories(t *testing.T) {
+	encoded := encodeCategories([]string{"z", "h"})
+	decoded, err := decodeCategories(encoded)
+	assert.NoError(t, err)
+	assert.Equal(t, []string{"z", "h"}, decoded)
+
+	encoded = encodeCategories(nil)
+	decoded, err = decodeCategories(encoded)
+	assert.NoError(t, err)
+	assert.Equal(t, []string{}, decoded)
+}
+
 func BenchmarkRedis(b *testing.B) {
 	log.CloseLogger()
 	// open db