How does NodeJS OS Module work - javascript

Nodejs has a built in OS module that we can use by having this line in our code
var os = require('os');
There are a number of functions to use such as getting free memory, total memory, cpu usage, load average, etc.
My question here is HOW does nodejs calculate this information?
For example, the free/total RAM memory, how is that being done under curtains. Is it calling another process to read stats from the system? is it running a separate command like iostat or dstat? How is it actually retrieving that data for us?

The os.totalmem function is a native function from process.binding('os') called getTotalMem. Their implementations can be found in the Node source code:
The following code can be found in src/node_os.cc:
static void GetTotalMemory(const FunctionCallbackInfo<Value>& args) {
double amount = uv_get_total_memory();
if (amount < 0)
return;
args.GetReturnValue().Set(amount);
}
// ...
env->SetMethod(target, "getTotalMem", GetTotalMemory);
The uv_get_total_memory function has several implementations based on the host OS.
Here is the Linux implementation deps/uv/src/unix/linux-core.c:
uint64_t uv_get_total_memory(void) {
struct sysinfo info;
if (sysinfo(&info) == 0)
return (uint64_t) info.totalram * info.mem_unit;
return 0;
}
The Linux build uses sysinfo to get this information. It does not need to spawn another another process.

Related

IOREDIS - Error Trying to Migrate from Redis to KeyDB

We were using Redis for a plenty of time until we have come to the conclusion that moving to KeyDB may be a good choice for its features.
Environment
OS: Centos 7
NodeJs: v12.18.0
Redis: v6.0.5
Targeted KeyDB: v0.0.0 (git:1069d0b4) // keydb-cli -v showed this. Installed Using Docker.
ioredis: v4.17.3
pm2: v4.2.1 // used for clustering my application.
Background
Referring to the KeyDB documentation, KeyDB is compatible with the latest version of Redis.
KeyDB remains fully compatible with Redis modules API and protocol. As such a migration from Redis to KeyDB is very simple and would be similar to what you would expect migrating in a Redis to Redis scenario. https://docs.keydb.dev/docs/migration/
In the same page they provide a list of redis clients which are compatible with KeyDB. The list contains ioredis which I'm using.
KeyDB is compatible with all Redis clients as listed here so this should not be a concern. Simply use your client as you would with Redis.
https://docs.keydb.dev/docs/migration/
Problem
As said in the documentation. I should be able to migrate easily to KeyDB in a few hours. Well that is not the case! At least not for me! I spent my last 3 days searching on the internet for the solution. I came to the conclusion that I should write to stackoverflow :)
The issue is somehow interesting. The Client is actually working with KeyDb and the process is actually setting and retrieving keys (Not sure but may lose some data during the error.). But On 10% of time it gives me the following error, And continues to work again after a while. As I'm using Redis for storing sessions and other stuff on my production environment; I can not risk ignoring such insisting error.
error: message=write EPIPE, stack=Error: write EPIPE
./app-error-1.log:37: at WriteWrap.onWriteComplete [as oncomplete] (internal/stream_base_commons.js:92:16), errno=EPIPE, code=EPIPE, syscall=write
I searched nearly all the internet for this error but no one provides a solution nor an explanation for what is going wrong.
Luckily the process "sometimes" shows a stack for the error. It points to lib/redis/index.ts:711 inside the ioredis codes. Which I have no idea what it does.
(stream || this.stream).write(command.toWritable());
https://github.com/luin/ioredis/blob/master/lib/redis/index.ts#L711
I found some issues on ioredis github repository mentioning some EPIPE error. But most of them were about error handling stuff and all marked as resolved.
I also found some general EPIPE errors on google(Most of them about socket.io which is not something I use.)
Wrap Up
What is wrong with this thing?
As no one wrote an answer at the end of bounty. I am writing my experience on solving the issue for the people who will get this error later.
Please note that this is not a canonical answer. But It is rather a workaround
I am starting with sharing what was happening.
We were trying to migrate from a Redis server hosting nearly 600 000 keys. Standard migration process was taking a lot of time for transfer that amount of keys from Redis to keyDB. So I came across a different solution.
Our KeyDB works on 2 Active-Active replica servers. I will provide the link to those are wondering how this system works.
https://medium.com/faun/failover-redis-like-cluster-from-two-masters-with-keydb-9ab8e806b66c
The solution was re-building up our Redis data using some MongoDB database aggregation and doing some batch operations on KeyDB.
Here is a simulation(Not exact as source. Also I did not tested for syntax errors)
const startPoint =
(Number.parseInt(process.env.NODE_APP_INSTANCE) || 0) * 40000;
let skip = 0 + startPoint;
let limit = 1000;
let results = await SomeMongooseSchema.find({someQueries}).limit(1000).skip(skip);
let counter = 0;
while (results.length){
if(counter > 39) break;
for(const res of results){
const item = {
key: '',
value: ''
};
// do some build ups on item
...
// end n
app.ioRedisClient.set(item.key, item.value);
}
counter++;
skip = i * limit + startPoint;
results = await SomeMongooseSchema.find({someQueries}).limit(limit).skip(skip);
}
Running this code on 16 processes using pm2 sets all the keys to keyDB in about 45 minutes. (compared to 4-5 hours)
pm2 start app.js -i 16
When we run the code on a Redis server. It works but giving the following error on KeyDB.
error: message=write EPIPE, stack=Error: write EPIPE
./app-error-1.log:37: at WriteWrap.onWriteComplete [as oncomplete] (internal/stream_base_commons.js:92:16), errno=EPIPE, code=EPIPE, syscall=write
Firstly I started by tuning the code by creating a transaction instead of setting every key separately. and setted a 1-second gap between each 1000 operation. the code changed as following.
const startPoint =
(Number.parseInt(process.env.NODE_APP_INSTANCE) || 0) * 40000;
let skip = 0 + startPoint;
let limit = 1000;
let results = await SomeMongooseSchema.find({someQueries}).limit(1000).skip(skip);
const batch = app.ioredisClient.multi();
let counter = 0;
while (results.length){
if(counter > 39) break;
for(const res of results){
const item = {
key: '',
value: ''
};
// do some build ups on item
...
// end n
batch.set(item.key, item.value);
}
counter++;
await batch.exec();
await sleep();
skip = i * limit + startPoint;
results = await SomeMongooseSchema.find({someQueries}).limit(limit).skip(skip);
}
This reduced the error rate as long as the operation time to 20 minutes. But the error was still persisting.
I suspected that this error may be due to some permission errors on the docker version. So I asked our server administrator to check and if possible remove the docker version and install from rpm repository.
https://download.keydb.dev/packages/rpm/centos7/x86_64/
Did that and it worked. All the errors vanished and successfully managed to migrate in 20 minutes.
I don't consider this as a real answer. But this should be useful for some experts to find out what was wrong.

