mirror of
https://github.com/lukaszraczylo/go-telegram.git
synced 2026-07-03 05:23:30 +00:00
perf(client): pool req-body buffer + manual http.Request with cached URL
Two changes that together cut allocs/call from 15 to 13 (client-internal bench) and per-call CPU from 600ns to 455ns (-24%) on the no-HTTP path: 1. Codec gets an optional BodyEncoder extension (MarshalTo io.Writer). When present, encodeJSONBody stream-encodes the request directly into a pooled *bytes.Buffer instead of allocating a [2-step] Marshal+Reader pair. DefaultCodec implements it via goccy/go-json.NewEncoder. 2. *Bot caches the parsed base URL on construction. buildRequest skips net/http.NewRequestWithContext for the common case and constructs *http.Request manually — clones the URL by value, sets the method path, and populates ContentLength + GetBody from the body's concrete type so RetryDoer's body-replay across attempts still works. Cross-library bench (sendMessage round-trip vs httptest.Server): -2 allocs/call (104 -> 102), bytes -1.2%, time within noise (real HTTP plumbing dominates). The CPU win is visible on synthetic stub-doer benches and translates to lower GC pressure on sustained-throughput workloads. Slow-path fallback preserved for codecs that don't implement BodyEncoder and for *Bot instances where url.Parse on the configured base failed — they take the original NewRequestWithContext path.
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
)
|
||||
|
||||
const defaultBaseURL = "https://api.telegram.org"
|
||||
|
||||
// Bot is the Telegram Bot API client. Construct via New. All API methods
|
||||
@@ -10,6 +14,13 @@ type Bot struct {
|
||||
http HTTPDoer
|
||||
codec Codec
|
||||
logger Logger
|
||||
|
||||
// baseURL is the parsed form of base, lazily populated on first Call.
|
||||
// Caching it avoids running url.Parse on every API request.
|
||||
baseURL *url.URL
|
||||
// pathPrefix is "/bot<token>/" built once so per-call URL assembly
|
||||
// is a single string concatenation with the method name.
|
||||
pathPrefix string
|
||||
}
|
||||
|
||||
// Token returns the bot token. Exposed for advanced use cases (custom
|
||||
@@ -44,5 +55,13 @@ func New(token string, opts ...Option) *Bot {
|
||||
for _, o := range opts {
|
||||
o(b)
|
||||
}
|
||||
// Pre-compute URL pieces. Errors here are unlikely (defaultBaseURL is
|
||||
// well-formed; user-supplied bases via WithBaseURL are validated by
|
||||
// url.Parse below) but if parsing fails we leave baseURL nil and fall
|
||||
// back to the string-concat path on the next Call.
|
||||
if u, err := url.Parse(b.base); err == nil {
|
||||
b.baseURL = u
|
||||
}
|
||||
b.pathPrefix = "/bot" + b.token + "/"
|
||||
return b
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user