// 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 }
// 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) } }