improve update performance

This commit is contained in:
liangping 2024-03-26 11:53:33 +08:00
parent 86a5e9c0da
commit 0b8d4dc987
2 changed files with 103 additions and 122 deletions

View File

@ -1,51 +1,14 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Commit } from '@/types'; import type { PropType } from 'vue';
import { computed, type PropType } from 'vue';
const props = defineProps({ const props = defineProps({
blocks: { type: Array as PropType<Commit[]> }, blocks: { type: Array as PropType<{height:string, color: string}[]> },
validator: { type: String },
}); });
const bars = computed(() => {
const uptime = Array(50).fill({ height: 0, color: 'bg-secondary' });
if(!props.blocks) return uptime
props.blocks.forEach((element) => {
// @deprecated
// const has = element.signatures?.findIndex(
// (sig) => sig.validator_address === props.validator
// );
// // console.log(has, props.validato, element)
// uptime.push({
// height: element.height,
// color: has > -1 ? 'bg-green-500' : 'bg-red-500',
// });
// show flag nil
const signature = element.signatures?.find(
(sig) => sig.validator_address === props.validator
);
// console.log("sign", signature)
let color = `bg-red-500`
if(signature) {
if(signature.block_id_flag === 'BLOCK_ID_FLAG_COMMIT') {
color = `bg-green-500`
} else {
color = `bg-yellow-500`
}
}
uptime.push({
height: element.height,
color,
});
uptime.shift();
});
return uptime;
});
</script> </script>
<template> <template>
<div class="flex gap-0.5"> <div class="flex gap-0.5">
<div class="cursor-default" v-for="(item, index) in bars" :key="index"> <div class="cursor-default" v-for="(item, index) in blocks" :key="index">
<div class="tooltip" <div class="tooltip"
:data-tip="item.height" :data-tip="item.height"
:class="item.color" :class="item.color"

View File

