diff options
-rw-r--r-- | modules/cache/cache.go | 32 | ||||
-rw-r--r-- | modules/cache/cache_test.go | 12 | ||||
-rw-r--r-- | options/locale/locale_en-US.ini | 5 | ||||
-rw-r--r-- | routers/web/admin/admin.go | 9 | ||||
-rw-r--r-- | routers/web/admin/config.go | 17 | ||||
-rw-r--r-- | routers/web/web.go | 1 | ||||
-rw-r--r-- | templates/admin/config.tmpl | 12 | ||||
-rw-r--r-- | templates/admin/self_check.tmpl | 58 |
8 files changed, 119 insertions, 27 deletions
diff --git a/modules/cache/cache.go b/modules/cache/cache.go index 0753671158..b5400b0bd6 100644 --- a/modules/cache/cache.go +++ b/modules/cache/cache.go @@ -4,6 +4,7 @@ package cache import ( + "fmt" "strconv" "time" @@ -35,6 +36,37 @@ func Init() error { 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 func GetCache() StringCache { return defaultCache diff --git a/modules/cache/cache_test.go b/modules/cache/cache_test.go index 0c68cc26ee..e0b82f86f2 100644 --- a/modules/cache/cache_test.go +++ b/modules/cache/cache_test.go @@ -34,6 +34,18 @@ func TestNewContext(t *testing.T) { 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) { createTestCache() diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index fbada5472c..815cba6eec 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -93,6 +93,7 @@ remove_all = Remove All remove_label_str = Remove item "%s" edit = Edit view = View +test = Test enabled = Enabled disabled = Disabled @@ -3225,6 +3226,10 @@ config.cache_adapter = Cache Adapter config.cache_interval = Cache Interval config.cache_conn = Cache Connection 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_provider = Session Provider diff --git a/routers/web/admin/admin.go b/routers/web/admin/admin.go index dee1650b5a..6fc97c949e 100644 --- a/routers/web/admin/admin.go +++ b/routers/web/admin/admin.go @@ -15,6 +15,7 @@ import ( activities_model "code.gitea.io/gitea/models/activities" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/json" @@ -222,6 +223,14 @@ func SelfCheck(ctx *context.Context) { 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) } diff --git a/routers/web/admin/config.go b/routers/web/admin/config.go index 2a842cff82..2ae93e9cac 100644 --- a/routers/web/admin/config.go +++ b/routers/web/admin/config.go @@ -12,6 +12,7 @@ import ( system_model "code.gitea.io/gitea/models/system" "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" @@ -42,6 +43,22 @@ func SendTestMail(ctx *context.Context) { 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 { fields := strings.Split(cfgItem, splitter) for i := 0; i < len(fields); i++ { diff --git a/routers/web/web.go b/routers/web/web.go index 5fb1ce0e80..08f5d3d068 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -692,6 +692,7 @@ func registerRoutes(m *web.Route) { m.Get("", admin.Config) m.Post("", admin.ChangeConfig) m.Post("/test_mail", admin.SendTestMail) + m.Post("/test_cache", admin.TestCache) m.Get("/settings", admin.ConfigSettings) }) diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl index 197a6c6add..87f18192a6 100644 --- a/templates/admin/config.tmpl +++ b/templates/admin/config.tmpl @@ -229,8 +229,8 @@ <dt>{{ctx.Locale.Tr "admin.config.mailer_user"}}</dt> <dd>{{if .Mailer.User}}{{.Mailer.User}}{{else}}(empty){{end}}</dd> <div class="divider"></div> - <dt class="tw-py-1">{{ctx.Locale.Tr "admin.config.send_test_mail"}}</dt> - <dd> + <dt class="tw-py-1 tw-flex tw-items-center">{{ctx.Locale.Tr "admin.config.send_test_mail"}}</dt> + <dd class="tw-py-0"> <form class="ui form ignore-dirty" action="{{AppSubUrl}}/admin/config/test_mail" method="post"> {{.CsrfTokenHtml}} <div class="ui tiny input"> @@ -260,6 +260,14 @@ <dt>{{ctx.Locale.Tr "admin.config.cache_item_ttl"}}</dt> <dd><code>{{.CacheItemTTL}}</code></dd> {{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> </div> diff --git a/templates/admin/self_check.tmpl b/templates/admin/self_check.tmpl index b249bf228e..a7f43f4e12 100644 --- a/templates/admin/self_check.tmpl +++ b/templates/admin/self_check.tmpl @@ -17,32 +17,40 @@ <div class="ui attached segment tw-hidden self-check-problem" id="self-check-by-frontend"></div> {{if .DatabaseCheckHasProblems}} - <div class="ui attached segment self-check-problem"> - {{if .DatabaseType.IsMySQL}} - <div class="tw-p-2">{{ctx.Locale.Tr "admin.self_check.database_fix_mysql"}}</div> - {{else if .DatabaseType.IsMSSQL}} - <div class="tw-p-2">{{ctx.Locale.Tr "admin.self_check.database_fix_mssql"}}</div> - {{end}} - {{if .DatabaseCheckCollationMismatch}} - <div class="ui red message">{{ctx.Locale.Tr "admin.self_check.database_collation_mismatch" .DatabaseCheckResult.ExpectedCollation}}</div> - {{end}} - {{if .DatabaseCheckCollationCaseInsensitive}} - <div class="ui warning message">{{ctx.Locale.Tr "admin.self_check.database_collation_case_insensitive" .DatabaseCheckResult.DatabaseCollation}}</div> - {{end}} - {{if .DatabaseCheckInconsistentCollationColumns}} - <div class="ui red message"> - <details> - <summary>{{ctx.Locale.Tr "admin.self_check.database_inconsistent_collation_columns" .DatabaseCheckResult.DatabaseCollation}}</summary> - <ul class="tw-w-full"> - {{range .DatabaseCheckInconsistentCollationColumns}} - <li>{{.}}</li> - {{end}} - </ul> - </details> - </div> - {{end}} - </div> + <div class="ui attached segment self-check-problem"> + {{if .DatabaseType.IsMySQL}} + <div class="tw-p-2">{{ctx.Locale.Tr "admin.self_check.database_fix_mysql"}}</div> + {{else if .DatabaseType.IsMSSQL}} + <div class="tw-p-2">{{ctx.Locale.Tr "admin.self_check.database_fix_mssql"}}</div> + {{end}} + {{if .DatabaseCheckCollationMismatch}} + <div class="ui red message">{{ctx.Locale.Tr "admin.self_check.database_collation_mismatch" .DatabaseCheckResult.ExpectedCollation}}</div> + {{end}} + {{if .DatabaseCheckCollationCaseInsensitive}} + <div class="ui warning message">{{ctx.Locale.Tr "admin.self_check.database_collation_case_insensitive" .DatabaseCheckResult.DatabaseCollation}}</div> + {{end}} + {{if .DatabaseCheckInconsistentCollationColumns}} + <div class="ui red message"> + <details> + <summary>{{ctx.Locale.Tr "admin.self_check.database_inconsistent_collation_columns" .DatabaseCheckResult.DatabaseCollation}}</summary> + <ul class="tw-w-full"> + {{range .DatabaseCheckInconsistentCollationColumns}} + <li>{{.}}</li> + {{end}} + </ul> + </details> + </div> + {{end}} + </div> {{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" */}} <div class="ui attached segment tw-hidden self-check-no-problem"> {{ctx.Locale.Tr "admin.self_check.no_problem_found"}} |