feat: add sync pool

main
sudo pacman -Syu 2023-03-05 17:23:25 +07:00
parent 3e672cb1c1
commit 0b504aafc1
2 changed files with 116 additions and 5 deletions

View File

@ -307,6 +307,45 @@ internal
Please don't use external libs for WorkerPool, I don't want to deal with
dependency hell.
</p>
<h3>
<a
id="user-content-use-syncpool-when-need-to-reuse-object-mainly-for-bytesbuffer"
class="anchor"
aria-hidden="true"
href="#use-syncpool-when-need-to-reuse-object-mainly-for-bytesbuffer"
><span aria-hidden="true" class="octicon octicon-link"></span></a
>Use
<a href="https://pkg.go.dev/sync#Pool" rel="nofollow">sync.Pool</a> when
need to reuse object, mainly for <code>bytes.Buffer</code>
</h3>
<p>Example:</p>
<div class="highlight highlight-source-go">
<pre><span class="pl-k">var</span> <span class="pl-s1">bufPool</span> <span class="pl-c1">=</span> sync.<span class="pl-smi">Pool</span>{
<span class="pl-c1">New</span>: <span class="pl-k">func</span>() <span class="pl-smi">any</span> {
<span class="pl-k">return</span> <span class="pl-en">new</span>(bytes.<span class="pl-smi">Buffer</span>)
},
}
<span class="pl-k">func</span> <span class="pl-en">MarshalWithoutEscapeHTML</span>(<span class="pl-s1">v</span> <span class="pl-smi">any</span>) ([]<span class="pl-smi">byte</span>, <span class="pl-smi">error</span>) {
<span class="pl-s1">b</span>, <span class="pl-s1">ok</span> <span class="pl-c1">:=</span> <span class="pl-s1">bufPool</span>.<span class="pl-en">Get</span>().(<span class="pl-c1">*</span>bytes.<span class="pl-smi">Buffer</span>)
<span class="pl-k">if</span> <span class="pl-c1">!</span><span class="pl-s1">ok</span> {
<span class="pl-k">return</span> <span class="pl-c1">nil</span>, <span class="pl-s1">ErrBufPoolNotBytesBuffer</span>
}
<span class="pl-s1">b</span>.<span class="pl-en">Reset</span>()
<span class="pl-k">defer</span> <span class="pl-s1">bufPool</span>.<span class="pl-en">Put</span>(<span class="pl-s1">b</span>)
<span class="pl-s1">encoder</span> <span class="pl-c1">:=</span> <span class="pl-s1">json</span>.<span class="pl-en">NewEncoder</span>(<span class="pl-s1">b</span>)
<span class="pl-s1">encoder</span>.<span class="pl-en">SetEscapeHTML</span>(<span class="pl-c1">false</span>)
<span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">encoder</span>.<span class="pl-en">Encode</span>(<span class="pl-s1">v</span>); <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
<span class="pl-k">return</span> <span class="pl-c1">nil</span>, <span class="pl-s1">err</span>
}
<span class="pl-s1">result</span> <span class="pl-c1">:=</span> <span class="pl-en">make</span>([]<span class="pl-smi">byte</span>, <span class="pl-s1">b</span>.<span class="pl-en">Len</span>())
<span class="pl-en">copy</span>(<span class="pl-s1">result</span>, <span class="pl-s1">b</span>.<span class="pl-en">Bytes</span>())
<span class="pl-k">return</span> <span class="pl-s1">result</span>, <span class="pl-c1">nil</span>
}</pre>
</div>
<h2>
<a
id="user-content-external-libs"
@ -433,6 +472,14 @@ internal
Don't use <code>gin.Context</code> when pass context from handler layer to
service layer, use <code>gin.Context.Request.Context()</code> instead.
</p>
<p>Remember to free resources after parse multipart form:</p>
<div class="highlight highlight-source-go">
<pre><span class="pl-k">defer</span> <span class="pl-k">func</span>() {
<span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">c</span>.<span class="pl-c1">Request</span>.<span class="pl-c1">MultipartForm</span>.<span class="pl-en">RemoveAll</span>(); <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
<span class="pl-s1">fmt</span>.<span class="pl-en">Println</span>(<span class="pl-s1">err</span>)
}
}()</pre>
</div>
<h3>
<a
id="user-content-if-you-want-log-just-use-uber-gozap"
@ -683,9 +730,9 @@ stringer -type=Drink</pre>
<a href="https://github.com/go-redis/redis_rate">go-redis/redis_rate</a>
</h3>
<p>
rate if you want rate limiter locally in your single instance of service.
redis_rate if you want rate limiter distributed across all your instances
of service.
<strong>rate</strong> if you want rate limiter locally in your single
instance of service. <strong>redis_rate</strong> if you want rate limiter
distributed across all your instances of service.
</p>
<h3>
<a
@ -761,6 +808,25 @@ fieldalignment -fix ./internal/business/<span class="pl-k">*</span>.go</pre>
>Three bugs in the Go MySQL Driver</a
>
</li>
<li>
<a href="https://mtlynch.io/notes/picoshare-perf/" rel="nofollow"
>Fixing Memory Exhaustion Bugs in My Golang Web App</a
>
</li>
<li>
<a
href="https://www.commonfate.io/blog/prevent-logging-secrets-in-go-by-using-custom-types"
rel="nofollow"
>Prevent Logging Secrets in Go by Using Custom Types</a
>
</li>
<li>
<a
href="https://jonwillia.ms/2019/12/22/conditional-gomock-mockgen"
rel="nofollow"
>Speed Up GoMock with Conditional Generation</a
>
</li>
</ul>
<div>

