I am observing pause failures in an application and it appears to me that 
I’m seeing it take from 5 to 28 seconds or more for the StopTheWorld to 
take effect for GC.

In all likelihood the problem lies in the application, but before I start 
changing it or attempting to tune GC, I’d like to understand what’s 
happening.  I’d appreciate any assistance in decoding what I’m seeing in 
schedtrace output.

Details are below but here are 4 consecutive samples at 1 second 
intervals.  For brevity I’ve pruned out lines with ‘M.* p=-1’ and ‘G.* 
status=[34]’ (syscall or waiting in runtime).

The first two are after we start the part of the workload that triggers the 
failure and before the expected health check arrives.  The second two are 
as we try to StopTheWorld, which holds off the health check.

SCHED 674246ms: gomaxprocs=4 idleprocs=4 threads=14 spinningthreads=0 
idlethreads=4 runqueue=0 gcwaiting=0 nmidlelocked=1 stopwait=0 sysmonwait=0
  P0: status=0 schedtick=7112 syscalltick=1916 m=-1 runqsize=0 gfreecnt=6
  P1: status=0 schedtick=3545 syscalltick=1452 m=-1 runqsize=0 gfreecnt=8
  P2: status=0 schedtick=4549 syscalltick=1465 m=-1 runqsize=0 gfreecnt=7
  P3: status=0 schedtick=1616 syscalltick=1223 m=-1 runqsize=0 gfreecnt=0
  M7: p=1 curg=301 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 
helpgc=0 spinning=false blocked=false lockedg=-1

^^^ I suspect G301/M7 was in transition when sampled without locks or in a 
state I don’t understand.  For reference, it reported:
   G301: status=3(IO wait) m=7 lockedm=-1

SCHED 675256ms: gomaxprocs=4 idleprocs=2 threads=14 spinningthreads=0 
idlethreads=3 runqueue=0 gcwaiting=0 nmidlelocked=1 stopwait=0 sysmonwait=0
  P0: status=0 schedtick=7118 syscalltick=1921 m=-1 runqsize=0 gfreecnt=6
  P1: status=1 schedtick=3548 syscalltick=2656 m=7 runqsize=0 gfreecnt=8
  P2: status=1 schedtick=4551 syscalltick=1465 m=2 runqsize=0 gfreecnt=7
  P3: status=0 schedtick=1620 syscalltick=1225 m=-1 runqsize=0 gfreecnt=0
  M7: p=1 curg=301 mallocing=1 throwing=0 preemptoff= locks=2 dying=0 
helpgc=0 spinning=false blocked=false lockedg=-1
  M2: p=2 curg=7 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 
helpgc=0 spinning=false blocked=true lockedg=-1
  G7: status=2(GC worker (idle)) m=2 lockedm=-1
  G301: status=2(IO wait) m=7 lockedm=-1

^^^ G301 is now running on M7/P1.  M7 is marked as mallocing=1, locks=2
^^^ G7 is a GC worker that is now running on M2/P2.  M2 has locks=1

>>> Health check log expected here

SCHED 676264ms: gomaxprocs=4 idleprocs=1 threads=14 spinningthreads=0 
idlethreads=2 runqueue=0 gcwaiting=0 nmidlelocked=1 stopwait=0 sysmonwait=0
  P0: status=1 schedtick=7120 syscalltick=1927 m=6 runqsize=0 gfreecnt=5
  P1: status=1 schedtick=3548 syscalltick=2656 m=7 runqsize=0 gfreecnt=8
  P2: status=1 schedtick=4551 syscalltick=1465 m=2 runqsize=0 gfreecnt=7
  P3: status=0 schedtick=1622 syscalltick=1229 m=-1 runqsize=0 gfreecnt=0
  M7: p=1 curg=301 mallocing=1 throwing=0 preemptoff= locks=2 dying=0 
helpgc=0 spinning=false blocked=false lockedg=-1
  M6: p=0 curg=188 mallocing=1 throwing=0 preemptoff= locks=2 dying=0 
helpgc=0 spinning=false blocked=false lockedg=-1
  M2: p=2 curg=7 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 
helpgc=0 spinning=false blocked=true lockedg=-1
  G7: status=2(GC worker (idle)) m=2 lockedm=-1
  G301: status=2(IO wait) m=7 lockedm=-1
  G188: status=2() m=6 lockedm=-1

^^^ total of 3 P in use, 1 GC worker, 2 mallocing

SCHED 677271ms: gomaxprocs=4 idleprocs=0 threads=14 spinningthreads=0 
idlethreads=3 runqueue=1 gcwaiting=1 nmidlelocked=1 stopwait=1 sysmonwait=0
  P0: status=1 schedtick=7120 syscalltick=1927 m=6 runqsize=0 gfreecnt=5
  P1: status=3 schedtick=3550 syscalltick=2656 m=7 runqsize=0 gfreecnt=8
  P2: status=3 schedtick=4552 syscalltick=1465 m=-1 runqsize=0 gfreecnt=7
  P3: status=3 schedtick=1622 syscalltick=1229 m=-1 runqsize=0 gfreecnt=0
  M7: p=1 curg=6 mallocing=0 throwing=0 preemptoff=gcing locks=0 dying=0 
