-
Notifications
You must be signed in to change notification settings - Fork 156
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
127 additions
and
100 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { pool } from "../../db" | ||
import { NextRequest, NextResponse } from "next/server" | ||
|
||
import * as Y from "yjs" | ||
import * as syncProtocol from "y-protocols/sync" | ||
|
||
import * as encoding from "lib0/encoding" | ||
import * as decoding from "lib0/decoding" | ||
|
||
import { toBase64, fromBase64 } from "lib0/buffer" | ||
|
||
export async function GET(request: NextRequest) { | ||
try { | ||
const { room } = await getRequestParams(request) | ||
|
||
doCompation(room) | ||
|
||
return NextResponse.json({}) | ||
} catch (e) { | ||
const resp = e instanceof Error ? e.message : e | ||
return NextResponse.json(resp, { status: 400 }) | ||
} | ||
} | ||
|
||
async function doCompation(room: string) { | ||
const db = await pool.connect() | ||
try { | ||
await db.query(`BEGIN`) | ||
const res = await db.query( | ||
`DELETE FROM ydoc_operations | ||
WHERE room = $1 | ||
RETURNING *`, | ||
[room] | ||
) | ||
|
||
const ydoc = new Y.Doc() | ||
res.rows.map(({ op }) => { | ||
const buf = fromBase64(op) | ||
const decoder = decoding.createDecoder(buf) | ||
syncProtocol.readSyncMessage( | ||
decoder, | ||
encoding.createEncoder(), | ||
ydoc, | ||
`server` | ||
) | ||
}) | ||
|
||
const encoder = encoding.createEncoder() | ||
syncProtocol.writeUpdate(encoder, Y.encodeStateAsUpdate(ydoc)) | ||
const encoded = toBase64(encoding.toUint8Array(encoder)) | ||
|
||
await db.query( | ||
`INSERT INTO ydoc_operations (room, op) | ||
VALUES ($1, $2)`, | ||
[room, encoded] | ||
) | ||
await db.query(`COMMIT`) | ||
} catch (e) { | ||
await db.query(`ROLLBACK`) | ||
throw e | ||
} finally { | ||
db.release() | ||
} | ||
} | ||
|
||
async function getRequestParams( | ||
request: NextRequest | ||
): Promise<{ room: string }> { | ||
const room = await request.nextUrl.searchParams.get(`room`) | ||
if (!room) { | ||
throw new Error(`'room' is required`) | ||
} | ||
|
||
return { room } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,93 +1,63 @@ | ||
import { pool } from "../../db" | ||
import { NextResponse } from "next/server" | ||
|
||
// TODO: still loading yjs twice | ||
import * as Y from "yjs" | ||
import * as syncProtocol from "y-protocols/sync" | ||
|
||
import * as encoding from "lib0/encoding" | ||
import * as decoding from "lib0/decoding" | ||
|
||
import { toBase64, fromBase64 } from "lib0/buffer" | ||
import { PoolClient } from "pg" | ||
|
||
const maxRowCount = 50 | ||
|
||
export async function POST(request: Request) { | ||
const db = await pool.connect() | ||
|
||
try { | ||
const body = await request.json() | ||
const { room, op, clientId } = await getRequestParams(request) | ||
|
||
const errorResponse = validateRequest(body) | ||
if (errorResponse) { | ||
return errorResponse | ||
if (!clientId) { | ||
saveOperation(room, op) | ||
} else { | ||
saveAwarenessOperation(room, op, clientId) | ||
} | ||
|
||
await db.query(`BEGIN`) | ||
await db.query( | ||
`INSERT INTO ydoc_operations (room, op) | ||
VALUES ($1, $2)`, | ||
[body.room, body.op] | ||
) | ||
await maybeCompact(db, body.room) | ||
await db.query(`COMMIT`) | ||
|
||
return NextResponse.json({}) | ||
} catch (e) { | ||
await db.query(`ROLLBACK`) | ||
throw e | ||
} finally { | ||
db.release() | ||
const resp = e instanceof Error ? e.message : e | ||
return NextResponse.json(resp, { status: 400 }) | ||
} | ||
} | ||
|
||
// naive implementation of compaction | ||
async function maybeCompact(db: PoolClient, room: string) { | ||
const res = await db.query( | ||
`SELECT id, op FROM ydoc_operations | ||
WHERE room = $1 | ||
ORDER BY id DESC`, | ||
[room] | ||
) | ||
|
||
if (res.rows.length < maxRowCount) { | ||
return | ||
async function saveOperation(room: string, op: string) { | ||
const db = await pool.connect() | ||
try { | ||
await db.query(`INSERT INTO ydoc_operations (room, op) VALUES ($1, $2)`, [ | ||
room, | ||
op, | ||
]) | ||
} finally { | ||
db.release() | ||
} | ||
} | ||
|
||
console.log(`compaction`) | ||
|
||
const ydoc = new Y.Doc() | ||
|
||
res.rows.map(({ op }) => { | ||
const buf = fromBase64(op) | ||
const decoder = decoding.createDecoder(buf) | ||
syncProtocol.readSyncMessage( | ||
decoder, | ||
encoding.createEncoder(), | ||
ydoc, | ||
`server` | ||
async function saveAwarenessOperation( | ||
room: string, | ||
op: string, | ||
clientId: string | ||
) { | ||
const db = await pool.connect() | ||
try { | ||
await db.query( | ||
`INSERT INTO ydoc_awareness (room, clientId, op) VALUES ($1, $2, $3) | ||
ON CONFLICT (clientId, room) | ||
DO UPDATE SET op = $3`, | ||
[room, clientId, op] | ||
) | ||
}) | ||
|
||
const encoder = encoding.createEncoder() | ||
syncProtocol.writeUpdate(encoder, Y.encodeStateAsUpdate(ydoc)) | ||
const encoded = toBase64(encoding.toUint8Array(encoder)) | ||
|
||
await db.query(`TRUNCATE ydoc_operations`) | ||
await db.query( | ||
`INSERT INTO ydoc_operations (room, op) | ||
VALUES ($1, $2)`, | ||
[room, encoded] | ||
) | ||
} finally { | ||
db.release() | ||
} | ||
} | ||
|
||
function validateRequest({ room, op }: { room: string; op: string }) { | ||
async function getRequestParams( | ||
request: Request | ||
): Promise<{ room: string; op: string; clientId?: string }> { | ||
const { room, op, clientId } = await request.json() | ||
if (!room) { | ||
return NextResponse.json({ error: `'room' is required` }, { status: 400 }) | ||
throw new Error(`'room' is required`) | ||
} | ||
|
||
if (!op) { | ||
return NextResponse.json({ error: `'op' is required` }, { status: 400 }) | ||
throw new Error(`'op' is required`) | ||
} | ||
|
||
return { room, op, clientId } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,5 @@ | ||
import { Pool } from "pg" | ||
|
||
console.log(`init pool`) | ||
const pool = new Pool({ | ||
host: `localhost`, | ||
port: 54321, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters