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

Write patterns deployment #2129

Merged
merged 22 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
adcbfd7
Changes to make it deployable. Still some issues to solve.
balegas Dec 9, 2024
561fcc6
Applied fix to pattern 4; something else is going on; extracted confi…
balegas Dec 10, 2024
84b6263
Allow deployment to different region
balegas Dec 10, 2024
f4c79dc
example: extract config out of the main code.
thruflo Dec 10, 2024
edd0235
Merge remote-tracking branch 'origin/main' into balegas/write-pattern…
KyleAMathews Dec 10, 2024
29898e5
fixpack
KyleAMathews Dec 10, 2024
f3b584e
Format
KyleAMathews Dec 10, 2024
4d8a50f
Fix type errors
KyleAMathews Dec 10, 2024
d0636a3
Fix format again
KyleAMathews Dec 10, 2024
61cbdee
add sst type
KyleAMathews Dec 10, 2024
6fd9b90
Merge remote-tracking branch 'origin/main' into balegas/write-pattern…
KyleAMathews Dec 10, 2024
931e6a6
fixees
KyleAMathews Dec 10, 2024
d5960f9
examples: disable vite-pwa.
thruflo Dec 10, 2024
6dad016
example: actually fix the bug.
thruflo Dec 10, 2024
db0eb3a
example: reenable vite-pwa, not the culprit!
thruflo Dec 10, 2024
eb1bac4
upgrade sst
KyleAMathews Dec 10, 2024
7cb2efb
Merge remote-tracking branch 'origin/main' into balegas/write-pattern…
KyleAMathews Dec 10, 2024
bbc6887
Merge remote-tracking branch 'origin/main' into balegas/write-pattern…
KyleAMathews Dec 10, 2024
f7fb7a8
Merge remote-tracking branch 'origin/main' into balegas/write-pattern…
KyleAMathews Dec 10, 2024
6770ab3
Merge remote-tracking branch 'origin/main' into balegas/write-pattern…
KyleAMathews Dec 10, 2024
0308b63
Merge remote-tracking branch 'origin/main' into balegas/write-pattern…
KyleAMathews Dec 10, 2024
99ba1e6
Merge remote-tracking branch 'origin/main' into balegas/write-pattern…
KyleAMathews Dec 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions examples/write-patterns/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
FROM node:lts-alpine AS base

# Stage 1: Install dependencies
FROM base AS deps
WORKDIR /app

RUN npm install -g pnpm

COPY pnpm-*.yaml ./
COPY package.json ./
COPY tsconfig.build.json ./
COPY tsconfig.base.json ./
COPY packages/typescript-client packages/typescript-client/
COPY packages/react-hooks packages/react-hooks/
COPY packages/experimental packages/experimental/
COPY examples/write-patterns/ examples/write-patterns/

# Install dependencies
RUN pnpm install --frozen-lockfile
RUN pnpm run -r build


# Need to make production image more clean
FROM node:lts-alpine AS prod
WORKDIR /app

ENV NODE_ENV=production
COPY --from=deps /app/ ./

WORKDIR /app/examples/write-patterns