View File

@ -147,6 +147,38 @@ if err := eg.Wait(); err != nil {
Please don't use external libs for WorkerPool, I don't want to deal with dependency hell.
### Use [sync.Pool](https://pkg.go.dev/sync#Pool) when need to reuse object, mainly for `bytes.Buffer`
Example:
```go
var bufPool = sync.Pool{
New: func() any {
return new(bytes.Buffer)
},
}
func MarshalWithoutEscapeHTML(v any) ([]byte, error) {
b, ok := bufPool.Get().(*bytes.Buffer)
if !ok {
return nil, ErrBufPoolNotBytesBuffer
}
b.Reset()
defer bufPool.Put(b)
encoder := json.NewEncoder(b)
encoder.SetEscapeHTML(false)
if err := encoder.Encode(v); err != nil {
return nil, err
}
result := make([]byte, b.Len())
copy(result, b.Bytes())
return result, nil
}
```
## External libs
### No need `vendor`
@ -202,6 +234,16 @@ prototool is deprecated, and buf can generate, lint, format as good as prototool
Don't use `gin.Context` when pass context from handler layer to service layer, use `gin.Context.Request.Context()` instead.
Remember to free resources after parse multipart form:
```go
defer func() {
if err := c.Request.MultipartForm.RemoveAll(); err != nil {
fmt.Println(err)
}
}()
```
### If you want log, just use [uber-go/zap](https://github.com/uber-go/zap)
It is fast!
@ -342,8 +384,8 @@ stringer -type=Drink
### Don't waste your time rewrite rate limiter if your use case is simple, use [rate](https://pkg.go.dev/golang.org/x/time/rate) or [go-redis/redis_rate](https://github.com/go-redis/redis_rate)
rate if you want rate limiter locally in your single instance of service.
redis_rate if you want rate limiter distributed across all your instances of service.
**rate** if you want rate limiter locally in your single instance of service.
**redis_rate** if you want rate limiter distributed across all your instances of service.
### Replace `go fmt`, `goimports` with [mvdan/gofumpt](https://github.com/mvdan/gofumpt).
@ -370,3 +412,6 @@ fieldalignment -fix ./internal/business/*.go
- [Functional options for friendly APIs](https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis)
- [Google Go Style](https://google.github.io/styleguide/go/index)
- [Three bugs in the Go MySQL Driver](https://github.blog/2020-05-20-three-bugs-in-the-go-mysql-driver/)
- [Fixing Memory Exhaustion Bugs in My Golang Web App](https://mtlynch.io/notes/picoshare-perf/)
- [Prevent Logging Secrets in Go by Using Custom Types](https://www.commonfate.io/blog/prevent-logging-secrets-in-go-by-using-custom-types)
- [Speed Up GoMock with Conditional Generation](https://jonwillia.ms/2019/12/22/conditional-gomock-mockgen)