func LastNLinesChunked(a Artifact, n, chunkSize int64) ([]string, error) {
	toRead := chunkSize + 1 // Add 1 for exclusive upper bound read range
	chunks := int64(1)
	var contents []byte
	var linesInContents int64
	artifactSize, err := a.Size()
	if err != nil {
		return nil, fmt.Errorf("error getting artifact size: %v", err)
	}
	offset := artifactSize - chunks*chunkSize
	lastOffset := offset
	var lastRead int64
	for linesInContents < n && offset != 0 {
		offset = lastOffset - lastRead
		if offset < 0 {
			toRead = offset + chunkSize + 1
			offset = 0
		}
		bytesRead := make([]byte, toRead)
		numBytesRead, err := a.ReadAt(bytesRead, offset)
		if err != nil && err != io.EOF {
			return nil, fmt.Errorf("error reading artifact: %v", err)
		}
		lastRead = int64(numBytesRead)
		lastOffset = offset
		bytesRead = bytes.Trim(bytesRead, "\x00")
		linesInContents += int64(bytes.Count(bytesRead, []byte("\n")))
		contents = append(bytesRead, contents...)
		chunks++
	}

	var lines []string
	scanner := bufio.NewScanner(bytes.NewReader(contents))
	scanner.Split(bufio.ScanLines)
	for scanner.Scan() {
		line := scanner.Text()
		lines = append(lines, line)
	}
	l := int64(len(lines))
	if l < n {
		return lines, nil
	}
	return lines[l-n:], nil
}
func LastNLinesChunked(a Artifact, n, chunkSize int64) ([]string, error) {
	toRead := chunkSize + 1 // Add 1 for exclusive upper bound read range
	chunks := int64(1)
	var contents []byte
	var linesInContents int64
	artifactSize, err := a.Size()
	if err != nil {
		return nil, fmt.Errorf("error getting artifact size: %v", err)
	}
	offset := artifactSize - chunks*chunkSize
	lastOffset := offset
	var lastRead int64
	for linesInContents < n && offset != 0 {
		offset = lastOffset - lastRead
		if offset < 0 {
			toRead = offset + chunkSize + 1
			offset = 0
		}
		bytesRead := make([]byte, toRead)
		numBytesRead, err := a.ReadAt(bytesRead, offset)
		if err != nil && err != io.EOF {
			return nil, fmt.Errorf("error reading artifact: %v", err)
		}
		lastRead = int64(numBytesRead)
		lastOffset = offset
		bytesRead = bytes.Trim(bytesRead, "\x00")
		linesInContents += int64(bytes.Count(bytesRead, []byte("\n")))
		contents = append(bytesRead, contents...)
		chunks++
	}

	var lines []string
	scanner := bufio.NewScanner(bytes.NewReader(contents))
	scanner.Split(bufio.ScanLines)
	for scanner.Scan() {
		line := scanner.Text()
		lines = append(lines, line)
	}
	l := int64(len(lines))
	if l < n {
		return lines, nil
	}
	return lines[l-n:], nil
}
// LastNLinesChunked reads the last n lines from an artifact by reading chunks of size chunkSize
// from the end of the artifact. Best performance is achieved by:
// argmin 0<chunkSize<INTMAX, f(chunkSize) = chunkSize - n * avgLineLength