Improve initial page load by embedding user account data into host page

Like the GerritConfig we now embed the user's account information into
the host page if they have a valid account cookie in the request.  This
avoids 2 RPCs during module initialization for a signed-in user, as we
don't need to connect to the AccountService.

The downside is slightly more CPU load on the server when sending out
the host page, as we need to clone then modify the DOM tree, and then
flatten that down for transmission.  The difference is rather minor,
about 3 ms additional time for the host page request.  This is better
than the 150+ ms required for the 2 RPCs to AccountService, so overall
we should see reduced server load.

Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2009-04-15 09:59:25 -07:00
parent 7b356f99dc
commit 47e29c74c4
3 changed files with 39 additions and 19 deletions

View File

@@ -18,6 +18,7 @@ import com.google.gerrit.client.reviewdb.Account;
import com.google.gerrit.client.reviewdb.AccountProjectWatch;
import com.google.gerrit.client.rpc.SignInRequired;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtjsonrpc.client.HostPageCache;
import com.google.gwtjsonrpc.client.RemoteJsonService;
import com.google.gwtjsonrpc.client.VoidResult;
@@ -26,6 +27,7 @@ import java.util.Set;
public interface AccountService extends RemoteJsonService {
@SignInRequired
@HostPageCache(name = "gerrit_myaccount_obj", once = true)
void myAccount(AsyncCallback<Account> callback);
@SignInRequired

View File

@@ -3,6 +3,7 @@
<title>Gerrit Code Review</title>
<meta name="gwt:property" content="locale=en_US" />
<script id="gerrit_gerritconfig"></script>
<script id="gerrit_myaccount"></script>
<script id="gerrit_module" type="text/javascript" language="javascript" src="com.google.gerrit.Gerrit.nocache.js"></script>
<style id="gerrit_sitecss" type="text/css"></style>
<link rel="icon" type="image/gif" href="favicon.ico" />

View File

@@ -14,6 +14,7 @@
package com.google.gerrit.server;
import com.google.gerrit.client.reviewdb.Account;
import com.google.gerrit.client.rpc.Common;
import com.google.gwt.user.server.rpc.RPCServletUtils;
import com.google.gwtjsonrpc.server.XsrfException;
@@ -39,29 +40,29 @@ import javax.servlet.http.HttpServletResponse;
/** Sends the Gerrit host page to clients. */
public class HostPageServlet extends HttpServlet {
private GerritServer server;
private String canonicalUrl;
private byte[] hostPageRaw;
private byte[] hostPageCompressed;
private long lastModified;
private Document hostDoc;
@Override
public void init(final ServletConfig config) throws ServletException {
super.init(config);
final GerritServer srv;
try {
srv = GerritServer.getInstance();
server = GerritServer.getInstance();
} catch (OrmException e) {
throw new ServletException("Cannot load GerritServer", e);
} catch (XsrfException e) {
throw new ServletException("Cannot load GerritServer", e);
}
final File sitePath = srv.getSitePath();
canonicalUrl = srv.getCanonicalURL();
final File sitePath = server.getSitePath();
canonicalUrl = server.getCanonicalURL();
final String hostPageName = "com/google/gerrit/public/Gerrit.html";
final Document hostDoc = HtmlDomUtil.parseFile(hostPageName);
hostDoc = HtmlDomUtil.parseFile(hostPageName);
if (hostDoc == null) {
throw new ServletException("No " + hostPageName + " in CLASSPATH");
}
@@ -70,13 +71,15 @@ public class HostPageServlet extends HttpServlet {
injectCssFile(hostDoc, "gerrit_sitecss", sitePath, "GerritSite.css");
injectXmlFile(hostDoc, "gerrit_header", sitePath, "GerritSiteHeader.html");
injectXmlFile(hostDoc, "gerrit_footer", sitePath, "GerritSiteFooter.html");
try {
hostPageRaw = HtmlDomUtil.toUTF8(hostDoc);
final Document anon = HtmlDomUtil.clone(hostDoc);
injectJson(anon, "gerrit_myaccount", null);
hostPageRaw = HtmlDomUtil.toUTF8(anon);
hostPageCompressed = HtmlDomUtil.compress(hostPageRaw);
} catch (IOException e) {
throw new ServletException(e.getMessage(), e);
}
lastModified = System.currentTimeMillis();
}
private void injectXmlFile(final Document hostDoc, final String id,
@@ -183,11 +186,6 @@ public class HostPageServlet extends HttpServlet {
scriptNode.setAttribute("src", src + "?content=" + vstr);
}
@Override
protected long getLastModified(final HttpServletRequest req) {
return lastModified;
}
@Override
protected void doGet(final HttpServletRequest req,
final HttpServletResponse rsp) throws IOException {
@@ -219,18 +217,37 @@ public class HostPageServlet extends HttpServlet {
return;
}
final Account.Id me = new GerritCall(server, req, rsp).getAccountId();
final Account account = Common.getAccountCache().get(me);
final byte[] tosend;
if (RPCServletUtils.acceptsGzipEncoding(req)) {
rsp.setHeader("Content-Encoding", "gzip");
tosend = hostPageCompressed;
if (account != null) {
// We know who the user is; embed their account data into the host
// page to avoid an RPC during module loading.
//
final Document peruser = HtmlDomUtil.clone(hostDoc);
injectJson(peruser, "gerrit_myaccount", account);
final byte[] raw = HtmlDomUtil.toUTF8(peruser);
if (RPCServletUtils.acceptsGzipEncoding(req)) {
rsp.setHeader("Content-Encoding", "gzip");
tosend = HtmlDomUtil.compress(raw);
} else {
tosend = raw;
}
} else {
tosend = hostPageRaw;
// User is anonymous (hasn't authenticated with us).
//
if (RPCServletUtils.acceptsGzipEncoding(req)) {
rsp.setHeader("Content-Encoding", "gzip");
tosend = hostPageCompressed;
} else {
tosend = hostPageRaw;
}
}
rsp.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
rsp.setHeader("Pragma", "no-cache");
rsp.setHeader("Cache-Control", "no-cache, must-revalidate");
rsp.setDateHeader("Expires", 0L);
rsp.setDateHeader("Last-Modified", lastModified);
rsp.setContentType("text/html");
rsp.setCharacterEncoding(HtmlDomUtil.ENC);
rsp.setContentLength(tosend.length);