baris

joined 11 years ago
[–] baris@community.nodebb.org 1 points 1 week ago

To enable the coursel mode, go to the plugin settings page at /admin/plugins/recentcards and turn it on.

image.png

[–] baris@community.nodebb.org 1 points 3 weeks ago

Minimum mongodb version is 3.6 and that is ancient you will be fine with the latest of both Redis and MongoDB. You can use both if you want, select mongodb during installation and then add a redis block into your config.json to use it for session handling. We have examples at https://docs.nodebb.org/configuring/scaling/#configure-redis.

v4.0 didn't have a lot of breaking changes like v3.0 so all plugins that were updated for 3.x will work for 4.x.

[–] baris@community.nodebb.org 1 points 3 weeks ago

It's at manage privileges section

image.png

[–] baris@community.nodebb.org 1 points 3 weeks ago

Uploading files/images is part of core, if the user has the upload privilege then the upload image button will show up in the composer.

[–] baris@community.nodebb.org 1 points 3 weeks ago

Those are just build warnings, you can ignore those. What do you get when you run node app.js

[–] baris@community.nodebb.org 1 points 3 weeks ago

Yes you can fork a child theme and make your own changes.

[–] baris@community.nodebb.org 1 points 3 weeks ago

That plugin is only for google adsense, depending on how your ads work you could embed them as html widgets. Those allow adding html and javascript.

[–] baris@community.nodebb.org 1 points 3 weeks ago

https://github.com/NodeBB-Community/nodebb-plugin-adsense

You can place ads in widget areas.

Widget visibility can be controlled by group in the ACP, place a widget and then select only the guests group if you want to show it to them.

[–] baris@community.nodebb.org 1 points 4 weeks ago

There is a toggle at the bottom to enable it, did you turn it on?

image.png

[–] baris@community.nodebb.org 1 points 4 weeks ago

@wall_e@ioc.exchange This is done now, consecutive shares are merged.

[–] baris@community.nodebb.org 1 points 4 weeks ago* (last edited 4 weeks ago)

The ttl should be in milliseconds, so try 600000.

You don't need await on cache.set since it is not async function. If you don't set a ttl it will stay in the cache. It will be removed if there is no space left and the key wasn't used recently.

[–] baris@community.nodebb.org 1 points 4 weeks ago

What's the output when you run npm audit? If they are direct dependencies we can upgrade them.

 

You can go to your theme settings and enable a new option to adjust the layout of the topic page.

One thing that always bothered me in Persona—and to some extent in Harmony—was how far down the main post appeared on the page. This new option helps alleviate that issue. By default, the post bar remains at the top, and the mobile view remains unchanged.

Here’s a screenshot:

f403770d-3ea0-4483-a0c0-528021192040-image.png

 

This will be a post about the various caches in NodeBB and how they work.

9377127e-e16d-41a0-96d9-a86b73f945ac-image.png

What is caching?

There are 4 different caches, I will go over each one and explain how they work and how they help make nodebb faster. But before that let's remember what a cache is and how they help:

>In computing, a cache is a hardware or software component that stores data so that future requests for that data can be served faster; the data stored in a cache might be the result of an earlier computation or a copy of data stored elsewhere. A cache hit occurs when the requested data can be found in a cache, while a cache miss occurs when it cannot. Cache hits are served by reading data from the cache, which is faster than recomputing a result or reading from a slower data store; thus, the more requests that can be served from the cache, the faster the system performs.

>To be cost-effective and to enable efficient use of data, caches must be relatively small. Nevertheless, caches have proven themselves in many areas of computing, because typical computer applications access data with a high degree of locality of reference. Such access patterns exhibit temporal locality, where data is requested that has been recently requested already, and spatial locality, where data is requested that is stored physically close to data that has already been requested.

Source: https://en.wikipedia.org/wiki/Cache_(computing)

Cache Module used in NodeBB

All the caches in nodebb use the nodejs module https://www.npmjs.com/package/lru-cache, A cache object that deletes the least-recently-used items. It let's you cache things and drop things out of the cache if they are not used frequently.

Let's go chronologically and see what caches there are and why they were added.

Post Cache

(added in 2015)

Out of all the caches this one was the most obvious. When a user types a post the content of that post is stored as is in the database. In NodeBB's case this is markdown but other formats can be used as well. Storing the text as the user typed it makes it easy to implement things like editing, since we can just display the same content in the composer. But when we need to display the post as part of the webpage we need to convert it into html. This process happens in :