Node.js fs.open() hangs after trying to open more than 4 named pipes (FIFOs)

I have a node.js process that needs to read from multiple named pipes fed by different other processes as an IPC method.
I realized after opening and creating read streams from more than four fifos, that fs seems to no longer be able to open fifos and just hangs there.
It seems that this number is a bit low, considering that it is possible to open thousands of files concurrently without trouble (for instance by replacing mkfifo by touch in the following script).
I tested with node.js v10.1.0 on MacOS 10.13 and with node.js v8.9.3 on Ubuntu 16.04 with the same result.
The faulty script
And a script that displays this behavior:
var fs = require("fs");
var net = require("net");
var child_process = require('child_process');
var uuid = function() {
for (var i = 0, str = ""; i < 32; i++) {
var number = Math.floor(Math.random() * 16);
str += number.toString(16);
}
return str;
}
function setupNamedPipe(cb) {
var id = uuid();
var fifoPath = "/tmp/tmpfifo/" + id;
child_process.exec("mkfifo " + fifoPath, function(error, stdout, stderr) {
if (error) {
return;
}
fs.open(fifoPath, 'r+', function(error, fd) {
if (error) {
return;
}
var stream = fs.createReadStream(null, {
fd
});
stream.on('data', function(data) {
console.log("FIFO data", data.toString());
});
stream.on("close", function(){
console.log("close");
});
stream.on("error", function(error){
console.log("error", error);
});
console.log("OK");
cb();
});
});
}
var i = 0;
function loop() {
++i;
console.log("Open ", i);
setupNamedPipe(loop);
}
child_process.exec("mkdir -p /tmp/tmpfifo/", function(error, stdout, stderr) {
if (error) {
return;
}
loop();
});
This script doesn't clean behind him, don't forget to rm -r /tmp/tmpfifo
Repl.it link
NOTE, The following part of this questions is related to what I already tried to answer the question but might not be central to it
Two interesting facts with this script
when writing twice in one of the FIFO, (ie echo hello > fifo) Node is then able to open one more fifo, but no longer receives from the one in which we wrote
when the read stream is created by directly providing the path to the fifo (instead of fd), the script doesn't block any more, but apparently no longer receive what is written in any of the FIFOs
Debug informations
I then tried to verify whether that could be related to some OS limit, for instance the number of file descriptor open.
Ouput of ulimit -a on the Mac is
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
file size (blocks, -f) unlimited
max locked memory (kbytes, -l) unlimited
max memory size (kbytes, -m) unlimited
open files (-n) 256
pipe size (512 bytes, -p) 1
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 1418
virtual memory (kbytes, -v) unlimited
Nothing points to some limit at 4.
C++ tentative
I then tried to write a similar script in C++.
In C++ the script successfully open a hundred fifos.
Note that there are a few differences between the two implementations. In the C++ one,
the script only open the fifos,
there is no tentative for reading,
and no multithreading
#include <string>
#include <cstring>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>
int main(int argc, char** argv)
{
for (int i=0; i < 100; i++){
std::string filePath = "/tmp/tmpfifo/" + std::to_string(i);
auto hehe = open(filePath.c_str(), O_RDWR);
std::cout << filePath << " " << hehe << std::endl;
}
return 0;
}
As a side note, the fifos need to be created before executing the script, for instance with
for i in $(seq 0 100); do mkfifo /tmp/tmpfifo/$i; done
Potential Node.js related issue
After a bit of search, it also seems to be linked to that issue on the Node.js Github:
https://github.com/nodejs/node/issues/1941.
But people seems to be complaining of the opposite behavior (fs.open() throwing EMFILE errors and not hanging silently...)
As you can see I tried to search in many directions and all of this lead me to my question:
Do you know what could cause this behavior?
Thank you
So I asked the question on the Node.js Github, https://github.com/nodejs/node/issues/23220
From the solution:
Dealing with FIFOs is currently a bit tricky.
The open() system call blocks on FIFOs by default until the other side of the pipe has been opened as well. Because Node.js uses a threadpool for file-system operations, opening multiple pipes where the open() calls don’t finish exhausts this threadpool.
The solution is to open the file in non-blocking mode, but that has the difficulty that the other fs calls aren’t built with non-blocking file descriptors in mind; net.Socket is, however.
So, the solution would look something like this:
fs.open('path/to/fifo/', fs.constants.O_RDONLY | fs.constants.O_NONBLOCK, (err, fd) => {
// Handle err
const pipe = new net.Socket({ fd });
// Now `pipe` is a stream that can be used for reading from the FIFO.
});

