mirror of
				https://github.com/coredns/coredns.git
				synced 2025-11-03 18:53:13 -05:00 
			
		
		
		
	- added dnstapEncoder object which incapsulates marshalling of dnstap
   messages to protobuf and writing data to connection
 - dnstapEncoder writes data directly to connection object. It doesn't
   use the framestream's "write" method, because it writes data to
   intermediate buffer (bufio.Writer) which leads to unnecessary
   data copying and drops the performance
 - dnstapEncoder reuses a preallocated buffer for marshalling dnstap
   messages. Many messages are added to the same buffer. They are
   separated with a "frame length" 4-byte values, so the buffer content
   is writen to connection object in the format compatible with
   framestream library
 - added test which guarantees that dnstapEncoder output is the same
   as framestream Encoder output
 - the performance increase is about 50% in (dio *dnstapIO) serve() method
   of dnstap plugin. The overall coredns performance increase is about 10%
   in the following configuration:
   .:1053 {
       erratic {
           drop 0
           truncate 0
           delay 0
       }
       dnstap tcp://127.0.0.1:6000 full
       errors stdout
   }
   tested with dnsperf tool
		
	
		
			
				
	
	
		
			93 lines
		
	
	
		
			1.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			93 lines
		
	
	
		
			1.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package dnstapio
 | 
						|
 | 
						|
import (
 | 
						|
	"encoding/binary"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
 | 
						|
	tap "github.com/dnstap/golang-dnstap"
 | 
						|
	fs "github.com/farsightsec/golang-framestream"
 | 
						|
	"github.com/golang/protobuf/proto"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	frameLenSize = 4
 | 
						|
	protobufSize = 1024 * 1024
 | 
						|
)
 | 
						|
 | 
						|
type dnstapEncoder struct {
 | 
						|
	fse    *fs.Encoder
 | 
						|
	opts   *fs.EncoderOptions
 | 
						|
	writer io.Writer
 | 
						|
	buffer *proto.Buffer
 | 
						|
}
 | 
						|
 | 
						|
func newDnstapEncoder(o *fs.EncoderOptions) *dnstapEncoder {
 | 
						|
	return &dnstapEncoder{
 | 
						|
		opts:   o,
 | 
						|
		buffer: proto.NewBuffer(make([]byte, 0, protobufSize)),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (enc *dnstapEncoder) resetWriter(w io.Writer) error {
 | 
						|
	fse, err := fs.NewEncoder(w, enc.opts)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	if err = fse.Flush(); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	enc.fse = fse
 | 
						|
	enc.writer = w
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (enc *dnstapEncoder) writeMsg(msg *tap.Dnstap) error {
 | 
						|
	if len(enc.buffer.Bytes()) >= protobufSize {
 | 
						|
		if err := enc.flushBuffer(); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	bufLen := len(enc.buffer.Bytes())
 | 
						|
	// add placeholder for frame length
 | 
						|
	if err := enc.buffer.EncodeFixed32(0); err != nil {
 | 
						|
		enc.buffer.SetBuf(enc.buffer.Bytes()[:bufLen])
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	if err := enc.buffer.Marshal(msg); err != nil {
 | 
						|
		enc.buffer.SetBuf(enc.buffer.Bytes()[:bufLen])
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	enc.encodeFrameLen(enc.buffer.Bytes()[bufLen:])
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (enc *dnstapEncoder) flushBuffer() error {
 | 
						|
	if enc.fse == nil || enc.writer == nil {
 | 
						|
		return fmt.Errorf("no writer")
 | 
						|
	}
 | 
						|
 | 
						|
	buf := enc.buffer.Bytes()
 | 
						|
	written := 0
 | 
						|
	for written < len(buf) {
 | 
						|
		n, err := enc.writer.Write(buf[written:])
 | 
						|
		written += n
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	enc.buffer.Reset()
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (enc *dnstapEncoder) encodeFrameLen(buf []byte) {
 | 
						|
	binary.BigEndian.PutUint32(buf, uint32(len(buf)-4))
 | 
						|
}
 | 
						|
 | 
						|
func (enc *dnstapEncoder) close() error {
 | 
						|
	if enc.fse != nil {
 | 
						|
		return enc.fse.Close()
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 |