// Get returns a tile that is the merged tiles from startIndex to endIndex
// inclusive.
func (m *MergedTiles) Get(scale, startIndex, endIndex int) (*tiling.Tile, error) {
	k := key{
		scale:      scale,
		startIndex: startIndex,
		endIndex:   endIndex,
	}

	tile := m.getFromCache(k)
	if tile != nil {
		return tile, nil
	}

	var err error
	tile, err = m.store.Get(scale, startIndex)
	if err != nil || tile == nil {
		return nil, fmt.Errorf("Failed retrieving tile to merge: %s.", err)
	}
	for i := startIndex + 1; i <= endIndex; i++ {
		// Look for a previously cached Tile that represents [i:end].
		// If found, just merge tile with it and be done.
		rKey := key{
			scale:      scale,
			startIndex: i,
			endIndex:   endIndex,
		}
		if rTile := m.getFromCache(rKey); rTile != nil {
			tile = tiling.Merge(tile, rTile)
			break
		}

		// Otherwise continue building the merged tile on a tile-by-tile basis.
		tile2, err := m.store.Get(scale, i)
		if err != nil || tile2 == nil {
			return nil, fmt.Errorf("Failed retrieving tile to merge: %s.", err)
		}
		tile = tiling.Merge(tile, tile2)
	}

	m.addToCache(k, tile)

	return tile, nil
}
Beispiel #2
0
// getLastTile gets a copy of the last tile for the given scale from disk. Its
// thread safety comes from not using the tile store cache at all.
func (store *FileTileStore) getLastTile(scale int) (*tiling.Tile, error) {
	tilePath := path.Join(store.dir, store.datasetName, fmt.Sprintf("%d/*.gob", scale))
	matches, _ := filepath.Glob(tilePath)
	if matches == nil {
		return nil, fmt.Errorf("Failed to find any tiles in %s", tilePath)
	}
	sort.Strings(matches)
	lastTileName := filepath.Base(matches[len(matches)-1])
	glog.Infof("Found the last tile: %s", lastTileName)
	tileIndex := strings.Split(lastTileName, ".")[0]
	newIndex, err := strconv.ParseInt(tileIndex, 10, 64)
	if err != nil {
		return nil, fmt.Errorf("Unable to get last tile index for scale %d", scale)
	}
	index := int(newIndex)
	filename, err := store.tileFilename(scale, index)
	if err != nil {
		return nil, fmt.Errorf("Unable to get filename for scale %d, index %d", scale, index)
	}
	tileData, err := openTile(filename)
	if err != nil {
		return nil, fmt.Errorf("Unable to open last tile file %s", lastTileName)
	}
	// If possible, merge with the previous tile.
	if index > 0 {
		prevFilename, err := store.tileFilename(scale, index-1)
		if err != nil {
			return nil, fmt.Errorf("Unable to get filename for scale %d, index %d", scale, index)
		}
		prevTile, err := openTile(prevFilename)
		if err != nil {
			return nil, fmt.Errorf("Unable to open prev tile file %s", prevFilename)
		}
		tileData = tiling.Merge(prevTile, tileData)
	}

	return tileData, nil
}
func TestMerge(t *testing.T) {
	t1 := tiling.NewTile()
	t1.Scale = 1
	t1.TileIndex = 20
	t1.Commits[1].Hash = "hash1"

	t2 := tiling.NewTile()
	t2.Scale = 1
	t2.TileIndex = 21
	t2.Commits[1].Hash = "hash33"
	t2.Commits[2].Hash = "hash34"

	t3 := tiling.NewTile()
	t2.Scale = 1
	t2.TileIndex = 22
	t2.Commits[1].Hash = "hash43"
	t2.Commits[2].Hash = "hash44"

	// Create a Trace that exists in both tile1 and tile2.
	tr := NewPerfTrace()
	tr.Params_["p1"] = "v1"
	tr.Params_["p2"] = "v2"
	tr.Values[0] = 0.1
	tr.Values[1] = 0.2

	t1.Traces["foo"] = tr

	tr = NewPerfTrace()
	tr.Params_["p1"] = "v1"
	tr.Params_["p2"] = "v2"
	tr.Params_["p5"] = "5"
	tr.Values[0] = 0.3
	tr.Values[1] = 0.4

	t2.Traces["foo"] = tr

	// Add a trace that only appears in tile2.
	tr = NewPerfTrace()
	tr.Params_["p1"] = "v1"
	tr.Params_["p3"] = "v3"
	tr.Values[0] = 0.5
	tr.Values[1] = 0.6

	t2.Traces["bar"] = tr

	// Merge the two tiles.
	merged := tiling.Merge(tiling.Merge(t1, t2), t3)
	if got, want := len(merged.Traces["foo"].(*PerfTrace).Values), 3*tiling.TILE_SIZE; got != want {
		t.Errorf("Wrong config.TILE_SIZE: Got %v Want %v", got, want)
	}

	if got, want := merged.Scale, 1; got != want {
		t.Errorf("Wrong scale: Got %v Want %v", got, want)
	}
	if got, want := merged.TileIndex, t1.TileIndex; got != want {
		t.Errorf("TileIndex is wrong: Got %v Want %v", got, want)
	}
	if got, want := len(merged.Traces), 2; got != want {
		t.Errorf("Number of traces: Got %v Want %v", got, want)
	}
	if got, want := len(merged.Traces["foo"].(*PerfTrace).Values), 3*tiling.TILE_SIZE; got != want {
		t.Errorf("Number of values: Got %v Want %v", got, want)
	}
	if got, want := len(merged.ParamSet), 4; got != want {
		t.Errorf("ParamSet length: Got %v Want %v", got, want)
	}
	if _, ok := merged.ParamSet["p5"]; !ok {
		t.Errorf("Merged tile missing 'p5' param.")
	}

	// Test the "foo" trace.
	tr = merged.Traces["foo"].(*PerfTrace)
	testCases := []struct {
		N int
		V float64
	}{
		{127, 1e100},
		{50, 0.3},
		{51, 0.4},
		{130, 1e100},
		{0, 0.1},
		{1, 0.2},
		{2, 1e100},
	}
	for _, tc := range testCases {
		if got, want := tr.Values[tc.N], tc.V; got != want {
			t.Errorf("Error copying trace values: Got %v Want %v at %d", got, want, tc.N)
		}
	}
	if got, want := tr.Params()["p1"], "v1"; got != want {
		t.Errorf("Wrong params for trace: Got %v Want %v", got, want)
	}

	// Test the "bar" trace.
	tr = merged.Traces["bar"].(*PerfTrace)
	testCases = []struct {
		N int
		V float64
	}{
		{127, 1e100},
		{50, 0.5},
		{51, 0.6},
		{130, 1e100},
	}
	for _, tc := range testCases {
		if got, want := tr.Values[tc.N], tc.V; got != want {
			t.Errorf("Error copying trace values: Got %v Want %v at %d", got, want, tc.N)
		}
	}
	if got, want := tr.Params()["p3"], "v3"; got != want {
		t.Errorf("Wrong params for trace: Got %v Want %v", got, want)
	}
}