helpgc=0 spinning=false blocked=true lockedg=-1
  M6: p=0 curg=188 mallocing=1 throwing=0 preemptoff= locks=2 dying=0 
helpgc=0 spinning=false blocked=false lockedg=-1
  G6: status=2(GC worker (idle)) m=7 lockedm=-1
  G301: status=1(IO wait) m=-1 lockedm=-1
  G188: status=2() m=6 lockedm=-1

^^^ Someone has called stopTheWorldWithSema, either directly or via 
stopTheWorld.  It hasn’t returned because stopwait is > 0.
^^^.   Looks like call is not likely stopTheWorld and caller is likely G6 
GC worker that is running on M7/P1 (stopTheWorldSema marks its own P as 
gcstop).
^^^    stopTheWorldWithSema called from gcStart and gcMarkDone is caller.
^^^    stopTheWorldWithSema is poll-waiting for preemptall() to take effect 
(for P0/M6/G188 to notice preemption)

For the next 4 samples there are no changes in P* or M*.

After that we see a health check handler logged and in the sample after 
that gcwaiting and stopwait are 0.

What can G188/M6/P0 be doing such that it doesn’t notice the request to 
preempt itself for 5 seconds?  And while having mallocing=1?

Thanks for any insights or suggestions,

Eric


=============

Details:

go version go1.11.1 linux/amd64
System: Containerized Linux (Alpine)
GOMAXPROCS=4

Container reports NumCPU=1 so GOMAXPROCS defaults to 1.  Problem occurs on 
both GOMAXPROCS=1 and GOMAXPROCS=4.

The application is a fairly basic Kubernetes application with an http 
server that services a health check, a separate goroutine that periodically 
makes an http client request to poll for work, and a pool of worker 
goroutines that pick up work from a channel.  The system is configured 
to issue a health check every 10 seconds.  Intermittently during testing 
the health check fails repeatedly and the app is restarted by Kubernetes.

The test load is not particularly high— often the workers are idle and at 
most 2 are active active at a time.  The pause failure can be seen with 
only 1 request in flight at a time. 

I added a log message to the health check handler, set 
GODEBUG=schedtrace=1000,scheddetail=1 and reproduced the failure.  When the 
system is running normally, we see 1 health check log message every 10 
seconds with 10 SCHED traces in between each.

In looking at the failure I observed one instance of the health check being 
serviced 5+ seconds late after which the system recovered and then a set of 
late health checks where it was over 28 seconds and where Kubernetes 
terminated the job.

The extract above is from the 5+ second pause.  I happy to share the fuller 
logs and also have runtime.MemStat dumped after the second failure.  It 
reports PauseTotalNs of 39.7 seconds with last 3 pauses being 5.7s, 5.2s, 
and 28.6s. 

========================

Even more details:

Might as well include MemStats:

memstats
"Alloc”:35820624
"TotalAlloc”:87362776
"Sys”:72284408
"Lookups”:0
"Mallocs":913917 
"Frees”:732071
"HeapAlloc”:35820624
"HeapSys”:66027520
"HeapIdle”:25460736
"HeapInuse”:40566784
"HeapReleased”:0
"HeapObjects”:181846
"StackInuse”:1048576
"StackSys”:1048576
"MSpanInuse”:361000
"MSpanSys”:409600
"MCacheInuse”:6912
"MCacheSys”:16384
"BuckHashSys”:1495863
"GCSys”:2441216
"OtherSys”:845249
"NextGC”:42461456
"LastGC”:1544141699097608371
"PauseTotalNs”:39724621006
"PauseNs":[121384
3326024
3534690
525590
54645
22208743
98898
250651
37238
2034238
33731
38616
41163
35875
37798
5794188053
5265243818
28632809851]
”PauseEnd":[1544140960164525406
1544140960182034741
1544140960209848541
1544140960278986107
1544140960733914972
1544140961198224758
1544140961290430652
1544140961374230055
1544140961455867822
1544140961561961827
1544141081577752402
1544141201598823342
1544141321622343360
1544141441643055123
1544141561665242451
1544141599296615758
1544141641227906209
1544141699097608371]
"NumGC”:18
"NumForcedGC”:0
"GCCPUFraction”:0.013482378367869897
"EnableGC”:true
"DebugGC”:false
"BySize”:[
{"Size":0,"Mallocs":0,"Frees":0}
{"Size":8,"Mallocs":19587,"Frees":16601}
{"Size":16,"Mallocs":329030,"Frees":254713}
{"Size":32,"Mallocs":169516,"Frees":119550}
{"Size":48,"Mallocs":89240,"Frees":73834}
{"Size":64,"Mallocs":27676,"Frees":20629}
{"Size":80,"Mallocs":19143,"Frees":13042}
{"Size":96,"Mallocs":8042,"Frees":6076}
{"Size":112,"Mallocs":12250,"Frees":10569}
{"Size":128,"Mallocs":23168,"Frees":16902}
{"Size":144,"Mallocs":3305,"Frees":1499}
{"Size":160,"Mallocs":29309,"Frees":28151}
{"Size":176,"Mallocs":799,"Frees":325}
{"Size":192,"Mallocs":1486,"Frees":1043}
{"Size":208,"Mallocs":2884,"Frees":1687}
{"Size":224,"Mallocs":1196,"Frees":530}
{"Size":240,"Mallocs":823,"Frees":671}
{"Size":256,"Mallocs":4766,"Frees":4442}
{"Size":288,"Mallocs":31701,"Frees":30325}
{"Size":320,"Mallocs":2082,"Frees":1431}
{"Size":352,"Mallocs":2775,"Frees":2370}
{"Size":384,"Mallocs":387,"Frees":250}
{"Size":416,"Mallocs":429,"Frees":284}
{"Size":448,"Mallocs":229,"Frees":116}
{"Size":480,"Mallocs":151,"Frees":133}
{"Size":512,"Mallocs":3240,"Frees":3053}
{"Size":576,"Mallocs":9709,"Frees":5829}
{"Size":640,"Mallocs":192,"Frees":137}
{"Size":704,"Mallocs":203,"Frees":131}
{"Size":768,"Mallocs":739,"Frees":427}
{"Size":896,"Mallocs":829,"Frees":401}
{"Size":1024,"Mallocs":2817,"Frees":2677}
{"Size":1152,"Mallocs":983,"Frees":812}
{"Size":1280,"Mallocs":467,"Frees":109}
{"Size":1408,"Mallocs":923,"Frees":335}
{"Size":1536,"Mallocs":429,"Frees":289}
{"Size":1792,"Mallocs":644,"Frees":547}
{"Size":2048,"Mallocs":390,"Frees":210}
{"Size":2304,"Mallocs":249,"Frees":144}
{"Size":2688,"Mallocs":190,"Frees":106}
{"Size":3072,"Mallocs":20,"Frees":16}
{"Size":3200,"Mallocs":23,"Frees":13}
{"Size":3456,"Mallocs":14,"Frees":11}
{"Size":4096,"Mallocs":201,"Frees":127}
{"Size":4864,"Mallocs":52,"Frees":42}
{"Size":5376,"Mallocs":29,"Frees":8}
{"Size":6144,"Mallocs":12,"Frees":7}
{"Size":6528,"Mallocs":11,"Frees":3}
{"Size":6784,"Mallocs":3,"Frees":1}
{"Size":6912,"Mallocs":0,"Frees":0}
{"Size":8192,"Mallocs":50,"Frees":32}
{"Size":9472,"Mallocs":46,"Frees":24}
{"Size":9728,"Mallocs":0,"Frees":0}
{"Size":10240,"Mallocs":0,"Frees":0}
{"Size":10880,"Mallocs":2,"Frees":1}
{"Size":12288,"Mallocs":3,"Frees":0}
{"Size":13568,"Mallocs":0,"Frees":0}
{"Size":14336,"Mallocs":7,"Frees":3}
{"Size":16384,"Mallocs":30,"Frees":17}
{"Size":18432,"Mallocs":3,"Frees":3}
{"Size":19072,"Mallocs":16,"Frees":12}
]}


Pause durations in milliseconds and end times:
    0 2018-12-07 00:02:40.164525406 +0000 UTC
    3 2018-12-07 00:02:40.182034741 +0000 UTC
    3 2018-12-07 00:02:40.209848541 +0000 UTC
    0 2018-12-07 00:02:40.278986107 +0000 UTC
    0 2018-12-07 00:02:40.733914972 +0000 UTC
   22 2018-12-07 00:02:41.198224758 +0000 UTC
    0 2018-12-07 00:02:41.290430652 +0000 UTC
    0 2018-12-07 00:02:41.374230055 +0000 UTC
    0 2018-12-07 00:02:41.455867822 +0000 UTC
    2 2018-12-07 00:02:41.561961827 +0000 UTC
    0 2018-12-07 00:04:41.577752402 +0000 UTC
    0 2018-12-07 00:06:41.598823342 +0000 UTC
    0 2018-12-07 00:08:41.62234336 +0000 UTC
    0 2018-12-07 00:10:41.643055123 +0000 UTC
    0 2018-12-07 00:12:41.665242451 +0000 UTC
 5794 2018-12-07 00:13:19.296615758 +0000 UTC
 5265 2018-12-07 00:14:01.227906209 +0000 UTC
28632 2018-12-07 00:14:59.097608371 +0000 UTC




-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to