Posts.parsePost = async function (postData) {
	if (!postData) {
		return postData;
	}
	postData.content = String(postData.content || '');
	const cache = require('./cache');
	const pid = String(postData.pid);
	const cachedContent = cache.get(pid);
	if (postData.pid && cachedContent !== undefined) {
		postData.content = cachedContent;
		return postData;
	}

	const data = await plugins.hooks.fire('filter:parse.post', { postData: postData });
	data.postData.content = translator.escape(data.postData.content);
	if (data.postData.pid) {
		cache.set(pid, data.postData.content);
	}
	return data.postData;
};

To turn the post content into html the hook filter:parse.post is used by nodebb-plugin-markdown https://github.com/NodeBB/nodebb-plugin-markdown/blob/master/index.js#L152-L158. This is done entirely in the nodebb process and blocks the cpu since it is not an async IO operation. When displaying a topic we do this for 20 posts(1 page) and if 20 users are loading the same topic we are doing 400 parses to generate the same output. If the posts are longer the process takes more time as well.

So it was a no-brainer to cache the parsed content since it rarely changes and we can just display the cached html content. As you can see from the first piece of code if the post content is cached filter:parse.post isn't fired and we don't spend time in the plugins.

This cache makes topic pages faster and reduces the cpu usage of the nodebb process.

Group Cache

(added in 2016)

Next up is the group cache, this cache is used in the groups module and caches all group membership checks.

Groups.isMember = async function (uid, groupName) {
	if (!uid || parseInt(uid, 10) <= 0 || !groupName) {
		return false;
	}

	const cacheKey = `${uid}:${groupName}`;
	let isMember = Groups.cache.get(cacheKey);
	if (isMember !== undefined) {
		return isMember;
	}
	isMember = await db.isSortedSetMember(`group:${groupName}:members`, uid);
	Groups.cache.set(cacheKey, isMember);
	return isMember;
};

When you load any page in NodeBB there is almost always a privilege check to see if you can see certain content or perform certain actions. Can this user see all categories? Can this user post a reply to this topic? All of these questions are answered by checking if the user is part of a specific group.

After the initial setup of the forum group membership rarely changes so it was a perfect candidate for caching.

Unlike the post cache which lowers the cpu usage of the nodebb process, this cache reduces the calls made to the database. Instead of making one or more database calls on every navigation we just make them once and store the results.

Object Cache

(added in 2017)

The reason why this cache is named Object cache is because it caches the results of db.getObject(s) calls.

module.getObjectsFields = async function (keys, fields) {
	if (!Array.isArray(keys) || !keys.length) {
		return [];
	}
	const cachedData = {};
	const unCachedKeys = cache.getUnCachedKeys(keys, cachedData);
	let data = [];
	if (unCachedKeys.length >= 1) {
		data = await module.client.collection('objects').find(
			{ _key: unCachedKeys.length === 1 ? unCachedKeys[0] : { $in: unCachedKeys } },
			{ projection: { _id: 0 } }
		).toArray();
		data = data.map(helpers.deserializeData);
	}

	const map = helpers.toMap(data);
	unCachedKeys.forEach((key) => {
		cachedData[key] = map[key] || null;
		cache.set(key, cachedData[key]);
	});

	if (!Array.isArray(fields) || !fields.length) {
		return keys.map(key => (cachedData[key] ? { ...cachedData[key] } : null));
	}
	return keys.map((key) => {
		const item = cachedData[key] || {};
		const result = {};
		fields.forEach((field) => {
			result[field] = item[field] !== undefined ? item[field] : null;
		});
		return result;
	});
};

Every time we load a page we make calls to the database to load the post/topic/user/category objects. These all use the call db.getObjects. Since these objects rarely change and are requested frequently it makes sense to cache these as well.

This lowers the load on the database significantly. Once a topic has been accessed most of the data required will be in the cache for anyone else loading the same topic.

Local Cache

(added in 2018)

The last cache is a bit different than the others as it was added to cache different types of things and can be used from plugins as well. I will give two examples of how this is used in nodebb to make pages faster.

The first one is the list of categories. Whenever someone loads the /categories page we load a list of category ids. Since the list of categories rarely changes we cache it in the local cache.