CefSharp offscreen with .net core 2.0 Runtime Error , System.IO.FileNotFoundException: 'Could not load file or assembly 'CefSharp.Core,

I am getting this error
System.IO.FileNotFoundException: 'Could not load file or assembly 'CefSharp.Core, Version=63.0.3.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138'. The system cannot find the file specified.'
I am trying to run the cefsharp.minimalexample.offscreen program in .net core 2.0. in visual studio 2017
what I have done so far
1 . Created .net core console application
2 . Installed NuGet packages Cefsharp.Offscreen (which installs the dependencies cefsharp.common and redist)
3 . Installed Microsoft.windows.compatibility nuget package to get the system.drawing in .net core (It was not working with System.Drawing.Common as the Cefsharp ScreenshotAsync function using system.drawing)
These steps will clear all the errors and the project will build successfully.
I am getting the above mentioned error.
I have checked all the required files mentioned in the Cefsharp documentation in the current running folder (debug). All files are available ,Still error is not going away.
It works fine in old Dot net versions 4.6.
I could not find any helping documents for implementing cefsharp.offscreen with .net core any where.
This is the code from the example provided in the Cefsharp.offscreen.
Please let me know if you can shed some light on this issue. Thanks in advance.
public class Program
{
private static ChromiumWebBrowser browser;
public static void Main(string[] args)
{
const string testUrl = "https://www.google.com/";
Console.WriteLine("This example application will load {0}, take a screenshot, and save it to your desktop.", testUrl);
Console.WriteLine("You may see Chromium debugging output, please wait...");
Console.WriteLine();
var settings = new CefSettings()
{
//By default CefSharp will use an in-memory cache, you need to specify a Cache Folder to persist data
CachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CefSharp\\Cache")
};
//Perform dependency check to make sure all relevant resources are in our output directory.
Cef.Initialize(settings, performDependencyCheck: true, browserProcessHandler: null);
// Create the offscreen Chromium browser.
browser = new ChromiumWebBrowser(testUrl);
// An event that is fired when the first page is finished loading.
// This returns to us from another thread.
browser.LoadingStateChanged += BrowserLoadingStateChanged;
// We have to wait for something, otherwise the process will exit too soon.
Console.ReadKey();
// Clean up Chromium objects. You need to call this in your application otherwise
// you will get a crash when closing.
Cef.Shutdown();
}
private static void BrowserLoadingStateChanged(object sender, LoadingStateChangedEventArgs e)
{
// Check to see if loading is complete - this event is called twice, one when loading starts
// second time when it's finished
// (rather than an iframe within the main frame).
if (!e.IsLoading)
{
// Remove the load event handler, because we only want one snapshot of the initial page.
browser.LoadingStateChanged -= BrowserLoadingStateChanged;
var scriptTask = browser.EvaluateScriptAsync("document.getElementById('lst-ib').value = 'CefSharp Was Here!'");
scriptTask.ContinueWith(t =>
{
//Give the browser a little time to render
Thread.Sleep(500);
// Wait for the screenshot to be taken.
var task = browser.ScreenshotAsync();
task.ContinueWith(x =>
{
// Make a file to save it to (e.g. C:\Users\jan\Desktop\CefSharp screenshot.png)
var screenshotPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "CefSharp screenshot.png");
Console.WriteLine();
Console.WriteLine("Screenshot ready. Saving to {0}", screenshotPath);
// Save the Bitmap to the path.
// The image type is auto-detected via the ".png" extension.
task.Result.Save(screenshotPath);
// We no longer need the Bitmap.
// Dispose it to avoid keeping the memory alive. Especially important in 32-bit applications.
task.Result.Dispose();
Console.WriteLine("Screenshot saved. Launching your default image viewer...");
// Tell Windows to launch the saved image.
Process.Start(screenshotPath);
Console.WriteLine("Image viewer launched. Press any key to exit.");
}, TaskScheduler.Default);
});
}
}
}

