FetchData fetchData(FetchRequest request) throws IOException, RrdException {
		long arcStep = getArcStep();
		long fetchStart = Util.normalize(request.getFetchStart(), arcStep);
		long fetchEnd = Util.normalize(request.getFetchEnd(), arcStep);
		if(fetchEnd < request.getFetchEnd()) {
			fetchEnd += arcStep;
		}
		long startTime = getStartTime();
		long endTime = getEndTime();
		String[] dsToFetch = request.getFilter();
		if(dsToFetch == null) {
			dsToFetch = parentDb.getDsNames();
		}
		int dsCount = dsToFetch.length;
		int ptsCount = (int) ((fetchEnd - fetchStart) / arcStep + 1);
		long[] timestamps = new long[ptsCount];
		double[][] values = new double[dsCount][ptsCount];
		long matchStartTime = Math.max(fetchStart, startTime);
		long matchEndTime = Math.min(fetchEnd, endTime);
		double[][] robinValues = null;
		if(matchStartTime <= matchEndTime) {
			// preload robin values
			int matchCount = (int)((matchEndTime - matchStartTime) / arcStep + 1);
			int matchStartIndex = (int)((matchStartTime - startTime) / arcStep);
			robinValues = new double[dsCount][];
			for(int i = 0; i < dsCount; i++) {
				int dsIndex = parentDb.getDsIndex(dsToFetch[i]);
				robinValues[i] = robins[dsIndex].getValues(matchStartIndex, matchCount);
			}
		}
		for(int ptIndex = 0; ptIndex < ptsCount; ptIndex++) {
			long time = fetchStart + ptIndex * arcStep;
			timestamps[ptIndex] = time;
			for(int i = 0; i < dsCount; i++) {
				double value = Double.NaN;
				if(time >= matchStartTime && time <= matchEndTime) {
					// inbound time
					int robinValueIndex = (int)((time - matchStartTime) / arcStep);
					value = robinValues[i][robinValueIndex];
				}
				values[i][ptIndex] = value;
			}
		}
		FetchData fetchData = new FetchData(this, request);
		fetchData.setTimestamps(timestamps);
		fetchData.setValues(values);
		return fetchData;
	}