func (cur *crsr) collectPos() string {
	var sb strings.Builder
	first := true
	for jn, jd := range cur.jDescs {
		if !first {
			sb.WriteString(cPosJrnlSplit)
		} else {
			first = false
		}
		sb.WriteString(jn)
		sb.WriteString(cPosJrnlVal)
		sb.WriteString(jd.it.Pos().String())
	}
	return sb.String()
}
collectPos walks over all iterators to collect their current position
package cursor
import (
	"context"
	"fmt"
	"github.com/jrivets/log4g"
	"github.com/logrange/logrange/pkg/lql"
	"github.com/logrange/logrange/pkg/model"
	"github.com/logrange/logrange/pkg/model/tag"
	"github.com/logrange/logrange/pkg/utils"
	"github.com/logrange/range/pkg/records"
	"github.com/logrange/range/pkg/records/journal"
	"github.com/pkg/errors"
	"io"
	"math"
	"strings"
)
type (
	State struct {
		Id uint64
		Query string
		Src string
		Pos string
	}
	Cursor interface {
		model.Iterator
		Id() uint64
		Offset(ctx context.Context, offs int)
		ApplyState(state State) error
		State(context.Context) State
		WaitNewData(ctx context.Context) error
	}
	crsr struct {
		logger log4g.Logger
		state  State
		it     model.Iterator
		itf    ItFactory
		jDescs map[string]*jrnlDesc
	}
	jrnlDesc struct {
		tags tag.Line
		j    journal.Journal
		it   journal.Iterator
	}
)
var errNoSources = errors.Errorf("no sources for the expression the query")
func newCursor(ctx context.Context, state State, itf ItFactory) (*crsr, error) {
	sel, srcs, err := getSourcesByState(ctx, state, itf)
	if err != nil {
		return nil, err
	}
	if len(srcs) == 0 {
		return nil, errNoSources
	}
	var tmr *model.TimeRange
	if sel.Range != nil {
		tmr = &model.TimeRange{}
		tmr.MinTs = utils.GetInt64Val((*int64)(sel.Range.TmPoint1), 0)
		tmr.MaxTs = utils.GetInt64Val((*int64)(sel.Range.TmPoint2), math.MaxInt64)
	}
	jd := make(map[string]*jrnlDesc, len(srcs))
	var it model.Iterator
	if len(srcs) == 1 {
		for tags, jrnl := range srcs {
			jit := itf.Itearator(jrnl, tmr)
			it = (&model.LogEventIterator{}).Wrap(tags, jit)
			jd[jrnl.Name()] = &jrnlDesc{tags, jrnl, jit}
		}
	} else {
		mxs := make([]model.Iterator, len(srcs))
		i := 0
		for tags, jrnl := range srcs {
			jit := itf.Itearator(jrnl, tmr)
			jd[jrnl.Name()] = &jrnlDesc{tags, jrnl, jit}
			mxs[i] = (&model.LogEventIterator{}).Wrap(tags, jit)
			i++
		}
		for len(mxs) > 1 {
			for i := 0; i < len(mxs)-1; i += 2 {
				m := &model.Mixer{}
				m.Init(model.GetEarliest, mxs[i], mxs[i+1])
				mxs[i/2] = m
			}
			if len(mxs)&1 == 1 {
				mxs[len(mxs)/2] = mxs[len(mxs)-1]
				mxs = mxs[:len(mxs)/2+1]
			} else {
				mxs = mxs[:len(mxs)/2]
			}
		}
		it = mxs[0]
	}
	if sel.Where != nil || tmr != nil {
		it, err = newFIterator(it, sel.Where, tmr)
		if err != nil {
			releaseJournals(itf, srcs)
			return nil, errors.Wrapf(err, "could not create filter for %s ", state.Query)
		}
	}
	cur := new(crsr)
	cur.logger = log4g.GetLogger("cursor")
	cur.state = state
	cur.it = it
	cur.jDescs = jd
	cur.itf = itf
	if err := cur.applyPos(); err != nil {
		releaseJournals(itf, srcs)
		return nil, errors.Wrapf(err, "the position %s could not be applied ", state.Pos)
	}
	return cur, nil
}
func getSourcesByState(ctx context.Context, state State, itf ItFactory) (sel *lql.Select, srcs map[tag.Line]journal.Journal, err error) {
	if state.Query != "" {
		l, err1 := lql.ParseLql(state.Query)
		if err1 != nil {
			err = errors.Wrapf(err1, "could not parse lql=%s, expecting select statement", state.Query)
			return
		}
		if err1 != nil || l.Select == nil {
			err = errors.Wrapf(err1, "could not parse lql=%s, expecting select statement", state.Query)
			return
		}
		sel = l.Select
		if state.Src == "" {
			srcs, err = itf.GetJournals(ctx, sel.Source, 50)
		}
	} else {
		sel = &lql.Select{}
	}
	if state.Src != "" {
		ts, jrnl, err1 := itf.GetJournal(ctx, state.Src)
		if err1 != nil {
			err = errors.Wrapf(err1, "could not obtain journal by src=%s for the state=%s", state.Src, state)
			return
		}
		srcs = map[tag.Line]journal.Journal{ts.Line(): jrnl}
	}
	return
}
func releaseJournals(itf ItFactory, srcs map[tag.Line]journal.Journal) {
	for _, j := range srcs {
		itf.Release(j.Name())
	}
}
func (cur *crsr) String() string {
	return fmt.Sprintf("{descs:%d, state:%s}", len(cur.jDescs), cur.state.String())
}
func (cur *crsr) Id() uint64 {
	return cur.state.Id
}
func (cur *crsr) Next(ctx context.Context) {
	cur.it.Next(ctx)
}
func (cur *crsr) Get(ctx context.Context) (model.LogEvent, tag.Line, error) {
	return cur.it.Get(ctx)
}
func (cur *crsr) Release() {
	cur.it.Release()
}
func (cur *crsr) SetBackward(bkwd bool) {
	cur.it.SetBackward(bkwd)
}
func (cur *crsr) CurrentPos() records.IteratorPos {
	return cur.it.CurrentPos()
}
func (cur *crsr) Offset(ctx context.Context, offs int) {
	if offs == 0 {
		return
	}
	var pos records.IteratorPos
	pos = records.IteratorPosUnknown
	bkwd := false
	if offs < 0 {
		bkwd = true
		offs = -offs
		_, _, err := cur.Get(ctx)
		pos = cur.CurrentPos()
		cur.SetBackward(true)
		if err == io.EOF {
			cur.Get(ctx)
			pos = cur.CurrentPos()
			offs--
		} else {
			cur.iterateToPos(ctx, pos)
		}
	}
	for offs > 0 {
		cur.Next(ctx)
		offs--
		_, _, err := cur.Get(ctx)
		if err != nil {
			pos = records.IteratorPosUnknown
			break
		}
		pos = cur.CurrentPos()
	}
	if bkwd {
		cur.SetBackward(false)
		cur.iterateToPos(ctx, pos)
	}
}
func (cur *crsr) State(ctx context.Context) State {
	cur.Get(ctx)
	cur.state.Pos = cur.collectPos()
	return cur.state
}
func (cur *crsr) ApplyState(state State) error {
	if cur.state.Query != state.Query || cur.state.Id != state.Id {
		return errors.Errorf("Could not apply state %s to the current cursor state %s", state, cur.state)
	}
	if cur.state.Pos != state.Pos {
		oldPos := cur.state.Pos
		cur.state.Pos = state.Pos
		err := cur.applyStatePos()
		if err != nil {
			cur.state.Pos = oldPos
			return errors.Wrapf(err, "Could not apply position %s to the cursor state %s ", state.Pos, cur.state)
		}
	}
	return nil
}
func (cur *crsr) WaitNewData(ctx context.Context) error {
	cur.it.Release()
	ctx2, cancel := context.WithCancel(ctx)
	for _, it := range cur.jDescs {
		go func(jrnl journal.Journal, pos journal.Pos) {
			jrnl.Chunks().WaitForNewData(ctx2, pos)
			cancel()
		}(it.j, it.it.Pos())
	}
	<-ctx2.Done()
	return ctx.Err()
}
const cPosJrnlSplit = ":"
const cPosJrnlVal = "="
func (cur *crsr) commit(ctx context.Context) State {
	st := cur.State(ctx)
	cur.it.Release()
	return st
}