Detect available memory inside of a WebView

I'm building an app that contains a WebView that runs some JavaScript code. That JavaScript code is quite allocation heavy and can require a lot of memory.
Sometimes, the amount of required memory exceeds the amount JavaScript can require and crashes the Chromium process of the WebView which crashes my app.
I listen to onMemoryTrim in my application - but it is never called in this scenario on devices with more than 1GB of memory. (Not even with TRIM_MEMORY_RUNNING_LOW).
Is there any way I could detect my WebView is running low on memory and either kill it or let it know (so it can free memory)?
I've tried polling performance.memory but it did not work. The following script crashes the WebView if executed in it:
var a = [];
var kek = () => {
var b = [];
for(var i = 0; i < 1024 * 1024 * 2; i++) b.push(Math.random());
return b;
}
var ival = setInterval(() => {
let m = performance.memory;
if(m.jsHeapSizeLimit - m.usedJSHeapSize < 1e5) {
console.log("Memory limited")
} else {
a.push(kek());
}
});
Is there any way to detect memory is about to run out so I can handle it gracefully without the app crashing?
I have discussed this with the Chromium team and the Android team and at the moment (they think and I believe them) that this is impossible.
Sometimes, the amount of required memory exceeds the amount JavaScript can require and crashes the Chromium process of the WebView which crashes my app.
You can however catch out of memory crashes in Android 8.0+ using the new termination handle API. So this works around my problem by not having to check the available memory required in the first place.
By overriding onRenderProcessGone - we get to catch the bug and recreate the WebView.

How to stream audio from browser to WebRTC native C++ application

