Skip to content

Commit

Permalink
feat: add lru cache and block info pagination (#26)
Browse files Browse the repository at this point in the history
Description
---
Add pagination to all outpus/inputs/kernels.
Add caching on the express server for this pagination requests.

Motivation and Context
---

How Has This Been Tested?
---
Manually.

What process can a PR reviewer use to test or verify this change?
---
Go to some very heavy block and see that the request is fast and also
lightweight.

<!-- Checklist -->
<!-- 1. Is the title of your PR in the form that would make nice release
notes? The title, excluding the conventional commit
tag, will be included exactly as is in the CHANGELOG, so please think
about it carefully. -->


Breaking Changes
---

- [x] None
- [ ] Requires data directory on base node to be deleted
- [ ] Requires hard fork
- [ ] Other - Please specify

<!-- Does this include a breaking change? If so, include this line as a
footer -->
<!-- BREAKING CHANGE: Description what the user should do, e.g. delete a
database, resync the chain -->
  • Loading branch information
leet4tari authored Feb 28, 2024
2 parents 7c0b19c + e2e44dd commit d19948c
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 10 deletions.
3 changes: 3 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ hbs.registerHelper("chart", function (data, height) {
hbs.registerHelper("json", function (obj) {
return JSON.stringify(obj);
});
hbs.registerHelper("add", function (a, b) {
return a + b;
});

hbs.registerPartials(path.join(__dirname, "partials"));

Expand Down
31 changes: 31 additions & 0 deletions cache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
class Cache {
constructor(limit = 1000) {
this.limit = limit;
this.cache = new Map();
}

set(key, value) {
if (this.cache.size >= this.limit) {
const firstItemKey = this.cache.keys().next().value;
this.cache.delete(firstItemKey);
}
this.cache.set(key, value);
}

async get(func, args) {
let cache_key = JSON.stringify(args);
if (this.cache.has(cache_key)) {
const temp = this.cache.get(cache_key);
this.cache.delete(cache_key);
this.cache.set(cache_key, temp);
return temp;
}
let result = await func(args);
console.log("Actual call", args);
this.set(cache_key, result);
return result;
}
}

var cache = new Cache();
module.exports = cache;
143 changes: 141 additions & 2 deletions routes/blocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
var { createClient } = require("../baseNodeClient");

var express = require("express");
const cache = require("../cache");
var router = express.Router();

function fromHexString(hexString) {
Expand Down Expand Up @@ -56,13 +57,151 @@ router.get("/:height_or_hash", async function (req, res) {
height = parseInt(height_or_hash);
}

block = await client.getBlocks({ heights: [height] });
let request = { heights: [height] };
block = await cache.get(client.getBlocks, request);
if (!block || block.length === 0) {
res.status(404);
res.render("404", { message: `Block at height ${height} not found` });
return;
}

let outputs_from = +(req.query.outputs_from || 0);
let outputs_to = +(req.query.outputs_to || 10);
let inputs_from = req.query.inputs_from || 0;
let inputs_to = +(req.query.inputs_to || 10);
let kernels_from = +(req.query.kernels_from || 0);
let kernels_to = +(req.query.kernels_to || 10);
let body = {
outputs_length: block[0].block.body.outputs.length,
inputs_length: block[0].block.body.inputs.length,
kernels_length: block[0].block.body.kernels.length,
outputs: block[0].block.body.outputs.slice(outputs_from, outputs_to),
inputs: block[0].block.body.inputs.slice(inputs_from, inputs_to),
kernels: block[0].block.body.kernels.slice(kernels_from, kernels_to),
outputsNext: null,
outputsNextLink: null,
outputsPrev: null,
outputsPrevLink: null,
outputsFrom: outputs_from,
inputsNext: null,
inputsNextLink: null,
inputsPrev: null,
inputsPrevLink: null,
inputsFrom: inputs_from,
kernelsNext: null,
kernelsNextLink: null,
kernelsPrev: null,
kernelsPrevLink: null,
kernelsFrom: kernels_from,
};
if (outputs_from > 0) {
body.outputsPrev = `${outputs_from - 10}..${outputs_from - 1}`;
body.outputsPrevLink =
"/blocks/" +
height +
"?outputs_from=" +
(outputs_from - 10) +
"&outputs_to=" +
(outputs_to - 10) +
"&inputs_from=" +
inputs_from +
"&inputs_to=" +
inputs_to +
"&kernels_from=" +
kernels_from +
"&kernels_to=" +
kernels_to;
}
if (outputs_to < body.outputs_length) {
body.outputsNext = `${outputs_to}..${outputs_to + 9}`;
body.outputsNextLink =
"/blocks/" +
height +
"?outputs_from=" +
(outputs_from + 10) +
"&outputs_to=" +
(outputs_to + 10) +
"&inputs_from=" +
inputs_from +
"&inputs_to=" +
inputs_to +
"&kernels_from=" +
kernels_from +
"&kernels_to=" +
kernels_to;
}
if (inputs_from > 0) {
body.inputsPrev = `${inputs_from - 10}..${inputs_from - 1}`;
body.inputsPrevLink =
"/blocks/" +
height +
"?outputs_from=" +
outputs_from +
"&outputs_to=" +
outputs_to +
"&inputs_from=" +
(inputs_from - 10) +
"&inputs_to=" +
(inputs_to - 10) +
"&kernels_from=" +
kernels_from +
"&kernels_to=" +
kernels_to;
}
if (inputs_to < body.inputs_length) {
body.inputsNext = `${inputs_to}..${inputs_to + 9}`;
body.inputsNextLink =
"/blocks/" +
height +
"?outputs_from=" +
outputs_from +
"&outputs_to=" +
outputs_to +
"&inputs_from=" +
(inputs_from + 10) +
"&inputs_to=" +
(inputs_to + 10) +
"&kernels_from=" +
kernels_from +
"&kernels_to=" +
kernels_to;
}
if (kernels_from > 0) {
body.kernelsPrev = `${kernels_from - 10}..${kernels_from - 1}`;
body.kernelsPrevLink =
"/blocks/" +
height +
"?outputs_from=" +
outputs_from +
"&outputs_to=" +
outputs_to +
"&inputs_from=" +
inputs_from +
"&inputs_to=" +
inputs_to +
"&kernels_from=" +
(kernels_from - 10) +
"&kernels_to=" +
(kernels_to - 10);
}
if (kernels_to < body.kernels_length) {
body.kernelsNext = `${kernels_to}..${kernels_to + 9}`;
body.kernelsNextLink =
"/blocks/" +
height +
"?outputs_from=" +
outputs_from +
"&outputs_to=" +
outputs_to +
"&inputs_from=" +
inputs_from +
"&inputs_to=" +
inputs_to +
"&kernels_from=" +
(kernels_from + 10) +
"&kernels_to=" +
(kernels_to + 10);
}
let tipInfo = await client.getTipInfo({});
let tipHeight = parseInt(tipInfo.metadata.best_block_height);

Expand All @@ -82,7 +221,7 @@ router.get("/:height_or_hash", async function (req, res) {
prevHeight,
nextLink,
nextHeight,
block: block[0].block,
body: body,
pows: { 0: "Monero", 1: "SHA-3" },
};
if (req.query.json !== undefined) {
Expand Down
40 changes: 32 additions & 8 deletions views/blocks.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -73,20 +73,28 @@
</tr>
</table>

{{#with this.block.body}}
{{#with this.body}}
<table>
<thead>
<tr>
<th>
<h2>Outputs({{outputs.length}})</h2>
<h2>Outputs({{outputs_length}})</h2>
<p>
{{#if outputsPrevLink}}
<a href="{{outputsPrevLink}}">&lt Outputs {{outputsPrev}}</a>
{{/if}}
{{#if outputsNextLink}}
<a href="{{outputsNextLink}}">Outputs {{outputsNext}} &gt;</a>
{{/if}}
</p>
</th>
</tr>
</thead>
<tbody>
{{#each outputs as |output|}}
<tr>
<td>
<h3>Output {{@index}}</h3>
<h3>Output {{add @index ../outputsFrom}}</h3>
{{>TransactionOutput output}}
</td>
</tr>
Expand All @@ -98,15 +106,23 @@
<thead>
<tr>
<th>
<h2>Inputs({{inputs.length}})</h2>
<h2>Inputs({{inputs_length}})</h2>
<p>
{{#if inputsPrevLink}}
<a href="{{inputsPrevLink}}">&lt Inputs {{inputsPrev}}</a>
{{/if}}
{{#if inputsNextLink}}
<a href="{{inputsNextLink}}">Inputs {{inputsNext}} &gt;</a>
{{/if}}
</p>
</th>
</tr>
</thead>
<tbody>
{{#each inputs as |input|}}
<tr>
<td>
<h3>Input {{@index}}</h4>
<h3>Input {{add @index ../inputsFrom}}</h4>
{{>TransactionInput input}}
</td>
</tr>
Expand All @@ -118,19 +134,27 @@
<thead>
<tr>
<th>
<h2>Kernels({{kernels.length}})</h2>
<h2>Kernels({{kernels_length}})</h2>
<p>
{{#if kernelsPrevLink}}
<a href="{{kernelsPrevLink}}">&lt Kernels {{kernelsPrev}}</a>
{{/if}}
{{#if kernelsNextLink}}
<a href="{{kernelsNextLink}}">Kernels {{kernelsNext}} &gt;</a>
{{/if}}
</p>
</th>
</tr>
</thead>
<tbody>
{{#each kernels as |kernel|}}
<tr>
<td>
<h3>Kernel {{@index}}</h3>
<h3>Kernel {{add @index ../kernelsFrom}}</h4>
{{>TransactionKernel kernel}}
</td>
</tr>
{{/each}}
</tbody>
</table>
{{/with}}
{{/with}}

0 comments on commit d19948c

Please sign in to comment.