EXPOSE 3001
ENTRYPOINT ["node", "shared/backend/api.js"]
33 changes: 17 additions & 16 deletions examples/write-patterns/package.json
Original file line number Diff line number Diff line change
@@ -1,37 +1,24 @@
{
"name": "@electric-examples/write-patterns",
"private": true,
"version": "0.0.1",
"author": "ElectricSQL",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/electric-sql/electric/issues"
},
"type": "module",
"scripts": {
"backend:up": "PROJECT_NAME=write-patterns pnpm -C ../../ run example-backend:up && pnpm db:migrate",
"backend:down": "PROJECT_NAME=write-patterns pnpm -C ../../ run example-backend:down",
"db:migrate": "dotenv -e ../../.env.dev -- pnpm exec pg-migrations apply --directory ./shared/migrations",
"dev": "concurrently \"vite\" \"node shared/backend/api.js\"",
"build": "vite build",
"format": "eslint . --ext ts,tsx --fix",
"stylecheck": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@electric-sql/client": "workspace:*",
"@electric-sql/experimental": "workspace:*",
"@electric-sql/pglite": "^0.2.14",
"@electric-sql/pglite-react": "^0.2.14",
"@electric-sql/pglite-sync": "^0.2.16",
"@electric-sql/pglite-sync": "^0.2.17",
"@electric-sql/react": "workspace:*",
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.19.2",
"pg": "^8.12.0",
"react": "19.0.0-rc.1",
"react-dom": "19.0.0-rc.1",
"sst": "3.3.64",
"uuid": "^10.0.0",
"valtio": "^2.1.2",
"zod": "^3.23.8"
Expand All @@ -50,10 +37,24 @@
"vite": "^5.3.4",
"vite-plugin-pwa": "^0.21.0"
},
"license": "Apache-2.0",
"overrides": {
"@types/react": "npm:types-react@rc",
"@types/react-dom": "npm:types-react-dom@rc",
"react": "19.0.0-rc.1",
"react-dom": "19.0.0-rc.1"
}
},
"private": true,
"scripts": {
"backend:down": "PROJECT_NAME=write-patterns pnpm -C ../../ run example-backend:down",
"backend:up": "PROJECT_NAME=write-patterns pnpm -C ../../ run example-backend:up && pnpm db:migrate",
"build": "vite build",
"db:migrate": "dotenv -e ../../.env.dev -- pnpm exec pg-migrations apply --directory ./shared/migrations",
"dev": "concurrently \"vite\" \"node shared/backend/api.js\"",
"format": "eslint . --fix",
"preview": "vite preview",
"stylecheck": "eslint . --quiet",
"typecheck": "tsc --noEmit"
},
"type": "module"
}
5 changes: 3 additions & 2 deletions examples/write-patterns/patterns/1-online-writes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from 'react'
import { v4 as uuidv4 } from 'uuid'
import { useShape } from '@electric-sql/react'
import api from '../../shared/app/client'

const ELECTRIC_URL = import.meta.env.ELECTRIC_URL || 'http://localhost:3000'
import api from '../../shared/app/client'
import { ELECTRIC_URL, envParams } from '../../shared/app/config'

