package reads import ( "github.com/influxdata/influxdb/storage/reads/datatypes" "github.com/influxdata/influxdb/tsdb/cursors" ) {{range .}} func (w *ResponseWriter) get{{.Name}}PointsFrame() *datatypes.ReadResponse_Frame_{{.Name}}Points { var res *datatypes.ReadResponse_Frame_{{.Name}}Points if len(w.buffer.{{.Name}}) > 0 { i := len(w.buffer.{{.Name}}) - 1 res = w.buffer.{{.Name}}[i] w.buffer.{{.Name}}[i] = nil w.buffer.{{.Name}} = w.buffer.{{.Name}}[:i] } else { res = &datatypes.ReadResponse_Frame_{{.Name}}Points{ {{.Name}}Points: &datatypes.ReadResponse_{{.Name}}PointsFrame{ Timestamps: make([]int64, 0, batchSize), Values: make([]{{.Type}}, 0, batchSize), }, } } return res } func (w *ResponseWriter) put{{.Name}}PointsFrame(f *datatypes.ReadResponse_Frame_{{.Name}}Points) { f.{{.Name}}Points.Timestamps = f.{{.Name}}Points.Timestamps[:0] f.{{.Name}}Points.Values = f.{{.Name}}Points.Values[:0] w.buffer.{{.Name}} = append(w.buffer.{{.Name}}, f) } func (w *ResponseWriter) stream{{.Name}}ArraySeries(cur cursors.{{.Name}}ArrayCursor) { w.sf.DataType = datatypes.DataType{{.Name}} ss := len(w.res.Frames) - 1 a := cur.Next() if len(a.Timestamps) == 0 { w.sz -= w.sf.Size() w.putSeriesFrame(w.res.Frames[ss].Data.(*datatypes.ReadResponse_Frame_Series)) w.res.Frames = w.res.Frames[:ss] } else if w.sz > writeSize { w.Flush() } } func (w *ResponseWriter) stream{{.Name}}ArrayPoints(cur cursors.{{.Name}}ArrayCursor) { w.sf.DataType = datatypes.DataType{{.Name}} ss := len(w.res.Frames) - 1 p := w.get{{.Name}}PointsFrame() frame := p.{{.Name}}Points w.res.Frames = append(w.res.Frames, datatypes.ReadResponse_Frame{Data: p}) var seriesValueCount = 0 for { // If the number of values produced by cur > 1000, // cur.Next() will produce batches of values that are of // length ≤ 1000. // We attempt to limit the frame Timestamps / Values lengths // the same to avoid allocations. These frames are recycled // after flushing so that on repeated use there should be enough space // to append values from a into frame without additional allocations. a := cur.Next() if len(a.Timestamps) == 0 { break } seriesValueCount += a.Len() // As specified in the struct definition, w.sz is an estimated // size (in bytes) of the buffered data. It is therefore a // deliberate choice to accumulate using the array Size, which is // cheap to calculate. Calling frame.Size() can be expensive // when using varint encoding for numbers. w.sz += a.Size() frame.Timestamps = append(frame.Timestamps, a.Timestamps...) frame.Values = append(frame.Values, a.Values...) // given the expectation of cur.Next, we attempt to limit // the number of values appended to the frame to batchSize (1000) needsFrame := len(frame.Timestamps) >= batchSize if w.sz >= writeSize { needsFrame = true w.Flush() if w.err != nil { break } } if needsFrame { // new frames are returned with Timestamps and Values preallocated // to a minimum of batchSize length to reduce further allocations. p = w.get{{.Name}}PointsFrame() frame = p.{{.Name}}Points w.res.Frames = append(w.res.Frames, datatypes.ReadResponse_Frame{Data: p}) } } w.vc += seriesValueCount if seriesValueCount == 0 { w.sz -= w.sf.Size() w.putSeriesFrame(w.res.Frames[ss].Data.(*datatypes.ReadResponse_Frame_Series)) w.res.Frames = w.res.Frames[:ss] } else if w.sz > writeSize { w.Flush() } } {{end}}