Add cache test for admins (#31265)

Add a test to probe the cache similar to the email test func.


![image](https://github.com/go-gitea/gitea/assets/24977596/700e2733-586d-4091-900f-f5f71e6e94bf)


![image](https://github.com/go-gitea/gitea/assets/24977596/2a953802-18fc-4e81-a37d-24ebe1297365)


![image](https://github.com/go-gitea/gitea/assets/24977596/e00d62ad-bb60-41cc-9138-09993daee156)

---------

Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
6543 2024-06-17 21:22:39 +02:00 committed by GitHub
parent 4b6eb46e69
commit 363c123598
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 119 additions and 27 deletions

View File

@ -4,6 +4,7 @@
package cache package cache
import ( import (
"fmt"
"strconv" "strconv"
"time" "time"
@ -35,6 +36,37 @@ func Init() error {
return nil return nil
} }
const (
testCacheKey = "DefaultCache.TestKey"
SlowCacheThreshold = 100 * time.Microsecond
)
func Test() (time.Duration, error) {
if defaultCache == nil {
return 0, fmt.Errorf("default cache not initialized")
}
testData := fmt.Sprintf("%x", make([]byte, 500))
start := time.Now()
if err := defaultCache.Delete(testCacheKey); err != nil {
return 0, fmt.Errorf("expect cache to delete data based on key if exist but got: %w", err)
}
if err := defaultCache.Put(testCacheKey, testData, 10); err != nil {
return 0, fmt.Errorf("expect cache to store data but got: %w", err)
}
testVal, hit := defaultCache.Get(testCacheKey)
if !hit {
return 0, fmt.Errorf("expect cache hit but got none")
}
if testVal != testData {
return 0, fmt.Errorf("expect cache to return same value as stored but got other")
}
return time.Since(start), nil
}
// GetCache returns the currently configured cache // GetCache returns the currently configured cache
func GetCache() StringCache { func GetCache() StringCache {
return defaultCache return defaultCache

View File

@ -34,6 +34,18 @@ func TestNewContext(t *testing.T) {
assert.Nil(t, con) assert.Nil(t, con)
} }
func TestTest(t *testing.T) {
defaultCache = nil
_, err := Test()
assert.Error(t, err)
createTestCache()
elapsed, err := Test()
assert.NoError(t, err)
// mem cache should take from 300ns up to 1ms on modern hardware ...
assert.Less(t, elapsed, SlowCacheThreshold)
}
func TestGetCache(t *testing.T) { func TestGetCache(t *testing.T) {
createTestCache() createTestCache()

View File

@ -93,6 +93,7 @@ remove_all = Remove All
remove_label_str = Remove item "%s" remove_label_str = Remove item "%s"
edit = Edit edit = Edit
view = View view = View
test = Test
enabled = Enabled enabled = Enabled
disabled = Disabled disabled = Disabled
@ -3225,6 +3226,10 @@ config.cache_adapter = Cache Adapter
config.cache_interval = Cache Interval config.cache_interval = Cache Interval
config.cache_conn = Cache Connection config.cache_conn = Cache Connection
config.cache_item_ttl = Cache Item TTL config.cache_item_ttl = Cache Item TTL
config.cache_test = Test Cache
config.cache_test_failed = Failed to probe the cache: %v.
config.cache_test_slow = Cache test successful, but response is slow: %s.
config.cache_test_succeeded = Cache test successful, got a response in %s.
config.session_config = Session Configuration config.session_config = Session Configuration
config.session_provider = Session Provider config.session_provider = Session Provider

View File

@ -15,6 +15,7 @@ import (
activities_model "code.gitea.io/gitea/models/activities" activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/json"
@ -222,6 +223,14 @@ func SelfCheck(ctx *context.Context) {
ctx.Data["DatabaseCheckHasProblems"] = hasProblem ctx.Data["DatabaseCheckHasProblems"] = hasProblem
} }
elapsed, err := cache.Test()
if err != nil {
ctx.Data["CacheError"] = err
} else if elapsed > cache.SlowCacheThreshold {
ctx.Data["CacheSlow"] = fmt.Sprint(elapsed)
}
ctx.HTML(http.StatusOK, tplSelfCheck) ctx.HTML(http.StatusOK, tplSelfCheck)
} }

View File

@ -12,6 +12,7 @@ import (
system_model "code.gitea.io/gitea/models/system" system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
@ -42,6 +43,22 @@ func SendTestMail(ctx *context.Context) {
ctx.Redirect(setting.AppSubURL + "/admin/config") ctx.Redirect(setting.AppSubURL + "/admin/config")
} }
// TestCache test the cache settings
func TestCache(ctx *context.Context) {
elapsed, err := cache.Test()
if err != nil {
ctx.Flash.Error(ctx.Tr("admin.config.cache_test_failed", err))
} else {
if elapsed > cache.SlowCacheThreshold {
ctx.Flash.Warning(ctx.Tr("admin.config.cache_test_slow", elapsed))
} else {
ctx.Flash.Info(ctx.Tr("admin.config.cache_test_succeeded", elapsed))
}
}
ctx.Redirect(setting.AppSubURL + "/admin/config")
}
func shadowPasswordKV(cfgItem, splitter string) string { func shadowPasswordKV(cfgItem, splitter string) string {
fields := strings.Split(cfgItem, splitter) fields := strings.Split(cfgItem, splitter)
for i := 0; i < len(fields); i++ { for i := 0; i < len(fields); i++ {

View File

@ -692,6 +692,7 @@ func registerRoutes(m *web.Route) {
m.Get("", admin.Config) m.Get("", admin.Config)
m.Post("", admin.ChangeConfig) m.Post("", admin.ChangeConfig)
m.Post("/test_mail", admin.SendTestMail) m.Post("/test_mail", admin.SendTestMail)
m.Post("/test_cache", admin.TestCache)
m.Get("/settings", admin.ConfigSettings) m.Get("/settings", admin.ConfigSettings)
}) })

View File

@ -229,8 +229,8 @@
<dt>{{ctx.Locale.Tr "admin.config.mailer_user"}}</dt> <dt>{{ctx.Locale.Tr "admin.config.mailer_user"}}</dt>
<dd>{{if .Mailer.User}}{{.Mailer.User}}{{else}}(empty){{end}}</dd> <dd>{{if .Mailer.User}}{{.Mailer.User}}{{else}}(empty){{end}}</dd>
<div class="divider"></div> <div class="divider"></div>
<dt class="tw-py-1">{{ctx.Locale.Tr "admin.config.send_test_mail"}}</dt> <dt class="tw-py-1 tw-flex tw-items-center">{{ctx.Locale.Tr "admin.config.send_test_mail"}}</dt>
<dd> <dd class="tw-py-0">
<form class="ui form ignore-dirty" action="{{AppSubUrl}}/admin/config/test_mail" method="post"> <form class="ui form ignore-dirty" action="{{AppSubUrl}}/admin/config/test_mail" method="post">
{{.CsrfTokenHtml}} {{.CsrfTokenHtml}}
<div class="ui tiny input"> <div class="ui tiny input">
@ -260,6 +260,14 @@
<dt>{{ctx.Locale.Tr "admin.config.cache_item_ttl"}}</dt> <dt>{{ctx.Locale.Tr "admin.config.cache_item_ttl"}}</dt>
<dd><code>{{.CacheItemTTL}}</code></dd> <dd><code>{{.CacheItemTTL}}</code></dd>
{{end}} {{end}}
<div class="divider"></div>
<dt class="tw-py-1 tw-flex tw-items-center">{{ctx.Locale.Tr "admin.config.cache_test"}}</dt>
<dd class="tw-py-0">
<form class="ui form ignore-dirty" action="{{AppSubUrl}}/admin/config/test_cache" method="post">
{{.CsrfTokenHtml}}
<button class="ui tiny primary button">{{ctx.Locale.Tr "test"}}</button>
</form>
</dd>
</dl> </dl>
</div> </div>

View File

@ -17,32 +17,40 @@
<div class="ui attached segment tw-hidden self-check-problem" id="self-check-by-frontend"></div> <div class="ui attached segment tw-hidden self-check-problem" id="self-check-by-frontend"></div>
{{if .DatabaseCheckHasProblems}} {{if .DatabaseCheckHasProblems}}
<div class="ui attached segment self-check-problem"> <div class="ui attached segment self-check-problem">
{{if .DatabaseType.IsMySQL}} {{if .DatabaseType.IsMySQL}}
<div class="tw-p-2">{{ctx.Locale.Tr "admin.self_check.database_fix_mysql"}}</div> <div class="tw-p-2">{{ctx.Locale.Tr "admin.self_check.database_fix_mysql"}}</div>
{{else if .DatabaseType.IsMSSQL}} {{else if .DatabaseType.IsMSSQL}}
<div class="tw-p-2">{{ctx.Locale.Tr "admin.self_check.database_fix_mssql"}}</div> <div class="tw-p-2">{{ctx.Locale.Tr "admin.self_check.database_fix_mssql"}}</div>
{{end}} {{end}}
{{if .DatabaseCheckCollationMismatch}} {{if .DatabaseCheckCollationMismatch}}
<div class="ui red message">{{ctx.Locale.Tr "admin.self_check.database_collation_mismatch" .DatabaseCheckResult.ExpectedCollation}}</div> <div class="ui red message">{{ctx.Locale.Tr "admin.self_check.database_collation_mismatch" .DatabaseCheckResult.ExpectedCollation}}</div>
{{end}} {{end}}
{{if .DatabaseCheckCollationCaseInsensitive}} {{if .DatabaseCheckCollationCaseInsensitive}}
<div class="ui warning message">{{ctx.Locale.Tr "admin.self_check.database_collation_case_insensitive" .DatabaseCheckResult.DatabaseCollation}}</div> <div class="ui warning message">{{ctx.Locale.Tr "admin.self_check.database_collation_case_insensitive" .DatabaseCheckResult.DatabaseCollation}}</div>
{{end}} {{end}}
{{if .DatabaseCheckInconsistentCollationColumns}} {{if .DatabaseCheckInconsistentCollationColumns}}
<div class="ui red message"> <div class="ui red message">
<details> <details>
<summary>{{ctx.Locale.Tr "admin.self_check.database_inconsistent_collation_columns" .DatabaseCheckResult.DatabaseCollation}}</summary> <summary>{{ctx.Locale.Tr "admin.self_check.database_inconsistent_collation_columns" .DatabaseCheckResult.DatabaseCollation}}</summary>
<ul class="tw-w-full"> <ul class="tw-w-full">
{{range .DatabaseCheckInconsistentCollationColumns}} {{range .DatabaseCheckInconsistentCollationColumns}}
<li>{{.}}</li> <li>{{.}}</li>
{{end}} {{end}}
</ul> </ul>
</details> </details>
</div> </div>
{{end}} {{end}}
</div> </div>
{{end}} {{end}}
{{if .CacheError}}
<div class="ui red message">{{ctx.Locale.Tr "admin.config.cache_test_failed" .CacheError}}</div>
{{end}}
{{if .CacheSlow}}
<div class="ui warning message">{{ctx.Locale.Tr "admin.config.cache_test_slow" .CacheSlow}}</div>
{{end}}
{{/* only shown when there is no visible "self-check-problem" */}} {{/* only shown when there is no visible "self-check-problem" */}}
<div class="ui attached segment tw-hidden self-check-no-problem"> <div class="ui attached segment tw-hidden self-check-no-problem">
{{ctx.Locale.Tr "admin.self_check.no_problem_found"}} {{ctx.Locale.Tr "admin.self_check.no_problem_found"}}