I have so far managed to run the following sample:
WebRTC native c++ to browser video streaming example
The sample shows how to stream video from a native C++ application (peerconnection_client.exe) to the browser (I am using Chrome). This works fine and I can see myself in the browser.
What I would like to do is to stream audio from the browser to the native application but I am not sure how. Can anyone give me some pointers please?
I'm trying to find a way to stream both video and audio from browser to my native program. and here is my way so far.
To stream video from browser to your native program without gui, just follow the example here. https://chromium.googlesource.com/external/webrtc/+/refs/heads/master/examples/peerconnection/client/
use AddOrUpdateSink to add your own VideoSinkInterface and you will receive your frame data in callback void OnFrame(const cricket::VideoFrame& frame). Instead of render the frame to GUI as the example does, you can do whatever you want.
To stream audio from browser to your native program without real audio device. you can use a fake audio device.
modify variable rtc_use_dummy_audio_file_devices to true in file https://chromium.googlesource.com/external/webrtc/+/master/webrtc/build/webrtc.gni
invoke the global static function to specify the filename webrtc::FileAudioDeviceFactory::SetFilenamesToUse("", "file_to_save_audio");
patch file_audio_device.cc with the code blew. (as I write this answer, FileAudioDevice has some issues, may already be fixed)
recompile your program, touch file_to_save_audio and you will see pcm data in file_to_save_audio after webrtc connection is established.
patch:
diff --git a/webrtc/modules/audio_device/dummy/file_audio_device.cc b/webrtc/modules/audio_device/dummy/file_audio_device.cc
index 8b3fa5e..2717cda 100644
--- a/webrtc/modules/audio_device/dummy/file_audio_device.cc
+++ b/webrtc/modules/audio_device/dummy/file_audio_device.cc
## -35,6 +35,7 ## FileAudioDevice::FileAudioDevice(const int32_t id,
_recordingBufferSizeIn10MS(0),
_recordingFramesIn10MS(0),
_playoutFramesIn10MS(0),
+ _initialized(false),
_playing(false),
_recording(false),
_lastCallPlayoutMillis(0),
## -135,12 +136,13 ## int32_t FileAudioDevice::InitPlayout() {
// Update webrtc audio buffer with the selected parameters
_ptrAudioBuffer->SetPlayoutSampleRate(kPlayoutFixedSampleRate);
_ptrAudioBuffer->SetPlayoutChannels(kPlayoutNumChannels);
+ _initialized = true;
}
return 0;
}
bool FileAudioDevice::PlayoutIsInitialized() const {
- return true;
+ return _initialized;
}
int32_t FileAudioDevice::RecordingIsAvailable(bool& available) {
## -236,7 +238,7 ## int32_t FileAudioDevice::StopPlayout() {
}
bool FileAudioDevice::Playing() const {
- return true;
+ return _playing;
}
int32_t FileAudioDevice::StartRecording() {
diff --git a/webrtc/modules/audio_device/dummy/file_audio_device.h b/webrtc/modules/audio_device/dummy/file_audio_device.h
index a69b47e..3f3c841 100644
--- a/webrtc/modules/audio_device/dummy/file_audio_device.h
+++ b/webrtc/modules/audio_device/dummy/file_audio_device.h
## -185,6 +185,7 ## class FileAudioDevice : public AudioDeviceGeneric {
std::unique_ptr<rtc::PlatformThread> _ptrThreadRec;
std::unique_ptr<rtc::PlatformThread> _ptrThreadPlay;
+ bool _initialized;;
bool _playing;
bool _recording;
uint64_t _lastCallPlayoutMillis;
I know this is an old question, but I struggled myself to find a solution currently so I thought sharing is appreciated.
There's is one more or less simple way to get an example running which streams from the browser to native code.You need the webrtc source http://www.webrtc.org/native-code/development
The two tools you need are the peerconnection server and client. Both can be found in the folder talk/example/peerconnection
To get it working you need to patch it to enable DTLS for the peerconnection client. So patch it with the patch provided here https://code.google.com/p/webrtc/issues/detail?id=3872 and rebuild the client. Now you are set up on the native site!
For the browser I recommend the peer2peer example from here https://github.com/GoogleChrome/webrtc after starting the peerconnection_server and connection the peerconnection_client try to connect with the peer2peer example.
Maybe a connection constraint is necessary:
{
"DtlsSrtpKeyAgreement": true
}
you could use the following example which implement a desktop client for appRTC.
https://github.com/TemasysCommunications/appRTCDesk
this completes and interop with the web client, android client and iOs client provided by the open source implementation at webrtc.org, giving you a full suite of clients to work with their free server. peer connection_{client|server} is an old example from the lib jingle time (pre webrtc) and does not interop with anything else.

Categories

Resources