-
Notifications
You must be signed in to change notification settings - Fork 94
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore(redactors): memory consumption improvements #1332
chore(redactors): memory consumption improvements #1332
Conversation
Strings are immutable and hence we need to create a new one all the time when operation on them
202acc1
to
a53acb4
Compare
@banjoh Working to get myself educated on these changes, so apologies in advance if none of the following makes sense :) A few questions so far:
Just to make sure I understand, if there's a large file without newlines it'll fall back to the buffer size same as Also, you mentioned:
With
|
FWIW, I see these pprof cumulative figures on M2 pro: pre-fix (main):
# ./bin/support-bundle test.yaml --debug --memprofile=mem.prof
Collecting support bundle ⠇ [apiserver-audit-logs] Running host collector...
support-bundle-2023-09-14T03_21_59.tar.gz
============ Collectors summary =============
Succeeded (S), eXcluded (X), Failed (F)
=============================================
apiserver-audit-logs (S) : 359ms
============ Redactors summary =============
Host collectors : 17,366ms
============= Analyzers summary =============
Succeeded (S), eXcluded (X), Failed (F)
=============================================
No analyzers executed
Duration: 17,726ms
# go tool pprof -alloc_space mem.prof
# (pprof) top
Showing nodes accounting for 41.38GB, 99.05% of 41.78GB total
Dropped 193 nodes (cum <= 0.21GB)
Showing top 10 nodes out of 19
flat flat% sum% cum cum%
17.31GB 41.43% 41.43% 17.31GB 41.43% strings.(*Builder).grow
12.01GB 28.75% 70.18% 12.01GB 28.75% bufio.(*Scanner).Text (inline)
11.22GB 26.86% 97.04% 11.22GB 26.86% github.com/replicatedhq/troubleshoot/pkg/redact.readLine
0.37GB 0.88% 97.93% 0.37GB 0.88% fmt.(*buffer).writeString (inline)
0.27GB 0.64% 98.56% 24.64GB 58.98% github.com/replicatedhq/troubleshoot/pkg/redact.(*SingleLineRedactor).Redact.func1
0.14GB 0.32% 98.88% 0.28GB 0.68% regexp.(*Regexp).ReplaceAllString
0.07GB 0.17% 99.05% 17.11GB 40.95% github.com/replicatedhq/troubleshoot/pkg/redact.(*MultiLineRedactor).Redact.func1
0 0% 99.05% 0.37GB 0.88% fmt.(*fmt).fmtS
0 0% 99.05% 0.37GB 0.88% fmt.(*fmt).padString
0 0% 99.05% 0.37GB 0.88% fmt.(*pp).doPrintf post-fix (feature branch):
# ./bin/support-bundle test.yaml --debug --memprofile=mem.prof
Collecting support bundle ⠼ [apiserver-audit-logs] Running host collector...
support-bundle-2023-09-14T03_08_42.tar.gz
============ Collectors summary =============
Succeeded (S), eXcluded (X), Failed (F)
=============================================
apiserver-audit-logs (S) : 303ms
============ Redactors summary =============
Host collectors : 16,986ms
============= Analyzers summary =============
Succeeded (S), eXcluded (X), Failed (F)
=============================================
No analyzers executed
Duration: 17,290ms
# go tool pprof -alloc_space mem.prof
# (pprof) top
(pprof) top
Showing nodes accounting for 24.22GB, 98.91% of 24.49GB total
Dropped 132 nodes (cum <= 0.12GB)
Showing top 10 nodes out of 13
flat flat% sum% cum cum%
17.10GB 69.81% 69.81% 17.17GB 70.10% bytes.ToLower
5.85GB 23.91% 93.71% 5.85GB 23.91% github.com/replicatedhq/troubleshoot/pkg/redact.readLine
0.71GB 2.90% 96.62% 12.33GB 50.36% github.com/replicatedhq/troubleshoot/pkg/redact.(*MultiLineRedactor).Redact.func1
0.41GB 1.69% 98.31% 0.41GB 1.69% regexp.(*bitState).push (inline)
0.15GB 0.6% 98.91% 0.15GB 0.6% regexp.(*Regexp).replaceAll
0 0% 98.91% 12.14GB 49.58% github.com/replicatedhq/troubleshoot/pkg/redact.(*SingleLineRedactor).Redact.func1
0 0% 98.91% 5.85GB 23.91% github.com/replicatedhq/troubleshoot/pkg/redact.getNextTwoLines
0 0% 98.91% 0.54GB 2.22% regexp.(*Regexp).Match (inline)
0 0% 98.91% 0.15GB 0.6% regexp.(*Regexp).ReplaceAll
0 0% 98.91% 0.53GB 2.18% regexp.(*Regexp).backtrack In either cases:
Data used: # cat test.yaml
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
metadata:
name: default
spec:
hostCollectors:
- copy:
collectorName: "apiserver-audit-logs"
path: ./log/k8s-audit* # ls log
-rw-------@ 1 arcolife staff 100M Sep 14 02:29 k8s-audit-2023-09-06T07-32-52.651.log
-rw-------@ 1 arcolife staff 100M Sep 14 02:29 k8s-audit-2023-09-06T10-52-44.261.log
-rw-------@ 1 arcolife staff 100M Sep 14 02:29 k8s-audit-2023-09-06T14-07-35.432.log
-rw-------@ 1 arcolife staff 100M Sep 14 02:29 k8s-audit-2023-09-06T17-28-43.581.log
-rw-------@ 1 arcolife staff 100M Sep 14 02:29 k8s-audit-2023-09-07T07-14-41.214.log
-rw-------@ 1 arcolife staff 100M Sep 14 02:29 k8s-audit-2023-09-07T13-30-36.751.log
-rw-------@ 1 arcolife staff 49M Sep 14 02:29 k8s-audit.log
|
I did this one on aws ec2 machine with 4 CPU, 16G ram with
main branch: |
I think we need to read a line without break for regex, otherwise the regex search is not precisely. So bufio.Scanner read by line can help it. However, large file in one line that is quite difficult to handle in current way |
Difference between
No. Reading that file will result in an error in the lines of
Our implementation of redaction has been designed to process one line at time. This optimization is addressing huge one line inputs
No. Previous implementation converts bytes to strings which leads to new string objects being created. I'm proposing using the bytes as they are read.
I meant the buffer created here. This line gets called multiple times and each time it allocates 1MB or memory. Using a bundle with 100 files, 1800MBs will be allocated "instantly" (depending on how fast goroutines start up)
In my tests, it doesn't look like the performance is getting impacted. I can provide some measurements here.
We we a bit too greedy in the memory we reserved as buffers for data read in from files. |
Thanks for the explanation on everything. This in particular was helpful to understand. |
pkg/redact/literal.go
Outdated
|
||
// io.WriteString would be nicer, but scanner strips new lines | ||
fmt.Fprintf(writer, "%s\n", clean) | ||
_, err = writer.Write(append(clean, '\n')) // Append newline since scanner strips it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think if the line is really long and you use append(line, '\n')
you will allocate another up to 10M. wouldnt it just be better to write twice?
_, err = writer.Write(append(clean, '\n')) // Append newline since scanner strips it | |
_, err = writer.Write(clean) | |
_, err = writer.Write('\n') // Append newline since scanner strips it |
this goes for all occurrences of append below
pkg/redact/multi_line.go
Outdated
if err != nil { | ||
return | ||
} | ||
_, err = writer.Write(append(clean, '\n')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we also change it to write twice here?
pkg/redact/single_line.go
Outdated
|
||
_, err = writer.Write(append(clean, '\n')) // Append newline since scanner strips it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also here, maybe need change to write twice
Thus far, confirming smoke tests ✅ for replicatedhq/kots#4031 with latest fixes |
Description, Motivation and Context
Redactor memory optimisations
[]byte
overstring
s. Strings are immutable and lead to more memory allocationsbufio.Scanner
where possible. Reading raw bytes until a newline is reached has the likely hood of using up too much heap memory leading to OOMs. This is possible when a large enough file, without newlines, is read. Withbufio.Scanner
, there is a10MB
limitFixes: #1331
Checklist
Does this PR introduce a breaking change?