Skip to content

Commit

Permalink
Merge 177ca0d into dad143c
Browse files Browse the repository at this point in the history
  • Loading branch information
samtstern committed Jul 19, 2019
2 parents dad143c + 177ca0d commit 88f5206
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 58 deletions.
1 change: 1 addition & 0 deletions changelog.txt
@@ -1,2 +1,3 @@
* Allow the RTDB emulator to hot-reload rules file on changes.
* Allows emulated Cloud Functions to talk to production RTDB/Firestore if the emulators are not running.
* Fixes an issue where internal logs would sometimes appear in stdout.
19 changes: 6 additions & 13 deletions src/emulator/functionsEmulator.ts
Expand Up @@ -801,6 +801,11 @@ export function InvokeRuntime(
stdout: { pipe: runtime.stdout, value: "" },
};

const ipcBuffer = { value: "" };
runtime.on("message", (message: any) => {
onData(runtime, emitter, ipcBuffer, message);
});

for (const id in buffers) {
if (buffers.hasOwnProperty(id)) {
const buffer = buffers[id];
Expand Down Expand Up @@ -836,19 +841,7 @@ function onData(
buffer: { value: string },
buf: Buffer
): void {
let bufString = buf.toString();

// Remove all chunk markings from the message
const endedWithChunk = bufString.endsWith(EmulatorLog.CHUNK_DIVIDER);
while (bufString.indexOf(EmulatorLog.CHUNK_DIVIDER) >= 0) {
bufString = bufString.replace(EmulatorLog.CHUNK_DIVIDER, "");
}
buffer.value += bufString;

// If the message ended with a chunk divider, we just wait for more to come.
if (endedWithChunk) {
return;
}
buffer.value += buf.toString();

const lines = buffer.value.split("\n");

Expand Down
62 changes: 17 additions & 45 deletions src/emulator/types.ts
Expand Up @@ -76,10 +76,6 @@ export interface Address {
}

export class EmulatorLog {
// Messages over 8192 bytes cause issues
static CHUNK_DIVIDER = "__CHUNK__";
static CHUNK_SIZE = 8000;

get date(): Date {
if (!this.timestamp) {
return new Date(0);
Expand Down Expand Up @@ -153,37 +149,13 @@ export class EmulatorLog {
}

/**
* As discovered in #1486, some very large log messages (>8192B) were not being properly passed
* between the emulator runtime and the emulator itself. We were falsely making the assumption
* that we could pass messages of any length over stdout and the stream reader would always get
* a whole message in a single "data" callback.
*
* Now we chunk the messages into 8000B pieces and then add a signal (CHUNK_DIVIDER) to the
* end of partial messages that instructs the receiver to wait for the whole message to
* appear.
*
* We use a global boolean to know if all of our messages have been flushed, and the functions
* emulator can wait on this variable to flip before exiting. This ensures that we never
* miss a log message that has been queued but has not yet flushed from stdout.
*
* Note: it would be better to use ipc via process.send() since that is faster and has
* extremely large message limits but in some experiments the IPC channel seemed to get
* full and not flush, so stdout remains.
* miss a log message that has been queued but has not yet flushed.
*/
log(): void {
const msg = `${this.toString()}\n`;
const chunks = this.chunkString(msg);

for (let i = 0; i < chunks.length; i++) {
const chunk = chunks[i];
const isLast = i === chunks.length - 1;
if (isLast) {
this.bufferMessage(chunk);
} else {
this.bufferMessage(chunk + EmulatorLog.CHUNK_DIVIDER);
}
}

this.bufferMessage(msg);
this.flush();
}

Expand All @@ -198,23 +170,23 @@ export class EmulatorLog {
}

EmulatorLog.WAITING_FOR_FLUSH = true;
process.stdout.write(nextMsg, () => {
EmulatorLog.WAITING_FOR_FLUSH = EmulatorLog.LOG_BUFFER.length > 0;
this.flush();
});
}

private chunkString(msg: string): string[] {
const chunks: string[] = [];
const numChunks = Math.ceil(msg.length / EmulatorLog.CHUNK_SIZE);
if (process.send) {
// For some reason our node.d.ts file does not include the version of subprocess.send() with a callback
// but the node docs assert that it has an optional callback.
// https://nodejs.org/api/child_process.html#child_process_subprocess_send_message_sendhandle_options_callback
(process.send as any)(nextMsg, undefined, {}, (err: any) => {
if (err) {
process.stderr.write(err);
}

for (let i = 0; i < numChunks; i++) {
const start = i * EmulatorLog.CHUNK_SIZE;
const length = EmulatorLog.CHUNK_SIZE;
chunks.push(msg.substr(start, length));
EmulatorLog.WAITING_FOR_FLUSH = EmulatorLog.LOG_BUFFER.length > 0;
this.flush();
});
} else {
process.stderr.write(
"subprocess.send() is undefined, cannot communicate with Functions Runtime."
);
}

return chunks;
}

private toStringCore(pretty = false): string {
Expand Down

0 comments on commit 88f5206

Please sign in to comment.