Skip to content

Commit

Permalink
fast local version too
Browse files Browse the repository at this point in the history
  • Loading branch information
jcscottiii committed Nov 27, 2024
1 parent 4e57e95 commit 7da3927
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 57 deletions.
1 change: 1 addition & 0 deletions backend/cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ func main() {
if _, found := os.LookupEnv("SPANNER_EMULATOR_HOST"); found {
slog.Info("setting spanner to local mode")
spannerClient.SetFeatureSearchBaseQuery(gcpspanner.LocalFeatureBaseQuery{})
spannerClient.SetMisingOneImplementationQuery(gcpspanner.LocalMissingOneImplementationQuery{})
}

// Allowed Origin. Can remove after UbP.
Expand Down
10 changes: 8 additions & 2 deletions lib/gcpspanner/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ var ErrInvalidCursorFormat = errors.New("invalid cursor format")
// Client is the client for interacting with GCP Spanner.
type Client struct {
*spanner.Client
featureSearchQuery FeatureSearchBaseQuery
searchCfg searchConfig
featureSearchQuery FeatureSearchBaseQuery
missingOneImplQuery MissingOneImplementationQuery
searchCfg searchConfig
batchWriter
batchSize int
batchWriters int
Expand Down Expand Up @@ -132,6 +133,7 @@ func NewSpannerClient(projectID string, instanceID string, name string) (*Client
return &Client{
client,
GCPFeatureSearchBaseQuery{},
GCPMissingOneImplementationQuery{},
searchConfig{maxOwnedSearchesPerUser: defaultMaxOwnedSearchesPerUser},
bw,
defaultBatchSize,
Expand All @@ -143,6 +145,10 @@ func (c *Client) SetFeatureSearchBaseQuery(query FeatureSearchBaseQuery) {
c.featureSearchQuery = query
}

func (c *Client) SetMisingOneImplementationQuery(query MissingOneImplementationQuery) {
c.missingOneImplQuery = query
}

// WPTRunCursor: Represents a point for resuming queries based on the last
// TimeStart and ExternalRunID. Useful for pagination.
type WPTRunCursor struct {
Expand Down
98 changes: 93 additions & 5 deletions lib/gcpspanner/missing_one_implementation.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,16 @@ import (
)

func init() {
missingOneImplTemplate = NewQueryTemplate(missingOneImplCountRawTemplate)
gcpMissingOneImplTemplate = NewQueryTemplate(gcpMissingOneImplCountRawTemplate)
localMissingOneImplTemplate = NewQueryTemplate(localMissingOneImplCountRawTemplate)
}

// nolint: gochecknoglobals // WONTFIX. Compile the template once at startup. Startup fails if invalid.
var (
// missingOneImplTemplate is the compiled version of missingOneImplCountRawTemplate.
missingOneImplTemplate BaseQueryTemplate
// gcpMissingOneImplTemplate is the compiled version of gcpMissingOneImplCountRawTemplate.
gcpMissingOneImplTemplate BaseQueryTemplate
// localMissingOneImplTemplate is the compiled version of localMissingOneImplCountRawTemplate.
localMissingOneImplTemplate BaseQueryTemplate
)

// MissingOneImplCountPage contains the details for the missing one implementation count request.
Expand Down Expand Up @@ -70,7 +73,62 @@ func encodeMissingOneImplCursor(releaseDate time.Time) string {
})
}

const missingOneImplCountRawTemplate = `
const localMissingOneImplCountRawTemplate = `
WITH WebFeatureIDs AS (
SELECT ID
FROM WebFeatures
),
TargetBrowserUnsupportedFeatures AS (
SELECT bfse.WebFeatureID, bfse.EventReleaseDate
FROM BrowserFeatureSupportEvents bfse
WHERE bfse.TargetBrowserName = @targetBrowserParam
AND bfse.SupportStatus = 'unsupported'
),
OtherBrowsersSupportedFeatures AS (
SELECT
bfse_other.WebFeatureID,
bfse_other.EventReleaseDate,
ARRAY_AGG(DISTINCT bfse_other.TargetBrowserName) AS SupportedBrowsers
FROM
BrowserFeatureSupportEvents bfse_other
WHERE
bfse_other.SupportStatus = 'supported'
GROUP BY
bfse_other.WebFeatureID, bfse_other.EventReleaseDate
)
SELECT releases.EventReleaseDate,
(
SELECT COUNT(DISTINCT wf.ID)
FROM WebFeatureIDs AS wf
INNER JOIN TargetBrowserUnsupportedFeatures tbuf
ON wf.ID = tbuf.WebFeatureID
AND tbuf.EventReleaseDate = releases.EventReleaseDate
INNER JOIN OtherBrowsersSupportedFeatures obsf
ON tbuf.WebFeatureID = obsf.WebFeatureID
AND obsf.EventReleaseDate = tbuf.EventReleaseDate
WHERE
{{ range $browserParamName := .OtherBrowsersParamNames }}
@{{ $browserParamName }} IN UNNEST(obsf.SupportedBrowsers)
AND
{{ end }}
1=1
) AS Count
FROM (
SELECT DISTINCT ReleaseDate AS EventReleaseDate
FROM BrowserReleases
WHERE BrowserName IN UNNEST(@allBrowsersParam)
AND ReleaseDate >= @startAt
AND ReleaseDate < @endAt
{{if .ReleaseDateParam }}
AND ReleaseDate < @{{ .ReleaseDateParam }}
{{end}}
AND ReleaseDate < CURRENT_TIMESTAMP()
) releases
ORDER BY releases.EventReleaseDate DESC
LIMIT @limit;
`

const gcpMissingOneImplCountRawTemplate = `
WITH WebFeatureIDs AS (
SELECT ID
FROM WebFeatures
Expand Down Expand Up @@ -106,6 +164,7 @@ FROM (
{{if .ReleaseDateParam }}
AND ReleaseDate < @{{ .ReleaseDateParam }}
{{end}}
AND ReleaseDate < CURRENT_TIMESTAMP()
) releases
ORDER BY releases.EventReleaseDate DESC
LIMIT @limit;
Expand All @@ -116,13 +175,41 @@ type missingOneImplTemplateData struct {
OtherBrowsersParamNames []string
}

// MissingOneImplementationQuery contains the base query for all missing one implementation
// related queries.
type MissingOneImplementationQuery interface {
Query(missingOneImplTemplateData) string
}

// GCPMissingOneImplementationQuery provides a base query that is optimal for GCP Spanner to retrieve the information
// described in the MissingOneImplementationQuery interface.
type GCPMissingOneImplementationQuery struct{}

func (q GCPMissingOneImplementationQuery) Query(data missingOneImplTemplateData) string {
return gcpMissingOneImplTemplate.Execute(data)
}

// LocalMissingOneImplementationQuery is a version of the base query that works well on the local emulator.
// For some reason, the local emulator takes at least 1 minute with the fake data when using the
// GCPMissingOneImplementationQuery.
// Rather than sacrifice performance for the sake of compatibility, we have this LocalMissingOneImplementationQuery
// implementation which is good for the volume of data locally.
// TODO. Consolidate to using either LocalMissingOneImplementationQuery or GCPMissingOneImplementationQuery to reduce
// the maintenance burden.
type LocalMissingOneImplementationQuery struct{}

func (q LocalMissingOneImplementationQuery) Query(data missingOneImplTemplateData) string {
return localMissingOneImplTemplate.Execute(data)
}

func buildMissingOneImplTemplate(
cursor *missingOneImplCursor,
targetBrowser string,
otherBrowsers []string,
startAt time.Time,
endAt time.Time,
pageSize int,
tmpl MissingOneImplementationQuery,
) spanner.Statement {
params := map[string]interface{}{}
allBrowsers := make([]string, len(otherBrowsers)+1)
Expand Down Expand Up @@ -152,7 +239,7 @@ func buildMissingOneImplTemplate(
ReleaseDateParam: releaseDateParamName,
OtherBrowsersParamNames: otherBrowsersParamNames,
}
sql := missingOneImplTemplate.Execute(tmplData)
sql := tmpl.Query(tmplData)
stmt := spanner.NewStatement(sql)
stmt.Params = params

Expand Down Expand Up @@ -188,6 +275,7 @@ func (c *Client) ListMissingOneImplCounts(
startAt,
endAt,
pageSize,
c.missingOneImplQuery,
)

it := txn.Query(ctx, stmt)
Expand Down
71 changes: 44 additions & 27 deletions lib/gcpspanner/missing_one_implementation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,17 +143,13 @@ func assertListMissingOneImplCounts(ctx context.Context, t *testing.T, startAt,
}
}

func TestListMissingOneImplCounts(t *testing.T) {
restartDatabaseContainer(t)
ctx := context.Background()

loadDataForListMissingOneImplCounts(ctx, t, spannerClient)
actualEvents := spannerClient.readAllBrowserFeatureSupportEvents(ctx, t)
slices.SortFunc(actualEvents, sortBrowserFeatureSupportEvents)
defaultStartAt := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
defaultEndAt := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC)
defaultPageSize := 100

func testMissingOneImplSuite(
ctx context.Context,
t *testing.T,
startAt,
endAt time.Time,
pageSize int,
) {
t.Run("bazBrowser ", func(t *testing.T) {
targetBrowser := "bazBrowser"
otherBrowsers := []string{
Expand Down Expand Up @@ -261,12 +257,12 @@ func TestListMissingOneImplCounts(t *testing.T) {
assertListMissingOneImplCounts(
ctx,
t,
defaultStartAt,
defaultEndAt,
startAt,
endAt,
nil,
targetBrowser,
otherBrowsers,
defaultPageSize,
pageSize,
expectedResult,
)
})
Expand Down Expand Up @@ -323,8 +319,8 @@ func TestListMissingOneImplCounts(t *testing.T) {
assertListMissingOneImplCounts(
ctx,
t,
defaultStartAt,
defaultEndAt,
startAt,
endAt,
nil,
targetBrowser,
otherBrowsers,
Expand Down Expand Up @@ -382,8 +378,8 @@ func TestListMissingOneImplCounts(t *testing.T) {
assertListMissingOneImplCounts(
ctx,
t,
defaultStartAt,
defaultEndAt,
startAt,
endAt,
&pageOneToken,
targetBrowser,
otherBrowsers,
Expand All @@ -410,8 +406,8 @@ func TestListMissingOneImplCounts(t *testing.T) {
assertListMissingOneImplCounts(
ctx,
t,
defaultStartAt,
defaultEndAt,
startAt,
endAt,
&pageTwoToken,
targetBrowser,
otherBrowsers,
Expand Down Expand Up @@ -474,7 +470,7 @@ func TestListMissingOneImplCounts(t *testing.T) {
nil,
targetBrowser,
otherBrowsers,
defaultPageSize,
pageSize,
expectedResult,
)
})
Expand Down Expand Up @@ -538,12 +534,12 @@ func TestListMissingOneImplCounts(t *testing.T) {
assertListMissingOneImplCounts(
ctx,
t,
defaultStartAt,
defaultEndAt,
startAt,
endAt,
nil,
targetBrowser,
otherBrowsers,
defaultPageSize,
pageSize,
expectedResult,
)
})
Expand Down Expand Up @@ -657,13 +653,34 @@ func TestListMissingOneImplCounts(t *testing.T) {
assertListMissingOneImplCounts(
ctx,
t,
defaultStartAt,
defaultEndAt,
startAt,
endAt,
nil,
targetBrowser,
otherBrowsers,
defaultPageSize,
pageSize,
expectedResult,
)
})
}

func TestListMissingOneImplCounts(t *testing.T) {
restartDatabaseContainer(t)
ctx := context.Background()

loadDataForListMissingOneImplCounts(ctx, t, spannerClient)
actualEvents := spannerClient.readAllBrowserFeatureSupportEvents(ctx, t)
slices.SortFunc(actualEvents, sortBrowserFeatureSupportEvents)
defaultStartAt := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
defaultEndAt := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC)
defaultPageSize := 100

t.Run("GCPMissingOneImplementationQuery", func(t *testing.T) {
testMissingOneImplSuite(ctx, t, defaultStartAt, defaultEndAt, defaultPageSize)
})

t.Run("LocalMissingOneImplementationQuery", func(t *testing.T) {
spannerClient.SetMisingOneImplementationQuery(LocalMissingOneImplementationQuery{})
testMissingOneImplSuite(ctx, t, defaultStartAt, defaultEndAt, defaultPageSize)
})
}
Loading

0 comments on commit 7da3927

Please sign in to comment.