loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Auto-update the system status in admin dashboard (#29163)

- Refactor the system status list into its own template
- Change the backend to return only the system status if htmx initiated
the request
- `hx-get="{{$.Link}}/system_status`: reuse the backend handler
- `hx-swap="innerHTML"`: replace the `<div>`'s innerHTML (essentially
the new template)
- `hx-trigger="every 5s"`: call every 5 seconds
- `hx-indicator=".divider"`: the `is-loading` class shouldn't be added
to the div during the request, so set it on an element it has no effect
on
- Render "Since Last GC Time" with `<relative-time>`, so we send a
timestamp

# Auto-update in action GIF

![action](https://github.com/go-gitea/gitea/assets/20454870/c6e1f220-f0fb-4460-ac3b-59f315e30e29)

---------

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
Co-authored-by: silverwind <me@silverwind.io>
(cherry picked from commit c70f65e83bc1876fb368fd117d342573ff18a9e8)

authored by

Yarden Shoham
silverwind
and committed by
Earl Warren
4f050f35 3adfb6cb

+97 -74
+6
package-lock.json
··· 31 31 "escape-goat": "4.0.0", 32 32 "fast-glob": "3.3.2", 33 33 "htmx.org": "1.9.10", 34 + "idiomorph": "0.3.0", 34 35 "jquery": "3.7.1", 35 36 "katex": "0.16.9", 36 37 "license-checker-webpack-plugin": "0.2.1", ··· 6173 6174 "peerDependencies": { 6174 6175 "postcss": "^8.1.0" 6175 6176 } 6177 + }, 6178 + "node_modules/idiomorph": { 6179 + "version": "0.3.0", 6180 + "resolved": "https://registry.npmjs.org/idiomorph/-/idiomorph-0.3.0.tgz", 6181 + "integrity": "sha512-UhV1Ey5xCxIwR9B+OgIjQa+1Jx99XQ1vQHUsKBU1RpQzCx1u+b+N6SOXgf5mEJDqemUI/ffccu6+71l2mJUsRA==" 6176 6182 }, 6177 6183 "node_modules/ieee754": { 6178 6184 "version": "1.2.1",
+1
package.json
··· 30 30 "escape-goat": "4.0.0", 31 31 "fast-glob": "3.3.2", 32 32 "htmx.org": "1.9.10", 33 + "idiomorph": "0.3.0", 33 34 "jquery": "3.7.1", 34 35 "katex": "0.16.9", 35 36 "license-checker-webpack-plugin": "0.2.1",
+16 -10
routers/web/admin/admin.go
··· 28 28 ) 29 29 30 30 const ( 31 - tplDashboard base.TplName = "admin/dashboard" 32 - tplSelfCheck base.TplName = "admin/self_check" 33 - tplCron base.TplName = "admin/cron" 34 - tplQueue base.TplName = "admin/queue" 35 - tplStacktrace base.TplName = "admin/stacktrace" 36 - tplQueueManage base.TplName = "admin/queue_manage" 37 - tplStats base.TplName = "admin/stats" 31 + tplDashboard base.TplName = "admin/dashboard" 32 + tplSystemStatus base.TplName = "admin/system_status" 33 + tplSelfCheck base.TplName = "admin/self_check" 34 + tplCron base.TplName = "admin/cron" 35 + tplQueue base.TplName = "admin/queue" 36 + tplStacktrace base.TplName = "admin/stacktrace" 37 + tplQueueManage base.TplName = "admin/queue_manage" 38 + tplStats base.TplName = "admin/stats" 38 39 ) 39 40 40 41 var sysStatus struct { ··· 72 73 73 74 // Garbage collector statistics. 74 75 NextGC string // next run in HeapAlloc time (bytes) 75 - LastGC string // last run in absolute time (ns) 76 + LastGCTime string // last run time 76 77 PauseTotalNs string 77 78 PauseNs string // circular buffer of recent GC pause times, most recent at [(NumGC+255)%256] 78 79 NumGC uint32 ··· 110 111 sysStatus.OtherSys = base.FileSize(int64(m.OtherSys)) 111 112 112 113 sysStatus.NextGC = base.FileSize(int64(m.NextGC)) 113 - sysStatus.LastGC = fmt.Sprintf("%.1fs", float64(time.Now().UnixNano()-int64(m.LastGC))/1000/1000/1000) 114 + sysStatus.LastGCTime = time.Unix(0, int64(m.LastGC)).Format(time.RFC3339) 114 115 sysStatus.PauseTotalNs = fmt.Sprintf("%.1fs", float64(m.PauseTotalNs)/1000/1000/1000) 115 116 sysStatus.PauseNs = fmt.Sprintf("%.3fs", float64(m.PauseNs[(m.NumGC+255)%256])/1000/1000/1000) 116 117 sysStatus.NumGC = m.NumGC ··· 132 133 ctx.Data["PageIsAdminDashboard"] = true 133 134 ctx.Data["NeedUpdate"] = updatechecker.GetNeedUpdate(ctx) 134 135 ctx.Data["RemoteVersion"] = updatechecker.GetRemoteVersion(ctx) 135 - // FIXME: update periodically 136 136 updateSystemStatus() 137 137 ctx.Data["SysStatus"] = sysStatus 138 138 ctx.Data["SSH"] = setting.SSH 139 139 prepareDeprecatedWarningsAlert(ctx) 140 140 ctx.HTML(http.StatusOK, tplDashboard) 141 + } 142 + 143 + func SystemStatus(ctx *context.Context) { 144 + updateSystemStatus() 145 + ctx.Data["SysStatus"] = sysStatus 146 + ctx.HTML(http.StatusOK, tplSystemStatus) 141 147 } 142 148 143 149 // DashboardPost run an admin operation
+1
routers/web/web.go
··· 681 681 // ***** START: Admin ***** 682 682 m.Group("/admin", func() { 683 683 m.Get("", admin.Dashboard) 684 + m.Get("/system_status", admin.SystemStatus) 684 685 m.Post("", web.Bind(forms.AdminDashboardForm{}), admin.DashboardPost) 685 686 686 687 if setting.Database.Type.IsMySQL() || setting.Database.Type.IsMSSQL() {
+3 -63
templates/admin/dashboard.tmpl
··· 75 75 <h4 class="ui top attached header"> 76 76 {{ctx.Locale.Tr "admin.dashboard.system_status"}} 77 77 </h4> 78 - <div class="ui attached table segment"> 79 - <dl class="admin-dl-horizontal"> 80 - <dt>{{ctx.Locale.Tr "admin.dashboard.server_uptime"}}</dt> 81 - <dd><relative-time format="duration" datetime="{{.SysStatus.StartTime}}">{{.SysStatus.StartTime}}</relative-time></dd> 82 - <dt>{{ctx.Locale.Tr "admin.dashboard.current_goroutine"}}</dt> 83 - <dd>{{.SysStatus.NumGoroutine}}</dd> 84 - <div class="divider"></div> 85 - <dt>{{ctx.Locale.Tr "admin.dashboard.current_memory_usage"}}</dt> 86 - <dd>{{.SysStatus.MemAllocated}}</dd> 87 - <dt>{{ctx.Locale.Tr "admin.dashboard.total_memory_allocated"}}</dt> 88 - <dd>{{.SysStatus.MemTotal}}</dd> 89 - <dt>{{ctx.Locale.Tr "admin.dashboard.memory_obtained"}}</dt> 90 - <dd>{{.SysStatus.MemSys}}</dd> 91 - <dt>{{ctx.Locale.Tr "admin.dashboard.pointer_lookup_times"}}</dt> 92 - <dd>{{.SysStatus.Lookups}}</dd> 93 - <dt>{{ctx.Locale.Tr "admin.dashboard.memory_allocate_times"}}</dt> 94 - <dd>{{.SysStatus.MemMallocs}}</dd> 95 - <dt>{{ctx.Locale.Tr "admin.dashboard.memory_free_times"}}</dt> 96 - <dd>{{.SysStatus.MemFrees}}</dd> 97 - <div class="divider"></div> 98 - <dt>{{ctx.Locale.Tr "admin.dashboard.current_heap_usage"}}</dt> 99 - <dd>{{.SysStatus.HeapAlloc}}</dd> 100 - <dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_obtained"}}</dt> 101 - <dd>{{.SysStatus.HeapSys}}</dd> 102 - <dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_idle"}}</dt> 103 - <dd>{{.SysStatus.HeapIdle}}</dd> 104 - <dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_in_use"}}</dt> 105 - <dd>{{.SysStatus.HeapInuse}}</dd> 106 - <dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_released"}}</dt> 107 - <dd>{{.SysStatus.HeapReleased}}</dd> 108 - <dt>{{ctx.Locale.Tr "admin.dashboard.heap_objects"}}</dt> 109 - <dd>{{.SysStatus.HeapObjects}}</dd> 110 - <div class="divider"></div> 111 - <dt>{{ctx.Locale.Tr "admin.dashboard.bootstrap_stack_usage"}}</dt> 112 - <dd>{{.SysStatus.StackInuse}}</dd> 113 - <dt>{{ctx.Locale.Tr "admin.dashboard.stack_memory_obtained"}}</dt> 114 - <dd>{{.SysStatus.StackSys}}</dd> 115 - <dt>{{ctx.Locale.Tr "admin.dashboard.mspan_structures_usage"}}</dt> 116 - <dd>{{.SysStatus.MSpanInuse}}</dd> 117 - <dt>{{ctx.Locale.Tr "admin.dashboard.mspan_structures_obtained"}}</dt> 118 - <dd>{{.SysStatus.MSpanSys}}</dd> 119 - <dt>{{ctx.Locale.Tr "admin.dashboard.mcache_structures_usage"}}</dt> 120 - <dd>{{.SysStatus.MCacheInuse}}</dd> 121 - <dt>{{ctx.Locale.Tr "admin.dashboard.mcache_structures_obtained"}}</dt> 122 - <dd>{{.SysStatus.MCacheSys}}</dd> 123 - <dt>{{ctx.Locale.Tr "admin.dashboard.profiling_bucket_hash_table_obtained"}}</dt> 124 - <dd>{{.SysStatus.BuckHashSys}}</dd> 125 - <dt>{{ctx.Locale.Tr "admin.dashboard.gc_metadata_obtained"}}</dt> 126 - <dd>{{.SysStatus.GCSys}}</dd> 127 - <dt>{{ctx.Locale.Tr "admin.dashboard.other_system_allocation_obtained"}}</dt> 128 - <dd>{{.SysStatus.OtherSys}}</dd> 129 - <div class="divider"></div> 130 - <dt>{{ctx.Locale.Tr "admin.dashboard.next_gc_recycle"}}</dt> 131 - <dd>{{.SysStatus.NextGC}}</dd> 132 - <dt>{{ctx.Locale.Tr "admin.dashboard.last_gc_time"}}</dt> 133 - <dd>{{.SysStatus.LastGC}}</dd> 134 - <dt>{{ctx.Locale.Tr "admin.dashboard.total_gc_pause"}}</dt> 135 - <dd>{{.SysStatus.PauseTotalNs}}</dd> 136 - <dt>{{ctx.Locale.Tr "admin.dashboard.last_gc_pause"}}</dt> 137 - <dd>{{.SysStatus.PauseNs}}</dd> 138 - <dt>{{ctx.Locale.Tr "admin.dashboard.gc_times"}}</dt> 139 - <dd>{{.SysStatus.NumGC}}</dd> 140 - </dl> 78 + {{/* TODO: make these stats work in multi-server deployments, likely needs per-server stats in DB */}} 79 + <div hx-get="{{$.Link}}/system_status" hx-swap="morph:innerHTML" hx-trigger="every 5s" hx-indicator=".divider" class="ui attached table segment"> 80 + {{template "admin/system_status" .}} 141 81 </div> 142 82 </div> 143 83 {{template "admin/layout_footer" .}}
+62
templates/admin/system_status.tmpl
··· 1 + <dl class="admin-dl-horizontal"> 2 + <dt>{{ctx.Locale.Tr "admin.dashboard.server_uptime"}}</dt> 3 + <dd><relative-time format="duration" datetime="{{.SysStatus.StartTime}}">{{.SysStatus.StartTime}}</relative-time></dd> 4 + <dt>{{ctx.Locale.Tr "admin.dashboard.current_goroutine"}}</dt> 5 + <dd>{{.SysStatus.NumGoroutine}}</dd> 6 + <div class="divider"></div> 7 + <dt>{{ctx.Locale.Tr "admin.dashboard.current_memory_usage"}}</dt> 8 + <dd>{{.SysStatus.MemAllocated}}</dd> 9 + <dt>{{ctx.Locale.Tr "admin.dashboard.total_memory_allocated"}}</dt> 10 + <dd>{{.SysStatus.MemTotal}}</dd> 11 + <dt>{{ctx.Locale.Tr "admin.dashboard.memory_obtained"}}</dt> 12 + <dd>{{.SysStatus.MemSys}}</dd> 13 + <dt>{{ctx.Locale.Tr "admin.dashboard.pointer_lookup_times"}}</dt> 14 + <dd>{{.SysStatus.Lookups}}</dd> 15 + <dt>{{ctx.Locale.Tr "admin.dashboard.memory_allocate_times"}}</dt> 16 + <dd>{{.SysStatus.MemMallocs}}</dd> 17 + <dt>{{ctx.Locale.Tr "admin.dashboard.memory_free_times"}}</dt> 18 + <dd>{{.SysStatus.MemFrees}}</dd> 19 + <div class="divider"></div> 20 + <dt>{{ctx.Locale.Tr "admin.dashboard.current_heap_usage"}}</dt> 21 + <dd>{{.SysStatus.HeapAlloc}}</dd> 22 + <dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_obtained"}}</dt> 23 + <dd>{{.SysStatus.HeapSys}}</dd> 24 + <dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_idle"}}</dt> 25 + <dd>{{.SysStatus.HeapIdle}}</dd> 26 + <dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_in_use"}}</dt> 27 + <dd>{{.SysStatus.HeapInuse}}</dd> 28 + <dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_released"}}</dt> 29 + <dd>{{.SysStatus.HeapReleased}}</dd> 30 + <dt>{{ctx.Locale.Tr "admin.dashboard.heap_objects"}}</dt> 31 + <dd>{{.SysStatus.HeapObjects}}</dd> 32 + <div class="divider"></div> 33 + <dt>{{ctx.Locale.Tr "admin.dashboard.bootstrap_stack_usage"}}</dt> 34 + <dd>{{.SysStatus.StackInuse}}</dd> 35 + <dt>{{ctx.Locale.Tr "admin.dashboard.stack_memory_obtained"}}</dt> 36 + <dd>{{.SysStatus.StackSys}}</dd> 37 + <dt>{{ctx.Locale.Tr "admin.dashboard.mspan_structures_usage"}}</dt> 38 + <dd>{{.SysStatus.MSpanInuse}}</dd> 39 + <dt>{{ctx.Locale.Tr "admin.dashboard.mspan_structures_obtained"}}</dt> 40 + <dd>{{.SysStatus.MSpanSys}}</dd> 41 + <dt>{{ctx.Locale.Tr "admin.dashboard.mcache_structures_usage"}}</dt> 42 + <dd>{{.SysStatus.MCacheInuse}}</dd> 43 + <dt>{{ctx.Locale.Tr "admin.dashboard.mcache_structures_obtained"}}</dt> 44 + <dd>{{.SysStatus.MCacheSys}}</dd> 45 + <dt>{{ctx.Locale.Tr "admin.dashboard.profiling_bucket_hash_table_obtained"}}</dt> 46 + <dd>{{.SysStatus.BuckHashSys}}</dd> 47 + <dt>{{ctx.Locale.Tr "admin.dashboard.gc_metadata_obtained"}}</dt> 48 + <dd>{{.SysStatus.GCSys}}</dd> 49 + <dt>{{ctx.Locale.Tr "admin.dashboard.other_system_allocation_obtained"}}</dt> 50 + <dd>{{.SysStatus.OtherSys}}</dd> 51 + <div class="divider"></div> 52 + <dt>{{ctx.Locale.Tr "admin.dashboard.next_gc_recycle"}}</dt> 53 + <dd>{{.SysStatus.NextGC}}</dd> 54 + <dt>{{ctx.Locale.Tr "admin.dashboard.last_gc_time"}}</dt> 55 + <dd><relative-time format="duration" datetime="{{.SysStatus.LastGCTime}}">{{.SysStatus.LastGCTime}}</relative-time></dd> 56 + <dt>{{ctx.Locale.Tr "admin.dashboard.total_gc_pause"}}</dt> 57 + <dd>{{.SysStatus.PauseTotalNs}}</dd> 58 + <dt>{{ctx.Locale.Tr "admin.dashboard.last_gc_pause"}}</dt> 59 + <dd>{{.SysStatus.PauseNs}}</dd> 60 + <dt>{{ctx.Locale.Tr "admin.dashboard.gc_times"}}</dt> 61 + <dd>{{.SysStatus.NumGC}}</dd> 62 + </dl>
+1 -1
templates/base/head.tmpl
··· 30 30 {{template "base/head_style" .}} 31 31 {{template "custom/header" .}} 32 32 </head> 33 - <body hx-headers='{"x-csrf-token": "{{.CsrfToken}}"}' hx-swap="outerHTML" hx-push-url="false"> 33 + <body hx-headers='{"x-csrf-token": "{{.CsrfToken}}"}' hx-swap="outerHTML" hx-ext="morph" hx-push-url="false"> 34 34 {{ctx.DataRaceCheck $.Context}} 35 35 {{template "custom/body_outer_pre" .}} 36 36
+3
web_src/js/htmx.js
··· 1 1 import * as htmx from 'htmx.org'; 2 2 import {showErrorToast} from './modules/toast.js'; 3 3 4 + // https://github.com/bigskysoftware/idiomorph#htmx 5 + import 'idiomorph/dist/idiomorph-ext.js'; 6 + 4 7 // https://htmx.org/reference/#config 5 8 htmx.config.requestClass = 'is-loading'; 6 9 htmx.config.scrollIntoViewOnBoost = false;
+4
webpack.config.js
··· 173 173 ], 174 174 }, 175 175 plugins: [ 176 + new webpack.ProvidePlugin({ // for htmx extensions 177 + htmx: 'htmx.org', 178 + }), 176 179 new DefinePlugin({ 177 180 __VUE_OPTIONS_API__: true, // at the moment, many Vue components still use the Vue Options API 178 181 __VUE_PROD_DEVTOOLS__: false, // do not enable devtools support in production ··· 211 214 override: { 212 215 'khroma@*': {licenseName: 'MIT'}, // https://github.com/fabiospampinato/khroma/pull/33 213 216 'htmx.org@1.9.10': {licenseName: 'BSD-2-Clause'}, // "BSD 2-Clause" -> "BSD-2-Clause" 217 + 'idiomorph@0.3.0': {licenseName: 'BSD-2-Clause'}, // "BSD 2-Clause" -> "BSD-2-Clause" 214 218 }, 215 219 emitError: true, 216 220 allow: '(Apache-2.0 OR BSD-2-Clause OR BSD-3-Clause OR MIT OR ISC OR CPAL-1.0 OR Unlicense OR EPL-1.0 OR EPL-2.0)',