@ -2,14 +2,14 @@
import { ref, onMounted, computed, onUnmounted } from 'vue'; import { ref, onMounted, computed, onUnmounted } from 'vue';
import { fromHex, toBase64 } from '@cosmjs/encoding'; import { fromHex, toBase64 } from '@cosmjs/encoding';
import { import {
useFormatter,
useStakingStore, useStakingStore,
useBaseStore, useBaseStore,
useBlockchain, useBlockchain,
useFormatter,
} from '@/stores'; } from '@/stores';
import UptimeBar from '@/components/UptimeBar.vue'; import UptimeBar from '@/components/UptimeBar.vue';
import type { Commit, SlashingParam, SigningInfo } from '@/types'; import type { SlashingParam, SigningInfo } from '@/types';
import { consensusPubkeyToHexAddress, pubKeyToValcons, valconsToBase64 } from '@/libs'; import { consensusPubkeyToHexAddress, valconsToBase64 } from '@/libs';
const props = defineProps(['chain']); const props = defineProps(['chain']);
@ -18,64 +18,87 @@ const format = useFormatter();
const baseStore = useBaseStore(); const baseStore = useBaseStore();
const chainStore = useBlockchain(); const chainStore = useBlockchain();
const latest = ref(0); const latest = ref(0);
const commits = ref([] as Commit[]);
const keyword = ref(''); const keyword = ref('');
const live = ref(true); const live = ref(true);
const slashingParam = ref({} as SlashingParam); const slashingParam = ref({} as SlashingParam);
const signingInfo = ref({} as Record<string, SigningInfo>); const signingInfo = ref({} as Record<string, SigningInfo>);
// filter validators by keywords interface BlockColor {
const validators = computed(() => { height: string;
if (keyword) color: string;
return stakingStore.validators.filter( }
(x) => x.description.moniker.indexOf(keyword.value) > -1 interface ValidatorUnit {
); moniker: string;
return stakingStore.validators; blocks: BlockColor[];
hex: string;
base64: string;
missed_blocks_counter: number | string;
uptime: number;
signing: SigningInfo;
}
function padding(blocks: BlockColor[] = []) {
const raw = Array(50).fill({ height: "0", color: 'bg-secondary' } as BlockColor).concat(blocks)
return raw.slice(raw.length - 50);
}
const validatorSet = computed(() => {
return stakingStore.validators.map((v) => {
const hex = consensusPubkeyToHexAddress(v.consensus_pubkey)
return {
moniker: v.description.moniker,
hex,
base64: toBase64(fromHex(hex))
};
});
}); });
const list = computed(() => { const blocks = ref({} as Record<string, BlockColor[]>);
if(chainStore.isConsumerChain) {
stakingStore.loadKeyRotationFromLocalstorage(baseStore.latest?.block?.header?.chain_id)
const window = Number(slashingParam.value.signed_blocks_window || 0); const grid = computed(() => {
const vset = validators.value.map((v) => {
const validators = keyword.value.length === 0 ? validatorSet.value :
const hexAddress = stakingStore.findRotatedHexAddress(v.consensus_pubkey) validatorSet.value.filter((v) => v.moniker.toLowerCase().includes(keyword.value.toLowerCase()));
const signing =
signingInfo.value[hexAddress]; const window = Number(slashingParam.value.signed_blocks_window || 0);
return { return validators.map((v) => {
v, const signing = signingInfo.value[v.hex];
signing, const uptime = signing && window > 0
hex: toBase64(fromHex(hexAddress)),
uptime:
signing && window > 0
? (window - Number(signing.missed_blocks_counter)) / window ? (window - Number(signing.missed_blocks_counter)) / window
: undefined, : undefined
}; return {
moniker: v.moniker,
hex: v.hex,
base64: v.base64,
blocks: padding(blocks.value[v.base64] || []) ,
uptime,
missed_blocks_counter: signing?.missed_blocks_counter || 0,
signing
} as ValidatorUnit;
})
});
baseStore.$subscribe((_, state) => {
if(Number(state.latest.block.header.height) % 7 === 0 ) updateTotalSigningInfo();
state.latest.block.last_commit?.signatures?.forEach((s) => {
const block = blocks.value[s.validator_address] || [];
block.push({
height: state.latest.block.header.height,
color: s.block_id_flag === 'BLOCK_ID_FLAG_COMMIT' ? 'bg-green-500' :
s.block_id_flag === 'BLOCK_ID_FLAG_NIL' ? 'bg-yellow-500' : 'bg-red-500',
}); });
return vset; if (block.length > 50) {
} else { block.shift();
const window = Number(slashingParam.value.signed_blocks_window || 0); }
const vset = validators.value.map((v) => { blocks.value[s.validator_address] = block;
const signing = });
signingInfo.value[consensusPubkeyToHexAddress(v.consensus_pubkey)];
return {
v,
signing,
hex: toBase64(fromHex(consensusPubkeyToHexAddress(v.consensus_pubkey))),
uptime:
signing && window > 0
? (window - Number(signing.missed_blocks_counter)) / window
: undefined,
};
});
return vset;
}
}); });
onMounted(() => { onMounted(() => {
live.value = true; live.value = true;
baseStore.fetchLatest().then((l) => { baseStore.fetchLatest().then((l) => {
let b = l; let b = l;
if ( if (
@ -85,7 +108,6 @@ onMounted(() => {
b = baseStore.recents?.at(0) || l; b = baseStore.recents?.at(0) || l;
} }
latest.value = Number(b.block.header.height); latest.value = Number(b.block.header.height);
commits.value.unshift(b.block.last_commit);
const height = Number(b.block.header?.height || 0); const height = Number(b.block.header?.height || 0);
if (height > 50) { if (height > 50) {
// constructs sequence for loading blocks // constructs sequence for loading blocks
@ -94,10 +116,18 @@ onMounted(() => {
promise = promise.then( promise = promise.then(
() => () =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
if (live.value && commits2.value.length < 50) { if (live.value) {
// continue only if the page is living // continue only if the page is living
baseStore.fetchBlock(i).then((x) => { baseStore.fetchBlock(i).then((x) => {
commits.value.unshift(x.block.last_commit); x.block.last_commit?.signatures?.forEach((s) => {
const block = blocks.value[s.validator_address] || [];
block.unshift({
height: x.block.header.height,
color: s.block_id_flag === 'BLOCK_ID_FLAG_COMMIT' ? 'bg-green-500' :
s.block_id_flag === 'BLOCK_ID_FLAG_NIL' ? 'bg-yellow-500' : 'bg-red-500',
});
blocks.value[s.validator_address] = block;
});
resolve(); resolve();
}); });
} }
@ -122,16 +152,6 @@ function updateTotalSigningInfo() {
}); });
} }
const commits2 = computed(() => {
const la = baseStore.recents.map((b) => b.block.last_commit);
// trigger update total signing info
if(la.length > 1 && Number(la.at(la.length-1)?.height|| 0) % 10 === 7) {
updateTotalSigningInfo();
};
const all = [...commits.value, ...la];
return all.length > 50 ? all.slice(all.length - 50) : all;
});
onUnmounted(() => { onUnmounted(() => {
live.value = false; live.value = false;
}); });
@ -185,27 +205,27 @@ function fetchAllKeyRotation() {
<!-- grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-x-4 mt-4 --> <!-- grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-x-4 mt-4 -->
<div :class="tab === '2' ? '' : 'hidden'"> <div :class="tab === '2' ? '' : 'hidden'">
<div class="flex flex-row flex-wrap gap-x-4 mt-4 justify-center"> <div class="flex flex-row flex-wrap gap-x-4 mt-4 justify-center">
<div v-for="({ v, signing, hex }, i) in list" :key="i"> <div v-for="(unit, i) in grid" :key="i">
<div class="flex justify-between py-0 w-[248px]"> <div class="flex justify-between py-0 w-[248px]">
<label class="truncate text-sm"> <label class="truncate text-sm">
<span class="ml-1 text-black dark:text-white" <span class="ml-1 text-black dark:text-white"
>{{ i + 1 }}.{{ v.description.moniker }}</span >{{ i + 1 }}.{{ unit.moniker }}</span
> >
</label> </label>
<div <div
v-if="Number(signing?.missed_blocks_counter || 0) > 10" v-if="Number(unit?.missed_blocks_counter || 0) > 10"
class="badge badge-sm bg-transparent border-0 text-red-500 font-bold" class="badge badge-sm bg-transparent border-0 text-red-500 font-bold"
> >
{{ signing?.missed_blocks_counter }} {{ unit?.missed_blocks_counter }}
</div> </div>
<div <div
v-else v-else
class="badge badge-sm bg-transparent text-green-600 border-0 font-bold" class="badge badge-sm bg-transparent text-green-600 border-0 font-bold"
> >
{{ signing?.missed_blocks_counter }} {{ unit?.missed_blocks_counter }}
</div> </div>
</div> </div>
<UptimeBar :blocks="commits2" :validator="hex" /> <UptimeBar :blocks="unit.blocks" />
</div> </div>
</div> </div>
<div class="mt-5 text-xs flex justify-center gap-2"> <div class="mt-5 text-xs flex justify-center gap-2">
@ -228,53 +248,51 @@ function fetchAllKeyRotation() {
<td>{{ $t('uptime.tombstoned') }}</td> <td>{{ $t('uptime.tombstoned') }}</td>
</tr> </tr>
</thead> </thead>
<tr v-for="({ v, signing, uptime }, i) in list" class="hover"> <tr v-for="(v, i) in grid" class="hover">
<td> <td>
<div class="truncate max-w-sm"> <div class="truncate max-w-sm">
{{ i + 1 }}. {{ v.description.moniker }} {{ i + 1 }}. {{ v.moniker }}
</div> </div>
</td> </td>
<td class="text-right"> <td class="text-right">
<span <span
v-if="signing"
class=""
:class=" :class="
uptime && uptime > 0.95 ? 'text-green-500' : 'text-red-500' v.uptime && v.uptime > 0.95 ? 'text-green-500' : 'text-red-500'
" "
> >
<div <div
class="tooltip" class="tooltip"
:data-tip="`${signing.missed_blocks_counter} missing blocks`" :data-tip="`${v.missed_blocks_counter} missing blocks`"
> >
{{ format.percent(uptime) }} {{ format.percent(v.uptime) }}
</div> </div>
</span> </span>
</td> </td>
<td> <td>
<span v-if="signing && !signing.jailed_until.startsWith('1970')"> <span v-if="v.signing && !v.signing.jailed_until.startsWith('1970')">
<div <div
class="tooltip" class="tooltip"
:data-tip="format.toDay(signing?.jailed_until, 'long')" :data-tip="format.toDay(v.signing.jailed_until, 'long')"
> >
<span>{{ format.toDay(signing?.jailed_until, 'from') }}</span> <span>{{ format.toDay(v.signing.jailed_until, 'from') }}</span>
</div> </div>
</span> </span>
</td> </td>
<td class="text-xs text-right"> <td class="text-xs text-right">
<span <span
v-if="signing && signing.jailed_until.startsWith('1970')" v-if="v.signing.jailed_until.startsWith('1970')"
class="text-right" class="text-right"
>{{ >{{
format.percent( format.percent(
Number(signing.index_offset) / Number(v.signing.index_offset) /
(latest - Number(signing.start_height)) (latest - Number(v.signing.start_height))
) )
}}</span }}</span
> >
{{ signing?.index_offset }} {{ v.signing?.index_offset }}
</td> </td>
<td class="text-right">{{ signing?.start_height }}</td> <td class="text-right">{{ v.signing?.start_height }}</td>
<td class="capitalize">{{ signing?.tombstoned }}</td> <td class="capitalize">{{ v.signing?.tombstoned }}</td>
</tr> </tr>
<tfoot> <tfoot>
<tr> <tr>