Skip to content

Commit

Permalink
check db file info in inspector-pro (#777)
Browse files Browse the repository at this point in the history
* check db file info in inspector-pro

* fix reset function

* fix notice content

* change threshold to param in config

* refine details

* replace print to logger
  • Loading branch information
ZhaoYangyang0403 authored Aug 23, 2023
1 parent 121b2be commit e52cabe
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 5 deletions.
7 changes: 7 additions & 0 deletions frontend/src/api/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,10 @@ export const exportSnapshotFromEvent = (data) => {
data
})
}

export const eventFileInfo = () => {
return axios({
url: '/api/event/fileinfo',
method: 'GET'
})
}
18 changes: 17 additions & 1 deletion frontend/src/store/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ export default {
selectedEvent: null,
eventDetail: '',
page: null,
eventSearchStr: ''
eventSearchStr: '',
eventFilePath: '',
eventFileSizeThreshold: '',
eventFileSize: '',
eventFileOversized: false
},
mutations: {
setChannelNames (state, channelNames) {
Expand All @@ -37,6 +41,18 @@ export default {
},
setEventSearchStr (state, val) {
state.eventSearchStr = val
},
setEventFilePath (state, val) {
state.eventFilePath = val
},
setEventFileSizeThreshold (state, val) {
state.eventFileSizeThreshold = val
},
setEventFileSize (state, val) {
state.eventFileSize = val
},
setEventFileOversized (state, val) {
state.eventFileOversized = val
}
},
actions: {
Expand Down
53 changes: 52 additions & 1 deletion frontend/src/views/event/EventButtonBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,26 @@
<span>Clear</span>
</v-tooltip>


<v-tooltip right open-delay=500 v-if=this.eventFileOversized>
<template v-slot:activator="{ on, attrs }">
<v-btn icon v-bind="attrs" v-on="on">
<v-icon size="18px" color="warning">mdi-information-outline</v-icon>
</v-btn>
</template>
<span>Database path: {{ eventFilePath }}</span><br>
<span>Database size: {{ eventFileSize }}</span>
<b style="white-space:pre"> Please clear</b><v-icon size="18px" color="accent">mdi-eraser</v-icon><b>as soon as possible.</b>
</v-tooltip>
<v-tooltip right open-delay=500 v-else>
<template v-slot:activator="{ on, attrs }">
<v-btn icon v-bind="attrs" v-on="on">
<v-icon size="18px" color="accent">mdi-information-outline</v-icon>
</v-btn>
</template>
<span>Database path: {{ eventFilePath }}</span><br>
<span>Database size: {{ eventFileSize }}</span>
</v-tooltip>

</div>

<div class="inline">
Expand Down Expand Up @@ -67,13 +86,17 @@
</template>

<script>
import { eventFileInfo } from '@/api'
import Icon from 'vue-svg-icon/Icon.vue'
export default {
name: 'eventButtonBar',
components: {
'svg-icon': Icon
},
activated () {
this.checkEventFileInfo()
},
data () {
return {
isShowClearDialog: false
Expand All @@ -87,9 +110,37 @@ export default {
set (val) {
this.$store.commit('setEventSearchStr', val)
}
},
eventFilePath() {
return this.$store.state.event.eventFilePath
},
eventFileSize() {
return this.$store.state.event.eventFileSize
},
eventFileOversized() {
return this.$store.state.event.eventFileOversized
}
},
methods: {
checkEventFileInfo () {
eventFileInfo()
.then(response => {
let eventFileInfo = response.data.file_info
let eventFilePath = eventFileInfo.path
let eventFileSizeThreshold = eventFileInfo.threshold
let eventFileSize = eventFileInfo.size
let eventFileOversized = eventFileInfo.oversized
if (eventFileOversized) {
this.$bus.$emit('msg.info', `Database size has exceeded ${eventFileSizeThreshold}, please clear it as soon!`)
}
this.$store.commit('setEventFilePath', eventFilePath)
this.$store.commit('setEventFileSizeThreshold', eventFileSizeThreshold)
this.$store.commit('setEventFileSize', eventFileSize)
this.$store.commit('setEventFileOversized', eventFileOversized)
}).catch(error => {
this.$bus.$emit('msg.error', `Get event file info error: ${error.data.message}`)
})
},
clearAllEvents () {
this.isShowClearDialog = false
this.$store.dispatch('clearEvents')
Expand Down
28 changes: 26 additions & 2 deletions lyrebird/db/database_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from pathlib import Path
from lyrebird import application
from lyrebird import log
from lyrebird.utils import JSONFormat
from lyrebird.utils import convert_size, convert_size_to_byte, JSONFormat
from lyrebird.base_server import ThreadServer
from lyrebird.mock import context
from sqlalchemy import event, and_
Expand All @@ -16,6 +16,7 @@
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, String, Integer, Text, DateTime, create_engine, Table, MetaData
from sqlalchemy.orm.attributes import InstrumentedAttribute
from sqlalchemy.exc import OperationalError


"""
Expand Down Expand Up @@ -141,6 +142,9 @@ def event_receiver(self, event, channel=None, event_id=None):
flow = Event(event_id=event_id, channel=channel, content=content, message=message)
self.storage_queue.put(flow)

def start(self):
super().start()

def run(self):
session = self._scoped_session()
while self.running:
Expand All @@ -149,6 +153,10 @@ def run(self):
session.add(event)
session.commit()
context.emit('db_action', 'add event log')
except OperationalError:
logger.error(f'Save event failed. {traceback.format_exc()}')
logger.warning(f'DB would be reset: {self.database_uri}')
self.reset()
except Exception:
logger.error(f'Save event failed. {traceback.format_exc()}')

Expand Down Expand Up @@ -211,12 +219,28 @@ def get_page_count(self, channel_rules, page_size=20, search_str=''):
result = query.count()
self._scoped_session.remove()
return math.ceil(result / page_size)

def get_database_info(self):
database_path = str(self.database_uri)
threshold_str = application._cm.config.get('event.file_size_threshold')
threshold_byte = convert_size_to_byte(threshold_str)
size = self.database_uri.stat().st_size
readable_size = convert_size(size)
oversized = threshold_byte and size > threshold_byte
database_info = {
'path': database_path,
'threshold': threshold_str,
'size': readable_size,
'oversized': oversized
}
return database_info

def reset(self):
self.stop()
self.database_uri.unlink()
self.init_engine()
# TODO After self.stop() could terminate Thread, change `self.running = True` into self.start()
if not self.server_thread.is_alive():
self.start()
self.running = True


Expand Down
3 changes: 2 additions & 1 deletion lyrebird/mock/blueprints/apis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from .menu import Menu
from .notice import Notice
from .checker import Checker
from .event import Event, EventExport, Channel
from .event import Event, EventExport, Channel, EventFileInfo
from .conflict_check import ConflictCheck, ActivatedDataConflictCheck
from .mock_editor import Cut, Copy, Paste, Duplicate
from .qrcode import Qrcode
Expand Down Expand Up @@ -89,3 +89,4 @@ def after_request(response):
api_source.add_resource(EventExport, '/event/export', '/event/export/<string:event_id>')
api_source.add_resource(Channel, '/channel', '/channel/<string:mode>')
api_source.add_resource(StatusBar, '/statusbar', '/statusbar/<string:item_id>')
api_source.add_resource(EventFileInfo, '/event/fileinfo')
9 changes: 9 additions & 0 deletions lyrebird/mock/blueprints/apis/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,12 @@ def post(self, event_id=None):
res.headers['Content-Disposition'] = f'attachment; filename={filename}'
return res


class EventFileInfo(Resource):

def get(self):
db = application.server['db']
file_info = dict()
if db is not None:
file_info = db.get_database_info()
return application.make_ok_response(file_info=file_info)
14 changes: 14 additions & 0 deletions lyrebird/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@ def convert_size(size_bytes):
return "%s %s" % (s, size_name[i])


def convert_size_to_byte(size_str):
size_str = size_str.strip().upper()
match = re.match(r'^(\d+\.?\d*)\s*([KMGTPEZY]?[B])$', size_str)
if not match:
logger.warning(f'Invalid size string: {size_str}')
return
size = float(match.group(1))
unit = match.group(2)
size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
i = size_name.index(unit)
size_bytes = int(size * (1024 ** i))
return size_bytes


def convert_time(duration):
if duration < 1:
return str(round(duration * 1000)) + 'ms'
Expand Down

0 comments on commit e52cabe

Please sign in to comment.