Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
go
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
go
Commits
5a65cbac
Commit
5a65cbac
authored
Dec 23, 2011
by
Andrew Gerrand
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
dashboard: cache packages, introduce caching helpers
R=rsc, gary.burd, adg CC=golang-dev
https://golang.org/cl/5498067
parent
9c2e0b75
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
124 additions
and
157 deletions
+124
-157
misc/dashboard/app/build/build.go
misc/dashboard/app/build/build.go
+3
-2
misc/dashboard/app/build/cache.go
misc/dashboard/app/build/cache.go
+0
-122
misc/dashboard/app/build/handler.go
misc/dashboard/app/build/handler.go
+27
-14
misc/dashboard/app/build/ui.go
misc/dashboard/app/build/ui.go
+12
-19
misc/dashboard/app/cache/cache.go
misc/dashboard/app/cache/cache.go
+82
-0
No files found.
misc/dashboard/app/build/build.go
View file @
5a65cbac
...
...
@@ -5,8 +5,6 @@
package
build
import
(
"appengine"
"appengine/datastore"
"bytes"
"compress/gzip"
"crypto/sha1"
...
...
@@ -15,6 +13,9 @@ import (
"io/ioutil"
"os"
"strings"
"appengine"
"appengine/datastore"
)
const
maxDatastoreStringLen
=
500
...
...
misc/dashboard/app/build/cache.go
deleted
100644 → 0
View file @
9c2e0b75
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package
build
import
(
"appengine"
"appengine/memcache"
"json"
"os"
)
const
(
todoCacheKey
=
"build-todo"
todoCacheExpiry
=
3600
// 1 hour in seconds
uiCacheKey
=
"build-ui"
uiCacheExpiry
=
10
*
60
// 10 minutes in seconds
)
// invalidateCache deletes the build cache records from memcache.
// This function should be called whenever the datastore changes.
func
invalidateCache
(
c
appengine
.
Context
)
{
keys
:=
[]
string
{
uiCacheKey
,
todoCacheKey
}
errs
:=
memcache
.
DeleteMulti
(
c
,
keys
)
for
i
,
err
:=
range
errs
{
if
err
!=
nil
&&
err
!=
memcache
.
ErrCacheMiss
{
c
.
Errorf
(
"memcache.Delete(%q): %v"
,
keys
[
i
],
err
)
}
}
}
// cachedTodo gets the specified todo cache entry (if it exists) from the
// shared todo cache.
func
cachedTodo
(
c
appengine
.
Context
,
todoKey
string
)
(
todo
*
Todo
,
ok
bool
)
{
t
:=
todoCache
(
c
)
if
t
==
nil
{
return
nil
,
false
}
todos
:=
unmarshalTodo
(
c
,
t
)
if
todos
==
nil
{
return
nil
,
false
}
todo
,
ok
=
todos
[
todoKey
]
return
}
// cacheTodo puts the provided todo cache entry into the shared todo cache.
// The todo cache is a JSON-encoded map[string]*Todo, where the key is todoKey.
func
cacheTodo
(
c
appengine
.
Context
,
todoKey
string
,
todo
*
Todo
)
{
// Get the todo cache record (or create a new one).
newItem
:=
false
t
:=
todoCache
(
c
)
if
t
==
nil
{
newItem
=
true
t
=
&
memcache
.
Item
{
Key
:
todoCacheKey
,
Value
:
[]
byte
(
"{}"
),
// default is an empty JSON object
}
}
// Unmarshal the JSON value.
todos
:=
unmarshalTodo
(
c
,
t
)
if
todos
==
nil
{
return
}
// Update the map.
todos
[
todoKey
]
=
todo
// Marshal the updated JSON value.
var
err
os
.
Error
t
.
Value
,
err
=
json
.
Marshal
(
todos
)
if
err
!=
nil
{
// This shouldn't happen.
c
.
Criticalf
(
"marshal todo cache: %v"
,
err
)
return
}
// Set a new expiry.
t
.
Expiration
=
todoCacheExpiry
// Update the cache record (or Set it, if new).
if
newItem
{
err
=
memcache
.
Set
(
c
,
t
)
}
else
{
err
=
memcache
.
CompareAndSwap
(
c
,
t
)
}
if
err
==
memcache
.
ErrCASConflict
||
err
==
memcache
.
ErrNotStored
{
// No big deal if it didn't work; it should next time.
c
.
Warningf
(
"didn't update todo cache: %v"
,
err
)
}
else
if
err
!=
nil
{
c
.
Errorf
(
"update todo cache: %v"
,
err
)
}
}
// todoCache gets the todo cache record from memcache (if it exists).
func
todoCache
(
c
appengine
.
Context
)
*
memcache
.
Item
{
t
,
err
:=
memcache
.
Get
(
c
,
todoCacheKey
)
if
err
!=
nil
{
if
err
!=
memcache
.
ErrCacheMiss
{
c
.
Errorf
(
"get todo cache: %v"
,
err
)
}
return
nil
}
return
t
}
// unmarshalTodo decodes the given item's memcache value into a map.
func
unmarshalTodo
(
c
appengine
.
Context
,
t
*
memcache
.
Item
)
map
[
string
]
*
Todo
{
todos
:=
make
(
map
[
string
]
*
Todo
)
if
err
:=
json
.
Unmarshal
(
t
.
Value
,
&
todos
);
err
!=
nil
{
// This shouldn't happen.
c
.
Criticalf
(
"unmarshal todo cache: %v"
,
err
)
// Kill the bad record.
if
err
:=
memcache
.
Delete
(
c
,
todoCacheKey
);
err
!=
nil
{
c
.
Errorf
(
"delete todo cache: %v"
,
err
)
}
return
nil
}
return
todos
}
misc/dashboard/app/build/handler.go
View file @
5a65cbac
...
...
@@ -5,13 +5,15 @@
package
build
import
(
"appengine"
"appengine/datastore"
"crypto/hmac"
"fmt"
"http"
"json"
"os"
"appengine"
"appengine/datastore"
"cache"
)
const
commitsPerPage
=
30
...
...
@@ -58,7 +60,7 @@ func commitHandler(r *http.Request) (interface{}, os.Error) {
if
err
:=
com
.
Valid
();
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"validating Commit: %v"
,
err
)
}
defer
invalidateCache
(
c
)
defer
cache
.
Tick
(
c
)
tx
:=
func
(
c
appengine
.
Context
)
os
.
Error
{
return
addCommit
(
c
,
com
)
}
...
...
@@ -132,7 +134,7 @@ func tagHandler(r *http.Request) (interface{}, os.Error) {
return
nil
,
err
}
c
:=
appengine
.
NewContext
(
r
)
defer
invalidateCache
(
c
)
defer
cache
.
Tick
(
c
)
_
,
err
:=
datastore
.
Put
(
c
,
t
.
Key
(
c
),
t
)
return
nil
,
err
}
...
...
@@ -148,14 +150,12 @@ type Todo struct {
// Multiple "kind" parameters may be specified.
func
todoHandler
(
r
*
http
.
Request
)
(
interface
{},
os
.
Error
)
{
c
:=
appengine
.
NewContext
(
r
)
todoKey
:=
r
.
Form
.
Encode
()
if
t
,
ok
:=
cachedTodo
(
c
,
todoKey
);
ok
{
c
.
Debugf
(
"cache hit"
)
return
t
,
nil
now
:=
cache
.
Now
(
c
)
key
:=
"build-todo-"
+
r
.
Form
.
Encode
()
cachedTodo
:=
new
(
Todo
)
if
cache
.
Get
(
r
,
now
,
key
,
cachedTodo
)
{
return
cachedTodo
,
nil
}
c
.
Debugf
(
"cache miss"
)
var
todo
*
Todo
var
err
os
.
Error
builder
:=
r
.
FormValue
(
"builder"
)
...
...
@@ -175,7 +175,7 @@ func todoHandler(r *http.Request) (interface{}, os.Error) {
}
}
if
err
==
nil
{
cache
Todo
(
c
,
todoK
ey
,
todo
)
cache
.
Set
(
r
,
now
,
k
ey
,
todo
)
}
return
todo
,
err
}
...
...
@@ -218,7 +218,19 @@ func buildTodo(c appengine.Context, builder, packagePath, goHash string) (interf
// packagesHandler returns a list of the non-Go Packages monitored
// by the dashboard.
func
packagesHandler
(
r
*
http
.
Request
)
(
interface
{},
os
.
Error
)
{
return
Packages
(
appengine
.
NewContext
(
r
))
c
:=
appengine
.
NewContext
(
r
)
now
:=
cache
.
Now
(
c
)
const
key
=
"build-packages"
var
p
[]
*
Package
if
cache
.
Get
(
r
,
now
,
key
,
&
p
)
{
return
p
,
nil
}
p
,
err
:=
Packages
(
c
)
if
err
!=
nil
{
return
nil
,
err
}
cache
.
Set
(
r
,
now
,
key
,
p
)
return
p
,
nil
}
// resultHandler records a build result.
...
...
@@ -240,7 +252,7 @@ func resultHandler(r *http.Request) (interface{}, os.Error) {
if
err
:=
res
.
Valid
();
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"validating Result: %v"
,
err
)
}
defer
invalidateCache
(
c
)
defer
cache
.
Tick
(
c
)
// store the Log text if supplied
if
len
(
res
.
Log
)
>
0
{
hash
,
err
:=
PutLog
(
c
,
res
.
Log
)
...
...
@@ -347,6 +359,7 @@ func AuthHandler(h dashHandler) http.HandlerFunc {
func
initHandler
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
// TODO(adg): devise a better way of bootstrapping new packages
c
:=
appengine
.
NewContext
(
r
)
defer
cache
.
Tick
(
c
)
for
_
,
p
:=
range
defaultPackages
{
if
err
:=
datastore
.
Get
(
c
,
p
.
Key
(
c
),
new
(
Package
));
err
==
nil
{
continue
...
...
misc/dashboard/app/build/ui.go
View file @
5a65cbac
...
...
@@ -8,9 +8,6 @@
package
build
import
(
"appengine"
"appengine/datastore"
"appengine/memcache"
"bytes"
"exp/template/html"
"http"
...
...
@@ -20,6 +17,10 @@ import (
"strconv"
"strings"
"template"
"appengine"
"appengine/datastore"
"cache"
)
func
init
()
{
...
...
@@ -30,6 +31,8 @@ func init() {
// uiHandler draws the build status page.
func
uiHandler
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
c
:=
appengine
.
NewContext
(
r
)
now
:=
cache
.
Now
(
c
)
const
key
=
"build-ui"
page
,
_
:=
strconv
.
Atoi
(
r
.
FormValue
(
"page"
))
if
page
<
0
{
...
...
@@ -37,15 +40,12 @@ func uiHandler(w http.ResponseWriter, r *http.Request) {
}
// Used cached version of front page, if available.
if
page
==
0
&&
r
.
Host
==
"build.golang.org"
{
t
,
err
:=
memcache
.
Get
(
c
,
uiCacheKey
)
if
err
==
nil
{
w
.
Write
(
t
.
Value
)
if
page
==
0
{
var
b
[]
byte
if
cache
.
Get
(
r
,
now
,
key
,
&
b
)
{
w
.
Write
(
b
)
return
}
if
err
!=
memcache
.
ErrCacheMiss
{
c
.
Errorf
(
"get ui cache: %v"
,
err
)
}
}
commits
,
err
:=
goCommits
(
c
,
page
)
...
...
@@ -78,15 +78,8 @@ func uiHandler(w http.ResponseWriter, r *http.Request) {
}
// Cache the front page.
if
page
==
0
&&
r
.
Host
==
"build.golang.org"
{
t
:=
&
memcache
.
Item
{
Key
:
uiCacheKey
,
Value
:
buf
.
Bytes
(),
Expiration
:
uiCacheExpiry
,
}
if
err
:=
memcache
.
Set
(
c
,
t
);
err
!=
nil
{
c
.
Errorf
(
"set ui cache: %v"
,
err
)
}
if
page
==
0
{
cache
.
Set
(
r
,
now
,
key
,
buf
.
Bytes
())
}
buf
.
WriteTo
(
w
)
...
...
misc/dashboard/app/cache/cache.go
0 → 100644
View file @
5a65cbac
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package
cache
import
(
"fmt"
"http"
"time"
"appengine"
"appengine/memcache"
)
const
(
nocache
=
"nocache"
timeKey
=
"cachetime"
expiry
=
600
// 10 minutes
)
func
newTime
()
uint64
{
return
uint64
(
time
.
Seconds
())
<<
32
}
// Now returns the current logical datastore time to use for cache lookups.
func
Now
(
c
appengine
.
Context
)
uint64
{
t
,
err
:=
memcache
.
Increment
(
c
,
timeKey
,
0
,
newTime
())
if
err
!=
nil
{
c
.
Errorf
(
"cache.Now: %v"
,
err
)
return
0
}
return
t
}
// Tick sets the current logical datastore time to a never-before-used time
// and returns that time. It should be called to invalidate the cache.
func
Tick
(
c
appengine
.
Context
)
uint64
{
t
,
err
:=
memcache
.
Increment
(
c
,
timeKey
,
1
,
newTime
())
if
err
!=
nil
{
c
.
Errorf
(
"cache.Tick: %v"
,
err
)
return
0
}
return
t
}
// Get fetches data for name at time now from memcache and unmarshals it into
// value. It reports whether it found the cache record and logs any errors to
// the admin console.
func
Get
(
r
*
http
.
Request
,
now
uint64
,
name
string
,
value
interface
{})
bool
{
if
now
==
0
||
r
.
FormValue
(
nocache
)
!=
""
{
return
false
}
c
:=
appengine
.
NewContext
(
r
)
key
:=
fmt
.
Sprintf
(
"%s.%d"
,
name
,
now
)
_
,
err
:=
memcache
.
JSON
.
Get
(
c
,
key
,
value
)
if
err
==
nil
{
c
.
Debugf
(
"cache hit %q"
,
key
)
return
true
}
c
.
Debugf
(
"cache miss %q"
,
key
)
if
err
!=
memcache
.
ErrCacheMiss
{
c
.
Errorf
(
"get cache %q: %v"
,
key
,
err
)
}
return
false
}
// Set puts value into memcache under name at time now.
// It logs any errors to the admin console.
func
Set
(
r
*
http
.
Request
,
now
uint64
,
name
string
,
value
interface
{})
{
if
now
==
0
||
r
.
FormValue
(
nocache
)
!=
""
{
return
}
c
:=
appengine
.
NewContext
(
r
)
key
:=
fmt
.
Sprintf
(
"%s.%d"
,
name
,
now
)
err
:=
memcache
.
JSON
.
Set
(
c
,
&
memcache
.
Item
{
Key
:
key
,
Object
:
value
,
Expiration
:
expiry
,
})
if
err
!=
nil
{
c
.
Errorf
(
"set cache %q: %v"
,
key
,
err
)
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment