From 4a375f58d05f0451e14062a595a188f8b0d7d19b Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Wed, 27 Mar 2024 15:42:53 +0800 Subject: [PATCH] optimized block loading --- src/modules/[chain]/uptime/index.vue | 230 +++++++++++---------------- 1 file changed, 96 insertions(+), 134 deletions(-) diff --git a/src/modules/[chain]/uptime/index.vue b/src/modules/[chain]/uptime/index.vue index 1dcd0fc0..30c05326 100644 --- a/src/modules/[chain]/uptime/index.vue +++ b/src/modules/[chain]/uptime/index.vue @@ -5,10 +5,10 @@ import { useStakingStore, useBaseStore, useBlockchain, -useFormatter, + useFormatter, } from '@/stores'; import UptimeBar from '@/components/UptimeBar.vue'; -import type { SlashingParam, SigningInfo } from '@/types'; +import type { SlashingParam, SigningInfo, Block } from '@/types'; import { consensusPubkeyToHexAddress, valconsToBase64 } from '@/libs'; const props = defineProps(['chain']); @@ -53,24 +53,24 @@ const validatorSet = computed(() => { }); }); -const blocks = ref({} as Record<string, BlockColor[]>); +const blockColors = ref({} as Record<string, BlockColor[]>); -const grid = computed(() => { +const grid = computed(() => { - const validators = keyword.value.length === 0 ? validatorSet.value : - validatorSet.value.filter((v) => v.moniker.toLowerCase().includes(keyword.value.toLowerCase())); + const validators = keyword.value.length === 0 ? validatorSet.value : + validatorSet.value.filter((v) => v.moniker.toLowerCase().includes(keyword.value.toLowerCase())); const window = Number(slashingParam.value.signed_blocks_window || 0); return validators.map((v) => { const signing = signingInfo.value[v.hex]; const uptime = signing && window > 0 - ? (window - Number(signing.missed_blocks_counter)) / window - : undefined + ? (window - Number(signing.missed_blocks_counter)) / window + : undefined return { moniker: v.moniker, hex: v.hex, base64: v.base64, - blocks: padding(blocks.value[v.base64] || []) , + blocks: padding(blockColors.value[v.base64] || []), uptime, missed_blocks_counter: signing?.missed_blocks_counter || 0, signing @@ -78,77 +78,29 @@ const grid = computed(() => { }) }); +const preload = ref(false); baseStore.$subscribe((_, state) => { const newHeight = Number(state.latest?.block?.header?.height || 0) if (newHeight > latest.value) { latest.value = newHeight; - if(Number(state.latest.block.header.height) % 7 === 0 ) updateTotalSigningInfo(); - validatorSet.value.forEach((v) => { - const sig = state.latest.block.last_commit?.signatures.find((s) => s.validator_address === v.base64) - const block = blocks.value[v.base64] || []; - if (sig){ - block.push({ - height: state.latest.block.header.height, - color: sig.block_id_flag === 'BLOCK_ID_FLAG_COMMIT' ? 'bg-green-500' : 'bg-yellow-500' - }); - } else { - block.push({ - height: state.latest.block.header.height, - color: 'bg-red-500' - }); - } - if (block.length > 50) block.shift(); - blocks.value[v.base64] = block; - }); + // initialize if it's the first time + if(!preload.value) { + preFill(); + preload.value = true; + } + + if (Number(state.latest.block.header.height) % 7 === 0) updateTotalSigningInfo(); + fillblock(state.latest); } }); onMounted(() => { live.value = true; - baseStore.fetchLatest().then((l) => { - let b = l; - if ( - baseStore.recents?.findIndex((x) => x.block_id.hash === l.block_id.hash) > - -1 - ) { - b = baseStore.recents?.at(0) || l; - } - latest.value = Number(b.block.header.height); - const height = Number(b.block.header?.height || 0); - if (height > 50) { - // constructs sequence for loading blocks - let promise = Promise.resolve(); - for (let i = height - 1; i > height - 50; i -= 1) { - promise = promise.then( - () => - new Promise((resolve) => { - if (live.value) { - // continue only if the page is living - baseStore.fetchBlock(i).then((x) => { - validatorSet.value.forEach((v) => { - const sig = x.block.last_commit?.signatures.find((s) => s.validator_address === v.base64) - const block = blocks.value[v.base64] || []; - if (sig){ - block.unshift({ - height: x.block.header.height, - color: sig.block_id_flag === 'BLOCK_ID_FLAG_COMMIT' ? 'bg-green-500' : 'bg-yellow-500' - }); - } else { - block.unshift({ - height: x.block.header.height, - color: 'bg-red-500' - }); - } - blocks.value[v.base64] = block; - }); - resolve(); - }); - } - }) - ); - } - } + + // fill the recent blocks + baseStore.recents?.forEach((b) => { + fillblock(b, 'start'); }); updateTotalSigningInfo(); @@ -158,6 +110,50 @@ onMounted(() => { }); }); +function preFill() { + + if(latest.value > 50 && baseStore.recents.length >= 49 ) return + // preload 50 blocks if recent blocks are not enough + let promise = Promise.resolve(); + for (let i = latest.value - baseStore.recents.length; i > latest.value - 50 && i > 1; i -= 1) { + promise = promise.then(() => + new Promise((resolve) => { + if (live.value) { + // continue only if the page is living + if (i > latest.value - 50) + baseStore.fetchBlock(i).then((x) => { + fillblock(x, 'start'); + resolve(); + }); + } + }) + ); + } +} +function fillblock(b: Block, direction: string = 'end') { + validatorSet.value.forEach((v) => { + const sig = b.block.last_commit?.signatures.find((s) => s.validator_address === v.base64) + const block = blockColors.value[v.base64] || []; + let color = { + height: b.block.header.height, + color: 'bg-red-500' + } + if (sig) { + color = { + height: b.block.header.height, + color: sig.block_id_flag === 'BLOCK_ID_FLAG_COMMIT' ? 'bg-green-500' : 'bg-yellow-500' + } + } + if (direction === 'end') { + block.push(color); + } else { + block.unshift(color); + } + if (block.length > 50) block.shift(); + blockColors.value[v.base64] = block; + }); +} + function updateTotalSigningInfo() { chainStore.rpc.getSlashingSigningInfos().then((x) => { x.info?.forEach((i) => { @@ -184,36 +180,24 @@ function fetchAllKeyRotation() { <template> <div> <div class="tabs tabs-boxed bg-transparent mb-4"> - <a - class="tab text-gray-400 capitalize" - :class="{ 'tab-active': tab === '3' }" - @click="changeTab('3')" - >{{ $t('uptime.overall') }}</a - > - <a - class="tab text-gray-400 capitalize" - :class="{ 'tab-active': tab === '2' }" - @click="changeTab('2')" - >{{ $t('module.blocks') }}</a - > + <a class="tab text-gray-400 capitalize" :class="{ 'tab-active': tab === '3' }" @click="changeTab('3')">{{ + $t('uptime.overall') }}</a> + <a class="tab text-gray-400 capitalize" :class="{ 'tab-active': tab === '2' }" @click="changeTab('2')">{{ + $t('module.blocks') }}</a> <RouterLink :to="`/${chain}/uptime/customize`"> <a class="tab text-gray-400 capitalize">{{ $t('uptime.customize') }}</a> </RouterLink> </div> <div class="bg-base-100 px-5 pt-5"> <div class="flex items-center gap-x-4"> - <input - type="text" - v-model="keyword" - placeholder="Keywords to filter validators" - class="input input-sm w-full flex-1 border border-gray-200 dark:border-gray-600" - /> - <button v-if="chainStore.isConsumerChain" class="btn btn-sm btn-primary" @click="fetchAllKeyRotation">Load Rotated Keys</button> + <input type="text" v-model="keyword" placeholder="Keywords to filter validators" + class="input input-sm w-full flex-1 border border-gray-200 dark:border-gray-600" /> + <button v-if="chainStore.isConsumerChain" class="btn btn-sm btn-primary" @click="fetchAllKeyRotation">Load + Rotated Keys</button> </div> <div v-if="chainStore.isConsumerChain && Object.keys(stakingStore.keyRotation).length === 0" - class="alert alert-warning my-4" - > + class="alert alert-warning my-4"> Note: Please load rotated keys to see the correct uptime </div> <!-- grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-x-4 mt-4 --> @@ -222,20 +206,13 @@ function fetchAllKeyRotation() { <div v-for="(unit, i) in grid" :key="i"> <div class="flex justify-between py-0 w-[248px]"> <label class="truncate text-sm"> - <span class="ml-1 text-black dark:text-white" - >{{ i + 1 }}.{{ unit.moniker }}</span - > + <span class="ml-1 text-black dark:text-white">{{ i + 1 }}.{{ unit.moniker }}</span> </label> - <div - v-if="Number(unit?.missed_blocks_counter || 0) > 10" - class="badge badge-sm bg-transparent border-0 text-red-500 font-bold" - > + <div v-if="Number(unit?.missed_blocks_counter || 0) > 10" + class="badge badge-sm bg-transparent border-0 text-red-500 font-bold"> {{ unit?.missed_blocks_counter }} </div> - <div - v-else - class="badge badge-sm bg-transparent text-green-600 border-0 font-bold" - > + <div v-else class="badge badge-sm bg-transparent text-green-600 border-0 font-bold"> {{ unit?.missed_blocks_counter }} </div> </div> @@ -244,9 +221,9 @@ function fetchAllKeyRotation() { </div> <div class="mt-5 text-xs flex justify-center gap-2"> <span class=" font-bold">{{ $t('uptime.legend') }}: </span> - <span class="bg-green-500"> </span> {{ $t('uptime.committed')}} - <span class="bg-yellow-500"> </span> {{ $t('uptime.precommitted')}} - <span class="bg-red-500"> </span> {{ $t('uptime.missed')}} + <span class="bg-green-500"> </span> {{ $t('uptime.committed') }} + <span class="bg-yellow-500"> </span> {{ $t('uptime.precommitted') }} + <span class="bg-red-500"> </span> {{ $t('uptime.missed') }} </div> </div> @@ -269,40 +246,27 @@ function fetchAllKeyRotation() { </div> </td> <td class="text-right"> - <span - :class=" - v.uptime && v.uptime > 0.95 ? 'text-green-500' : 'text-red-500' - " - > - <div - class="tooltip" - :data-tip="`${v.missed_blocks_counter} missing blocks`" - > + <span :class="v.uptime && v.uptime > 0.95 ? 'text-green-500' : 'text-red-500' + "> + <div class="tooltip" :data-tip="`${v.missed_blocks_counter} missing blocks`"> {{ format.percent(v.uptime) }} </div> </span> </td> <td> <span v-if="v.signing && !v.signing.jailed_until.startsWith('1970')"> - <div - class="tooltip" - :data-tip="format.toDay(v.signing.jailed_until, 'long')" - > + <div class="tooltip" :data-tip="format.toDay(v.signing.jailed_until, 'long')"> <span>{{ format.toDay(v.signing.jailed_until, 'from') }}</span> </div> </span> </td> <td class="text-xs text-right"> - <span - v-if="v.signing && v.signing.jailed_until.startsWith('1970')" - class="text-right" - >{{ - format.percent( - Number(v.signing.index_offset) / - (latest - Number(v.signing.start_height)) - ) - }}</span - > + <span v-if="v.signing && v.signing.jailed_until.startsWith('1970')" class="text-right">{{ + format.percent( + Number(v.signing.index_offset) / + (latest - Number(v.signing.start_height)) + ) + }}</span> {{ v.signing?.index_offset }} </td> <td class="text-right">{{ v.signing?.start_height }}</td> @@ -312,12 +276,10 @@ function fetchAllKeyRotation() { <tr> <td colspan="2" class="text-right"> {{ $t('uptime.minimum_uptime') }}: - <span - class="lowercase tooltip" - :data-tip="`Window size: ${slashingParam.signed_blocks_window}`" - ><span class="ml-2 btn btn-error btn-xs">{{ - format.percent(slashingParam.min_signed_per_window) - }}</span> + <span class="lowercase tooltip" :data-tip="`Window size: ${slashingParam.signed_blocks_window}`"><span + class="ml-2 btn btn-error btn-xs">{{ + format.percent(slashingParam.min_signed_per_window) + }}</span> </span> </td> <td colspan="8"></td>