Categories.getAllCidsFromSet = async function (key) {
	let cids = cache.get(key);
	if (cids) {
		return cids.slice();
	}

	cids = await db.getSortedSetRange(key, 0, -1);
	cids = cids.map(cid => parseInt(cid, 10));
	cache.set(key, cids);
	return cids.slice();
};

Another use of this cache is for plugin settings. Whenever a plugin loads settings with await meta.settings.get('mypluginid'); the result is loaded and cached.

Settings.get = async function (hash) {
	const cached = cache.get(`settings:${hash}`);
	if (cached) {
		return _.cloneDeep(cached);
	}
	const [data, sortedLists] = await Promise.all([
		db.getObject(`settings:${hash}`),
		db.getSetMembers(`settings:${hash}:sorted-lists`),
	]);
	const values = data || {};
	await Promise.all(sortedLists.map(async (list) => {
		const members = await db.getSortedSetRange(`settings:${hash}:sorted-list:${list}`, 0, -1);
		const keys = members.map(order => `settings:${hash}:sorted-list:${list}:${order}`);

		values[list] = [];

		const objects = await db.getObjects(keys);
		objects.forEach((obj) => {
			values[list].push(obj);
		});
	}));

	const result = await plugins.hooks.fire('filter:settings.get', { plugin: hash, values: values });
	cache.set(`settings:${hash}`, result.values);
	return _.cloneDeep(result.values);
};

Out of all the caches used this one will have the highest hit rate as you can see from the screenshot. Because the things stored in this cache pretty much never change during regular operation, once the list of categories is loaded it will always be served from the cache unless you add or reorder categories.

ACP Page

(/admin/advanced/cache)

On the ACP cache page you can enable/disable the cache, see the hit rate for each cache and even download the contents of the cache as json.

You can also empty the cache here, this is sometimes useful if you have to make changes to the database directly via CLI, for example if you make a change in the database to a topic title and if that topic is cached you won't see the changes until you clear the object cache.

Hope this answers any questions about the caches in NodeBB! :runner:

0
Who is using NodeBB? (community.nodebb.org)
submitted 11 years ago* (last edited 2 years ago) by baris@community.nodebb.org to c/general-discussion@community.nodebb.org
 

Let's keep a list of NodeBB installations here :

|-----------------------------------------------------------------|----------------------------------------------------------------| | Ubisoft | The Burrow by DBS | Sea of Thieves | MOZ | Opera |Phantasy Star Online 2 | Ironheart | MangoLassi | Online Soccer Manager | Exo.do | Team Coaching Global Alliance | MLB The Show
Notepad++ Community | Focus Home Interactive |Bleeding Edge
| Klockprat | The Daily WTF | OneKite I-doit Community |Ninjabet |Ninjabet Germany | Ninjabet Spain | Ninjabet Brazil | CSBsConnect
|Profit Accumulator | OddsMonkey |Internet of Things Onewheel |Suunto | Widora
| Basenji | Netgate | Flic Dataclay | Vivaldi | Bitcoin Hrvatska Videostream Community | Sport bikes | Afraid to Ask
|Qt | ÁgoraPIC | CryptoFR
SVS Learn | Crowdfund Talks | MBAtious
Gruppe Adler | Cplusplus |La Cabane Libre
ITProTV | Kubuckets | HardLimit NFC Ring | Schönen-Dunk | Narfell
| Yesilkart Forum | Maxon | Destiny's Divide Forum
MySensors | Meatgistics | Minecraft Forge France |Pure Data Forum | Discuss IMDb | TcgForum ioBroker | My VR | SailfishOS Club | SweetP Productions | Ave Ziomalu | SAES RPG Mitmachim Top | Gamelancer | Plebplace FOG Project | Emerge Australia Community | CasinoMonkey | MH-ET Live | Virtual Exchange | Internet of Water | GTSS Academy |PIER | MyRoute | Next Coder | JSCAD | Cincom | Domegaia| DrawBot | RoboFont Phenomlab | Rusticaland | Quasar Framework Ch2i | Crypto.Pro | TutorPlace |FAForever | Veyon | Python Forum
| Discuss Places | Happy US Club| GGG Realms | Sudonix | Planete Warez | Hostrisk.com | Deepnn.science | Miktzav.com | The Falcon BMS Community | Wave Anime Radio | Socialpoint Games | J-Novel Club | WLSDM Community | Cyberforums | SysAdmins Zone | Godot Community |

If you are running your own NodeBB let us know we will add it to the list!

view more: next ›