Skip to content

Commit 41e730c

Browse files
committed
feat: support data preload
1 parent bf5f09c commit 41e730c

13 files changed

Lines changed: 204 additions & 61 deletions

File tree

.todo

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@
1515
[x] slog secretName -> secret_name
1616
[x] memory db 支持制定目录,默认 /tmp/xxx.db
1717
[x] 添加 manifests
18+
[x] 支持 WebSocket
1819
[] 支持消息队列(kafka)
19-
[] 支持 WebSocket

internal/osbuilder/helper/helper.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,11 @@ func RenderTemplate(fm *file.FileManager, pairs map[string]string, funcs templat
441441
return fmt.Errorf("parse template %q for %q: %w", tplPath, dstPath, err)
442442
}
443443

444+
tmpl, err = tmpl.ParseFiles("/home/colin/workspace/golang/src/github.com/onexstack/osbuilder/internal/osbuilder/tpl/project/configs/mb-apiserver.yaml")
445+
if err != nil {
446+
return err
447+
}
448+
444449
// Execute template
445450
var buf bytes.Buffer
446451
if err = tmpl.Execute(&buf, data); err != nil {

internal/osbuilder/statik/statik.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/osbuilder/tpl/project.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ webServers:
7373
serviceRegistry: none
7474
# 只有 webFramework 为 gin 时生效。gRPC 框架实现类似 websocket 的效果可使用 gRPC 流式通信方式
7575
withWS: true
76+
withPreloader: true # 是否开启数据预加载代码示例
7677
clients:
7778
- fake
7879
- oss

internal/osbuilder/tpl/project/README.md

Lines changed: 1 addition & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -154,58 +154,7 @@ $ docker run --name {{.BinaryName}} -v configs/{{.BinaryName}}.yaml:/etc/{{.Bina
154154
{{.BinaryName}} 配置文件 `configs/{{.BinaryName}}.yaml`
155155
156156
```yaml
157-
{{- if eq .WebFramework "gin" }}
158-
addr: 0.0.0.0:5555 # 服务监听地址
159-
timeout: 30s # 服务端超时
160-
{{- end -}}
161-
{{- if eq .WebFramework "grpc" }}
162-
{{- if .WithOTel }}
163-
metrics-addr: 0.0.0.0:29090
164-
{{- end -}}
165-
grpc:
166-
port: 6666
167-
timeout: 30s
168-
{{- end -}}
169-
{{- if not .WithOTel }}
170-
slog:
171-
level: info # debug, info, warn, error
172-
add-source: true
173-
format: json # console, json
174-
time-format: "2006-01-02 15:04:05"
175-
output: stdout
176-
{{- end -}}
177-
{{- if .WithOTel }}
178-
otel:
179-
endpoint: 127.0.0.1:4327
180-
service-name: {{.BinaryName}}
181-
output-mode: otel
182-
level: debug
183-
add-source: true
184-
use-prometheus-endpoint: true
185-
slog: # 改配置项只有 output-mod 为 slog 时生效
186-
format: text
187-
time-format: "2006-01-02 15:04:05"
188-
output: stdout
189-
{{- end -}}
190-
{{- if eq .ServiceRegistry "polaris" }}
191-
polaris:
192-
addr: 127.0.0.1:8091
193-
timeout: 30s
194-
retry-count: 3
195-
provider:
196-
namespace: {{$.D.ProjectName}}
197-
service: {{.BinaryName}}
198-
{{- end -}}
199-
{{- if or (eq .StorageType "mysql") (eq .StorageType "mariadb") }}
200-
mysql:
201-
addr: 127.0.0.1:3306
202-
username: onex
203-
password: "onex(#)666"
204-
database: onex
205-
max-connection-life-time: 10s
206-
max-idle-connections: 100
207-
max-open-connections: 100
208-
{{- end}}
157+
{{ template "mb-apiserver-config-body" .}}
209158
```
210159
{{- end }}
211160

internal/osbuilder/tpl/project/configs/mb-apiserver.yaml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
{{- with .Web }}
2-
{{- if .WithUser }}
1+
{{- /* 使用 block 定义模版主体,同时作为单独渲染的入口 */ -}}
2+
{{- /* 单独渲染时:上下文是 TemplateData,我们将 .Web 传给 block */ -}}
3+
{{- /* 被引用时:README 会显式调用这个名字,并传入具体的 WebServer 对象 */ -}}
4+
{{- block "mb-apiserver-config-body" .Web -}}
5+
{{- if .WithUser -}}
36
jwt-key: xxxxxxxxxxxx
47
{{- end}}
58
{{- if eq .WebFramework "gin" }}
@@ -78,4 +81,4 @@ mysql:
7881
# 访问远端服务时的超时时间
7982
timeout: 30s
8083
{{- end -}}
81-
{{end -}}
84+
{{- end -}}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
go.mod.dev
1+
go.mod.prod
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package asyncstore
2+
3+
import (
4+
"context"
5+
"sync"
6+
"time"
7+
8+
"github.com/google/wire"
9+
retryutil "github.com/onexstack/onexstack/pkg/util/retry"
10+
)
11+
12+
var ProviderSet = wire.NewSet(NewStore, wire.Bind(new(Factory), new(*store)))
13+
14+
// Factory defines the unified interface for retrieving various stores.
15+
type Factory interface {
16+
Fake() *FakeStore
17+
}
18+
19+
// store is the concrete implementation of the Factory interface.
20+
type store struct {
21+
fakeStore *FakeStore
22+
}
23+
24+
var (
25+
once sync.Once
26+
// S is a global variable for convenient access to the initialized datasyncstore
27+
// instance from other packages.
28+
S *store
29+
)
30+
31+
// NewStore initializes and returns the Store Factory interface.
32+
func NewStore(ctx context.Context, refreshInterval time.Duration) Factory {
33+
// Enable fake data async store
34+
fakeStore := NewFakeStore()
35+
_ = retryutil.RunImmediatelyThenPeriod(ctx, fakeStore.Sync, refreshInterval)
36+
37+
// Initialize the singleton datasyncstore instance only once.
38+
once.Do(func() {
39+
S = &store{fakeStore: fakeStore}
40+
})
41+
42+
return S
43+
}
44+
45+
// Fake returns the FakeStore instance.
46+
func (s *store) Fake() *FakeStore {
47+
return s.fakeStore
48+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package asyncstore
2+
3+
import (
4+
"context"
5+
"sync"
6+
7+
"github.com/brianvoe/gofakeit/v7"
8+
9+
v1 "github.com/onexstack/b-dms/pkg/api/apiserver/v1"
10+
)
11+
12+
// FakeStore implements the storage mechanism for FakeData.
13+
type FakeStore struct {
14+
// Use RWMutex to allow concurrent reads.
15+
mu sync.RWMutex
16+
items map[string]*v1.FakeData
17+
}
18+
19+
// NewFakeStore creates a new instance of FakeStore.
20+
func NewFakeStore() *FakeStore {
21+
return &FakeStore{
22+
items: make(map[string]*v1.FakeData),
23+
}
24+
}
25+
26+
// Sync simulates data synchronization by generating random fake data.
27+
func (s *FakeStore) Sync(ctx context.Context) error {
28+
s.mu.Lock()
29+
defer s.mu.Unlock()
30+
31+
// Simulate generating 10 random items.
32+
newItems := make(map[string]*v1.FakeData)
33+
for i := 0; i < 10; i++ {
34+
// Generate a random UUID as the key
35+
id := gofakeit.UUID()
36+
37+
newItems[id] = &v1.FakeData{
38+
ID: id,
39+
Name: gofakeit.AppName(),
40+
Category: gofakeit.CarMaker(),
41+
Description: gofakeit.Phrase(),
42+
Status: gofakeit.RandomString([]string{"active", "pending", "failed"}),
43+
Score: gofakeit.Float64Range(0.0, 100.0),
44+
}
45+
}
46+
47+
// Example: Add a fixed item for deterministic testing.
48+
fixedID := "fixed-item-001"
49+
newItems[fixedID] = &v1.FakeData{
50+
ID: fixedID,
51+
Name: "Fixed Test Item",
52+
Category: "Testing",
53+
Description: "This item always exists.",
54+
Status: "active",
55+
Score: 99.9,
56+
}
57+
58+
// Atomically replace the entire map.
59+
s.items = newItems
60+
61+
return nil
62+
}
63+
64+
// Get retrieves an item by its ID.
65+
// It returns the item pointer and a boolean indicating if the item was found.
66+
func (s *FakeStore) Get(id string) (*v1.FakeData, bool) {
67+
s.mu.RLock() // Acquire read lock
68+
defer s.mu.RUnlock()
69+
70+
item, ok := s.items[id]
71+
return item, ok
72+
}
73+
74+
// List retrieves all items.
75+
func (s *FakeStore) List() []*v1.FakeData {
76+
s.mu.RLock()
77+
defer s.mu.RUnlock()
78+
79+
list := make([]*v1.FakeData, 0, len(s.items))
80+
for _, item := range s.items {
81+
list = append(list, item)
82+
}
83+
return list
84+
}

internal/osbuilder/tpl/project/internal/apiserver/server.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ import (
2020

2121
"{{.D.ModuleName}}/internal/{{.Web.Name}}/biz"
2222
"{{.D.ModuleName}}/internal/{{.Web.Name}}/pkg/validation"
23+
{{- if .Web.WithPreloader}}
24+
"{{.D.ModuleName}}/internal/{{.Web.Name}}/pkg/asyncstore"
25+
{{- end}}
2326
{{- if .Web.WithUser}}
2427
"{{.D.ModuleName}}/internal/pkg/contextx"
2528
"{{.D.ModuleName}}/internal/pkg/known"
@@ -36,6 +39,9 @@ import (
3639
{{- end}}
3740
)
3841

42+
// Dependencies collects all components that need initialization but are not directly used.
43+
type Dependencies struct{}
44+
3945
// Config contains application-related configurations.
4046
type Config struct {
4147
{{- if .Web.WithUser}}
@@ -84,6 +90,7 @@ type ServerConfig struct {
8490
retriever mw.UserRetriever
8591
authz *authz.Authz
8692
{{- end}}
93+
deps *Dependencies
8794
}
8895

8996
// NewServer initializes and returns a new Server instance.
@@ -98,7 +105,7 @@ func (cfg *Config) NewServer(ctx context.Context) (*Server, error) {
98105

99106
{{- end}}
100107
// Create the core server instance.
101-
return NewServer(cfg)
108+
return NewServer(ctx, cfg)
102109
}
103110

104111
// Run starts the server and listens for termination signals.
@@ -189,6 +196,21 @@ func Provide{{. | kind}}Client(cfg *Config) {{. | lowerkind}}.Interface {
189196
}
190197
{{- end}}
191198

199+
{{- if .Web.WithPreloader}}
200+
func ProvideAStore(ctx context.Context) asyncstore.Factory {
201+
return asyncstore.NewStore(ctx, 30*time.Minute)
202+
}
203+
{{- end}}
204+
205+
// NewDependencies initializes all components that need to be started but not directly stored.
206+
func NewDependencies(ctx context.Context{{- if .Web.WithPreloader }}, _ asyncstore.Factory{{- end -}}) *Dependencies {
207+
{{- if .Web.WithPreloader}}
208+
fake, _ := asyncstore.S.Fake().Get("fixed-item-001")
209+
slog.DebugContext(ctx, "Successfully retrieved fake cache data", "data", fake.String())
210+
{{- end}}
211+
return &Dependencies{}
212+
}
213+
192214
// NewWebServer creates and returns a new web server instance using the provided server configuration.
193215
func NewWebServer(serverConfig *ServerConfig) (server.Server, error) {
194216
{{- if or (eq .Web.WebFramework "grpc") (eq .Web.WebFramework "grpc-gateway")}}

0 commit comments

Comments
 (0)