type Todo = {
id: string
Expand All @@ -19,6 +19,7 @@ export default function OnlineWrites() {
url: `${ELECTRIC_URL}/v1/shape`,
params: {
table: 'todos',
...envParams,
},
parser: {
timestamptz: (value: string) => new Date(value),
Expand Down
5 changes: 3 additions & 2 deletions examples/write-patterns/patterns/2-optimistic-state/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import React, { useOptimistic, useTransition } from 'react'
import { v4 as uuidv4 } from 'uuid'
import { matchBy, matchStream } from '@electric-sql/experimental'
import { useShape } from '@electric-sql/react'
import api from '../../shared/app/client'

const ELECTRIC_URL = import.meta.env.ELECTRIC_URL || 'http://localhost:3000'
import api from '../../shared/app/client'
import { ELECTRIC_URL, envParams } from '../../shared/app/config'

type Todo = {
id: string
Expand Down Expand Up @@ -34,6 +34,7 @@ export default function OptimisticState() {
url: `${ELECTRIC_URL}/v1/shape`,
params: {
table: 'todos',
...envParams,
},
parser: {
timestamptz: (value: string) => new Date(value),
Expand Down
46 changes: 26 additions & 20 deletions examples/write-patterns/patterns/3-shared-persistent/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { matchBy, matchStream } from '@electric-sql/experimental'
import { useShape } from '@electric-sql/react'

import api from '../../shared/app/client'
import { ELECTRIC_URL, envParams } from '../../shared/app/config'

const ELECTRIC_URL = import.meta.env.ELECTRIC_URL || 'http://localhost:3000'
const KEY = 'electric-sql/examples/write-patterns/shared-persistent'

type Todo = {
Expand Down Expand Up @@ -112,6 +112,7 @@ export default function SharedPersistent() {
url: `${ELECTRIC_URL}/v1/shape`,
params: {
table: 'todos',
...envParams,
},
parser: {
timestamptz: (value: string) => new Date(value),
Expand All @@ -123,25 +124,30 @@ export default function SharedPersistent() {
// Get the local optimistic state.
const localWrites = useSnapshot<Map<string, LocalWrite>>(optimisticState)

// Merge the synced state with the local state.
const todos = localWrites
.values()
.reduce((synced: Todo[], { operation, value }: LocalWrite) => {
switch (operation) {
case 'insert':
return synced.some((todo) => todo.id === value.id)
? synced
: [...synced, value as Todo]

case 'update':
return synced.map((todo) =>
todo.id === value.id ? { ...todo, ...value } : todo
)

case 'delete':
return synced.filter((todo) => todo.id !== value.id)
}
}, sorted)
const computeOptimisticState = (
synced: Todo[],
writes: LocalWrite[]
): Todo[] => {
return writes.reduce(
(synced: Todo[], { operation, value }: LocalWrite): Todo[] => {
switch (operation) {
case 'insert':
return [...synced, value as Todo]
case 'update':
return synced.map((todo) =>
todo.id === value.id ? { ...todo, ...value } : todo
)
case 'delete':
return synced.filter((todo) => todo.id !== value.id)
default:
return synced
}
},
synced
)
}

const todos = computeOptimisticState(sorted, [...localWrites.values()])

// These are the same event handler functions from the previous optimistic
// state pattern, adapted to add the state to the shared, persistent store.
Expand Down
14 changes: 10 additions & 4 deletions examples/write-patterns/patterns/4-through-the-db/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@ import { electricSync } from '@electric-sql/pglite-sync'

import localSchemaMigrations from './local-schema.sql?raw'

import { ELECTRIC_URL, envParams } from '../../shared/app/config'

const DATA_DIR = 'idb://electric-write-patterns-example'
const ELECTRIC_URL = import.meta.env.ELECTRIC_URL || 'http://localhost:3000'

const registry = new Map<string, Promise<PGliteWithLive>>()

export default async function loadPGlite(): Promise<PGliteWithLive> {
const loadingPromise = registry.get('loadingPromise')
let loadingPromise = registry.get('loadingPromise')

if (loadingPromise === undefined) {
registry.set('loadingPromise', _loadPGlite())
loadingPromise = _loadPGlite()

registry.set('loadingPromise', loadingPromise)
}

return loadingPromise as Promise<PGliteWithLive>
Expand All @@ -32,7 +35,10 @@ async function _loadPGlite(): Promise<PGliteWithLive> {
await pglite.electric.syncShapeToTable({
shape: {
url: `${ELECTRIC_URL}/v1/shape`,
table: 'todos',
params: {
table: 'todos',
...envParams,
},
},
shapeKey: 'todos',
table: 'todos_synced',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,7 @@ END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE TRIGGER delete_local_on_synced_insert
AFTER INSERT ON todos_synced
FOR EACH ROW
EXECUTE FUNCTION delete_local_on_synced_insert_trigger();

CREATE OR REPLACE TRIGGER delete_local_on_synced_insert_and_update
AFTER UPDATE ON todos_synced
AFTER INSERT OR UPDATE ON todos_synced
FOR EACH ROW
EXECUTE FUNCTION delete_local_on_synced_insert_and_update_trigger();

Expand Down
2 changes: 1 addition & 1 deletion examples/write-patterns/shared/app/client.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const API_URL = import.meta.env.API_URL || 'http://localhost:3001'
const API_URL = import.meta.env.VITE_SERVER_URL || 'http://localhost:3001'

type RequestOptions = {
method: string
Expand Down
11 changes: 11 additions & 0 deletions examples/write-patterns/shared/app/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const ELECTRIC_URL =
import.meta.env.VITE_ELECTRIC_URL || 'http://localhost:3000'

export const envParams: { database_id?: string; token?: string } =
import.meta.env.VITE_ELECTRIC_TOKEN &&
import.meta.env.VITE_ELECTRIC_DATABASE_ID
? {
database_id: import.meta.env.VITE_ELECTRIC_DATABASE_ID,
token: import.meta.env.VITE_ELECTRIC_TOKEN,
}
: {}
Loading
Loading