Last active
April 6, 2022 23:18
-
-
Save loucou/878ec2d8c0c0b1ec33ed9a4926e6970f to your computer and use it in GitHub Desktop.
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
function countListener(onCountChange: (count: number) => void) { | |
const query = firebase.firestore().collection("fruits"); | |
let count = 0; | |
return query.onSnapshot(snapshot => { | |
snapshot.docChanges().forEach(change => { | |
if (change.type === "added") onCountChange(++count); | |
if (change.type === "removed") onCountChange(--count); | |
}); | |
}); | |
} |
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
const db = admin.firestore(); | |
const fruitTrigger = functions.firestore.document("fruits/{id}"); | |
export const onCreate = fruitTrigger.onCreate((doc, context) => | |
updateCount(context.eventId, +1) | |
); | |
export const onDelete = fruitTrigger.onDelete((doc, context) => | |
updateCount(context.eventId, -1) | |
); | |
async function updateCount(eventId: string, delta: number) { | |
try { | |
// create will throw ALREADY_EXISTS if the event has already been processed | |
await db | |
.doc(`deduped_fruits_counter/counter/events/${eventId}`) | |
.create({ createdAt: firestore.FieldValue.serverTimestamp() }); | |
await db | |
.doc("deduped_fruits_counter/counter") | |
.set({ count: firestore.FieldValue.increment(delta) }, { merge: true }); | |
if (Math.random() < 1.0 / 300) { | |
await cleanup(); | |
} | |
} catch (error) { | |
if (error.code === GrpcStatus.ALREADY_EXISTS) { | |
functions.logger.debug("Duplicated event trigger!"); | |
} else { | |
throw error; | |
} | |
} | |
} | |
async function cleanup() { | |
const limitDate = new Date(Date.now() - 1000 * 60 * 10); | |
const batch = db.batch(); | |
const pastEvents = await db | |
.collection("deduped_fruits_counter/counter/events") | |
.orderBy("createdAt", "asc") | |
.where("createdAt", "<", limitDate) | |
.limit(400) | |
.get(); | |
pastEvents.forEach(event => batch.delete(event.ref)); | |
await batch.commit(); | |
} |
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
const fruitTrigger = functions.firestore.document("fruits/{id}"); | |
export const onCreate = fruitTrigger.onCreate(() => updateCount(+1)); | |
export const onDelete = fruitTrigger.onDelete(() => updateCount(-1)); | |
async function updateCount(delta: number) { | |
await admin | |
.firestore() | |
.doc("simple_fruits_counter/counter") | |
.set({ count: firestore.FieldValue.increment(delta) }, { merge: true }); | |
} |
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
async function paginatedCount() { | |
const query = admin | |
.firestore() | |
.collection("fruits") | |
.orderBy(firestore.FieldPath.documentId(), "asc"); | |
let lastDocId = ""; | |
let count = 0; | |
while (true) { | |
const offsetQuery = lastDocId ? query.startAfter(lastDocId) : query; | |
const snapshot = await offsetQuery.limit(2).get(); | |
const size = snapshot.size; | |
if (size === 0) break; | |
count += size; | |
lastDocId = snapshot.docs[size - 1].id; | |
} | |
return count; | |
} |
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
export const onSchedule = functions.pubsub.schedule("every 15 minutes").onRun(async () => { | |
const count = await streamedCount(); | |
await admin | |
.firestore() | |
.doc("refreshed_fruits_counter/counter") | |
.set({ count, updatedAt: firestore.FieldValue.serverTimestamp() }); | |
}); |
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
const db = admin.firestore(); | |
const fruitTrigger = functions.firestore.document("fruits/{id}"); | |
export const onCreate = fruitTrigger.onCreate((snapshot, context) => | |
updateCount(snapshot.id, +1) | |
); | |
export const onDelete = fruitTrigger.onDelete((snapshot, context) => | |
updateCount(snapshot.id, -1) | |
); | |
let pool: any = null; | |
async function updateCount(fruitId: string, action: number) { | |
pool = pool || (await createPool()); | |
if (action === 1) { | |
await pool.query("INSERT IGNORE INTO fruits VALUES (?)", [fruitId]); | |
} else { | |
await pool.query("DELETE FROM fruits WHERE id = ?", [fruitId]); | |
} | |
const res = await pool.query("SELECT COUNT(*) AS count FROM fruits"); | |
await db.doc("sql_fruits_counter/counter").set({ count: res[0].count }); | |
} | |
async function createPool() { | |
// ... | |
// see https://cloud.google.com/sql/docs/mysql/connect-functions#public-ip-default_1 | |
} |
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
function streamedCount() { | |
const query = admin.firestore().collection("fruits"); | |
return new Promise<number>(resolve => { | |
let count = 0; | |
const stream = query.stream(); | |
stream.on("data", _ => ++count); | |
stream.on("end", () => resolve(count)); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment