rpc/server: encode answers more efficiently
We're at the point where even this code can clearly be seen in profiles. We can save on some buffers (and CPU cycles) by encoding the answer once. Another ~2% TPS for single node.
This commit is contained in:
parent
9187e2ab25
commit
af17bbfeab
2 changed files with 19 additions and 20 deletions
|
@ -24,6 +24,13 @@ type Raw struct {
|
|||
Result json.RawMessage `json:"result,omitempty"`
|
||||
}
|
||||
|
||||
// Abstract represents abstract JSON-RPC 2.0 response, it differs from Raw in
|
||||
// that Result field is an interface here.
|
||||
type Abstract struct {
|
||||
HeaderAndError
|
||||
Result interface{} `json:"result,omitempty"`
|
||||
}
|
||||
|
||||
// Notification is a type used to represent wire format of events, they're
|
||||
// special in that they look like requests but they don't have IDs and their
|
||||
// "method" is actually an event name.
|
||||
|
|
|
@ -230,7 +230,7 @@ func (s *Server) handleHTTPRequest(w http.ResponseWriter, httpRequest *http.Requ
|
|||
s.log.Info("websocket connection upgrade failed", zap.Error(err))
|
||||
return
|
||||
}
|
||||
resChan := make(chan response.Raw)
|
||||
resChan := make(chan response.Abstract)
|
||||
subChan := make(chan *websocket.PreparedMessage, notificationBufSize)
|
||||
subscr := &subscriber{writer: subChan, ws: ws}
|
||||
s.subsLock.Lock()
|
||||
|
@ -262,13 +262,13 @@ func (s *Server) handleHTTPRequest(w http.ResponseWriter, httpRequest *http.Requ
|
|||
s.writeHTTPServerResponse(req, w, resp)
|
||||
}
|
||||
|
||||
func (s *Server) handleRequest(req *request.In, sub *subscriber) response.Raw {
|
||||
func (s *Server) handleRequest(req *request.In, sub *subscriber) response.Abstract {
|
||||
var res interface{}
|
||||
var resErr *response.Error
|
||||
|
||||
reqParams, err := req.Params()
|
||||
if err != nil {
|
||||
return s.packResponseToRaw(req, nil, response.NewInvalidParamsError("Problem parsing request parameters", err))
|
||||
return s.packResponse(req, nil, response.NewInvalidParamsError("Problem parsing request parameters", err))
|
||||
}
|
||||
|
||||
s.log.Debug("processing rpc request",
|
||||
|
@ -287,10 +287,10 @@ func (s *Server) handleRequest(req *request.In, sub *subscriber) response.Raw {
|
|||
res, resErr = handler(s, *reqParams, sub)
|
||||
}
|
||||
}
|
||||
return s.packResponseToRaw(req, res, resErr)
|
||||
return s.packResponse(req, res, resErr)
|
||||
}
|
||||
|
||||
func (s *Server) handleWsWrites(ws *websocket.Conn, resChan <-chan response.Raw, subChan <-chan *websocket.PreparedMessage) {
|
||||
func (s *Server) handleWsWrites(ws *websocket.Conn, resChan <-chan response.Abstract, subChan <-chan *websocket.PreparedMessage) {
|
||||
pingTicker := time.NewTicker(wsPingPeriod)
|
||||
eventloop:
|
||||
for {
|
||||
|
@ -337,7 +337,7 @@ drainloop:
|
|||
}
|
||||
}
|
||||
|
||||
func (s *Server) handleWsReads(ws *websocket.Conn, resChan chan<- response.Raw, subscr *subscriber) {
|
||||
func (s *Server) handleWsReads(ws *websocket.Conn, resChan chan<- response.Abstract, subscr *subscriber) {
|
||||
ws.SetReadLimit(wsReadLimit)
|
||||
ws.SetReadDeadline(time.Now().Add(wsPongLimit))
|
||||
ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(wsPongLimit)); return nil })
|
||||
|
@ -712,7 +712,7 @@ func (s *Server) getStorage(ps request.Params) (interface{}, *response.Error) {
|
|||
|
||||
item := s.chain.GetStorageItem(id, key)
|
||||
if item == nil {
|
||||
return nil, nil
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return hex.EncodeToString(item.Value), nil
|
||||
|
@ -1250,8 +1250,8 @@ func (s *Server) blockHeightFromParam(param *request.Param) (int, *response.Erro
|
|||
return num, nil
|
||||
}
|
||||
|
||||
func (s *Server) packResponseToRaw(r *request.In, result interface{}, respErr *response.Error) response.Raw {
|
||||
resp := response.Raw{
|
||||
func (s *Server) packResponse(r *request.In, result interface{}, respErr *response.Error) response.Abstract {
|
||||
resp := response.Abstract{
|
||||
HeaderAndError: response.HeaderAndError{
|
||||
Header: response.Header{
|
||||
JSONRPC: r.JSONRPC,
|
||||
|
@ -1262,15 +1262,7 @@ func (s *Server) packResponseToRaw(r *request.In, result interface{}, respErr *r
|
|||
if respErr != nil {
|
||||
resp.Error = respErr
|
||||
} else {
|
||||
resJSON, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
s.log.Error("failed to marshal result",
|
||||
zap.Error(err),
|
||||
zap.String("method", r.Method))
|
||||
resp.Error = response.NewInternalServerError("failed to encode result", err)
|
||||
} else {
|
||||
resp.Result = resJSON
|
||||
}
|
||||
resp.Result = result
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
@ -1292,11 +1284,11 @@ func (s *Server) logRequestError(r *request.In, jsonErr *response.Error) {
|
|||
|
||||
// writeHTTPErrorResponse writes an error response to the ResponseWriter.
|
||||
func (s *Server) writeHTTPErrorResponse(r *request.In, w http.ResponseWriter, jsonErr *response.Error) {
|
||||
resp := s.packResponseToRaw(r, nil, jsonErr)
|
||||
resp := s.packResponse(r, nil, jsonErr)
|
||||
s.writeHTTPServerResponse(r, w, resp)
|
||||
}
|
||||
|
||||
func (s *Server) writeHTTPServerResponse(r *request.In, w http.ResponseWriter, resp response.Raw) {
|
||||
func (s *Server) writeHTTPServerResponse(r *request.In, w http.ResponseWriter, resp response.Abstract) {
|
||||
// Errors can happen in many places and we can only catch ALL of them here.
|
||||
if resp.Error != nil {
|
||||
s.logRequestError(r, resp.Error)
|
||||
|
|
Loading…
Reference in a new issue