Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSG not storing to s3 for dynamic pages with fallback: true #798

Closed
arelaxend opened this issue Nov 14, 2020 · 7 comments · Fixed by #800
Closed

SSG not storing to s3 for dynamic pages with fallback: true #798

arelaxend opened this issue Nov 14, 2020 · 7 comments · Fixed by #800

Comments

@arelaxend
Copy link

arelaxend commented Nov 14, 2020

Hi people,

Describe the bug

Say, we have dynamic pages /test/[id].js with some getStaticPaths(...fallback: true) and getStaticProps(...). When we request a new id, it renders the html correctly after a loading but it does not store anything on s3.

So, SSG is not working here!

We suspect that it does not deal multi-regions correctly, but we actually don't know. Please read what follows.

Actual behavior

Nothing is stored in s3 when fallback:true for pages /test/xxx not statistically generated at build time. But it does work in localhost.

Expected behavior

After a hit, subsequent requests just follow the RFC that is get from s3, hit, render.

Additional information

We tested this with the following in /test/[id].tsx

import { GetStaticPaths, GetStaticProps } from "next";
import Head from "next/head";
import { useRouter } from "next/router";
import { useRef } from "react";

export const getStaticPaths: GetStaticPaths = async () => {
  return {
    fallback: true,
    paths: [{ params: { id: "e2efdd70-177f-4bf8-907d-a953c702a304" } }],
  };
};

export const getStaticProps: GetStaticProps = async ({}: any) => {
  return {
    props: {
      id: "e2efdd70-177f-4bf8-907d-a953c702a304",
      upload: {
        title: "UNICEF Children, food and nutrition.",
      },
    },
  };
};

export default function Publication({ upload }: any) {
  const router = useRouter();

  if (router.isFallback) {
    return (
      <div>
        <h1>Loading&hellip;</h1>
      </div>
    );
  }

  const title = useRef((upload && upload.title) || "docduo");

  return (
    <Head>
      <title>{title.current}</title>
    </Head>
  );
}

The content of /static-pages/test/[id].html is the following:


Capture d’écran 2020-11-14 à 22 03 50

The content of _next/static/QTXnzCYbWqOui3ql6FK75/_buildManifest.js:


The content of _next/static/QTXnzCYbWqOui3ql6FK75/_ssgManifest.js is:


When I test the results of this with the following url: https://e.docduo.fr/test/59407fcb-a848-4ab5-95f0-019f613ea0c1
I expect to have something store in s3 after the first hit.. but in the s3 folder there is just:
Sans titre-1

The lambda function (we only see one lambda, Default @ Lambda/Edge, no API) has the following permissions (we also test with administrator permissions, does not work).
Capture d’écran 2020-11-14 à 22 41 44

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Resource": "*",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ]
        },
        {
            "Effect": "Allow",
            "Resource": "arn:aws:s3:::1k4854-p0yvzbh/*",
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ]
        }
    ]
}

Strangely, there is no lambda invocation (on us-east-1, where the plugin creates the lambda)

Capture d’écran 2020-11-14 à 22 24 21

The serverless includes a domain where we issue a certificate in us-east-1

# serverless.yml
nextamplified:
  component: "@sls-next/[email protected]"
  inputs: 
    domain: ["e", "docduo.fr"]

Also maybe this is relevant, the cloudwatch logs are in ue-west-1:
Capture d’écran 2020-11-14 à 22 29 20

But the plugin setups one lambda in us-east-1.

In cloudfront, under _next/data* we have this setup (automatically by the plugin, it looks nice)
Capture d’écran 2020-11-14 à 22 51 21

Under s3, there is the following perms:

{
    "Version": "2012-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Sid": " Grant a CloudFront Origin Identity access to support private content",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity E376221HJ5Y29H"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::1k4854-p0yvzbh/*"
        }
    ]
}

Versions

  • OS/Environment: mac os, chrome
  • @sls-next/serverless-component version: 1.19.0-alpha.7
  • Next.js version: 10

Checklist

A.

@dphang
Copy link
Collaborator

dphang commented Nov 15, 2020

Thanks, looks like the problem is in here:

} else if (
pagePath === "pages/_error.js" ||
!prerenderManifest.routes[normalisedDataRequestUri]
) {
// Break to continue to SSR render in two cases:
// 1. URI routes to _error.js
// 2. URI is not unmatched, but it's not in prerendered routes, i.e this is an SSR data request, we need to SSR render the JSON
break S3Check;
}
. Instead of breaking to do SSR render of the data request in origin request handler for fallback data requests, we should try to return an s3 request to tell CloudFront fetch it from S3 (which will fail the first time, but the origin response handler will then fetch and store it in s3).

Not sure why it wasn't caught in unit tests, I'll fix it and update both the unit/e2e tests to catch this.

@arelaxend
Copy link
Author

arelaxend commented Nov 15, 2020

@dphang Got it! Well now it stores on s3🥇 . You are doing very great 👍

Just a comment, It may occur that if you just return (<Head></Head>) or null for example, the fallback loading appears at each re-render. Also it downloads the json file every times.

I may open a thread for this, but who cares.

@dphang
Copy link
Collaborator

dphang commented Nov 15, 2020

Thanks for testing. I actually didn't publish the latest yet.

I did find that issue too, it is because fallback page gets cached after 1st hit. But if you bust the cache after hitting a non-prerendered route (add a random query parameter), with the new change it would pick up the page that was just stored in s3, so all props are populated.

To fix it, we need to make the fallback page never cached, so the lambda can return the newly generated page (and cache it) the next time the same route is hit. But we also need to version the pages properly as I realized we are not clearing them properly (we only have one set of pages under static-pages, so subsequent deploys may pick up an old version).

@arelaxend
Copy link
Author

arelaxend commented Nov 15, 2020

Thanks for testing. I actually didn't publish the latest yet.

@dphang we tested with the PR.

To fix it, we need to make the fallback page never cached, so the lambda can return the newly generated page (and cache it) the next time the same route is hit. But we also need to version the pages properly as I realized we are not clearing them properly (we only have one set of pages under static-pages, so subsequent deploys may pick up an old version).

Absolutely, it makes sense since in the end what users request is cached, still up-to-date versions at the edges. Keep up 💯

@rahul3103
Copy link

@arelaxend Can you tell me how you are uploading your static generated files to s3.

Right now, what I am doing is after build I upload all the static files and folder on S3 and do yarn start after updating the asset prefix. But, I am stuck with this incremental static page generation and also with next-images which generate so many images. But, I am not able to upload these on S3. Can you guide me how you are uploading your build time and incremental static files on S3

@arelaxend
Copy link
Author

@rahul3103 can you create a request / issue following the instructions and check that your question has no precedence. Thank you

@danigrandem
Copy link

Im having the same problem.
How can i solve this?

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants