Skip to content

Load the Firebase SDK lazily — cable apps currently pay ~700 KiB of it on every page, including ones with no stream #18

Description

@mattdrees

Following up on the note at the end of #17 ("worth a follow-up if that lazy behavior is actually wanted"). It is, and there's measured evidence from cru-bot that it's worth more than it might look.

The behaviour

controllers/index.js imports the controller at boot, the controller imports firebase/{app,auth,firestore} at module top level, so the SDK loads on every page of a cable app whether or not anything called firestore_stream_from. As #17 says, preload: false only keeps them out of the modulepreload set.

Measured

From a HAR captured on cru-bot's stage environment (the one that found #9). GET /conversations/new — a page with no stream element; firestore_stream_from appears only in conversations/show — fetched the controller and then all three modules:

module uncompressed
firebase-app.js 103,065
firebase-auth.js 158,756
firebase-firestore.js 454,720
total 716,541 (~700 KiB)

Those are content.size from the HAR, so uncompressed — gstatic serves them compressed, so wire cost is a fraction of that. But parse/compile cost is paid on the uncompressed bytes, and firebase-firestore.js alone is ~455 KiB of that.

Why it matters more than a typical dashboard app

cru-bot is public-facing and evangelistic: / is ConversationsController#new, the first thing a seeker sees, often on a phone. It has no stream. It currently pays for the entire Firestore SDK to render a text box. The pages that actually need cable are conversation show pages, which are reached after that first paint.

So the current shape puts the cost precisely where it has the least value and the most impact.

Shape of a fix

A dynamic import() inside connect() — i.e. the controller module stays cheap and pulls the SDK only when a stream element actually appears. #17 correctly flags that this is not free:

  • connect() becomes async, so the scheduleTokenFetch / queueMicrotask ordering needs a look.
  • connectEmulators must still run exactly once, before any sign-in or snapshot, and it currently sits in establishSession — which would now be downstream of an await.
  • The registry.app / registry.db memoisation would need to tolerate concurrent first-connects (two stream elements on one page racing the same dynamic import).

None of that looks hard, but it's the connect path, which is the part with the subtle ordering. Happy to be the guinea pig on cru-bot when it lands — it's the app with the most stream-free pages, so it's where the win shows up.

Not urgent

Nothing is broken; this is cost, not correctness. Filing it so the note in #17 doesn't get lost now that #12 is closed. If it helps, the data-solid-gcp-cable-listening / solid-gcp-cable:failed machinery from 0.5.0 gives a clean way to assert in the emulator system test that a lazily-loaded client still goes live — so the change is testable with what's already there.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions