feat(db): implemented iterator over targets
parent
f8f611f5c5
commit
c9edb55abc
|
@ -7,7 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [Unreleased](https://git.rustybever.be/vieter-v/vieter/src/branch/dev)
|
||||
|
||||
### Added
|
||||
|
||||
* Metrics endpoint for Prometheus integration
|
||||
* Search in list of targets using API & CLI
|
||||
|
||||
## [0.5.0](https://git.rustybever.be/vieter-v/vieter/src/tag/0.5.0)
|
||||
|
||||
|
|
|
@ -1,56 +1,6 @@
|
|||
module db
|
||||
|
||||
import models { Target, TargetArch, TargetFilter }
|
||||
import math
|
||||
|
||||
// get_targets returns all targets in the database.
|
||||
pub fn (db &VieterDb) get_targets(filter TargetFilter) []Target {
|
||||
window_size := 32
|
||||
|
||||
mut out := []Target{}
|
||||
mut targets := []Target{cap: window_size}
|
||||
|
||||
mut offset := 0
|
||||
mut filtered_offset := u64(0)
|
||||
|
||||
for out.len < filter.limit {
|
||||
targets = sql db.conn {
|
||||
select from Target order by id limit window_size offset offset
|
||||
}
|
||||
offset += targets.len
|
||||
|
||||
if targets.len == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
if filter.repo != '' {
|
||||
targets = targets.filter(it.repo == filter.repo)
|
||||
}
|
||||
|
||||
if filter.query != '' {
|
||||
targets = targets.filter(it.url.contains(filter.query) || it.path.contains(filter.query)
|
||||
|| it.branch.contains(filter.query))
|
||||
}
|
||||
|
||||
if filtered_offset > filter.offset {
|
||||
end_index := math.min(filter.limit - u64(out.len), u64(targets.len))
|
||||
|
||||
out << targets[0..end_index]
|
||||
}
|
||||
// We start counting targets in the middle of the current window
|
||||
else if filtered_offset + u64(targets.len) > filter.offset {
|
||||
start_index := filter.offset - filtered_offset
|
||||
end_index := start_index +
|
||||
math.min(filter.limit - u64(out.len), u64(targets.len) - start_index)
|
||||
|
||||
out << targets[start_index..end_index]
|
||||
}
|
||||
|
||||
filtered_offset += u64(targets.len)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// get_target tries to return a specific target.
|
||||
pub fn (db &VieterDb) get_target(target_id int) ?Target {
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
module db
|
||||
|
||||
import models { Target, TargetFilter }
|
||||
import sqlite
|
||||
|
||||
// Iterator providing a filtered view into the list of targets currently stored
|
||||
// in the database. It replaces functionality usually performed in the database
|
||||
// using SQL queries that can't currently be used due to missing stuff in V's
|
||||
// ORM.
|
||||
pub struct TargetsIterator {
|
||||
conn sqlite.DB
|
||||
filter TargetFilter
|
||||
window_size int = 32
|
||||
mut:
|
||||
window []Target
|
||||
window_index u64
|
||||
// Offset in entire list of unfiltered targets
|
||||
offset int
|
||||
// Offset in filtered list of targets
|
||||
filtered_offset u64
|
||||
started bool
|
||||
done bool
|
||||
}
|
||||
|
||||
// targets returns an iterator allowing filtered access to the list of targets.
|
||||
pub fn (db &VieterDb) targets(filter TargetFilter) TargetsIterator {
|
||||
window_size := 32
|
||||
|
||||
return TargetsIterator{
|
||||
conn: db.conn
|
||||
filter: filter
|
||||
window: []Target{cap: window_size}
|
||||
window_size: window_size
|
||||
}
|
||||
}
|
||||
|
||||
// advance_window moves the sliding window over the filtered list of targets
|
||||
// until it either reaches the end of the list of targets, or has encountered a
|
||||
// non-empty window.
|
||||
fn (mut ti TargetsIterator) advance_window() {
|
||||
for {
|
||||
ti.window = sql ti.conn {
|
||||
select from Target order by id limit ti.window_size offset ti.offset
|
||||
}
|
||||
ti.offset += ti.window.len
|
||||
|
||||
if ti.window.len == 0 {
|
||||
ti.done = true
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if ti.filter.repo != '' {
|
||||
ti.window = ti.window.filter(it.repo == ti.filter.repo)
|
||||
}
|
||||
|
||||
if ti.filter.query != '' {
|
||||
ti.window = ti.window.filter(it.url.contains(ti.filter.query)
|
||||
|| it.path.contains(ti.filter.query) || it.branch.contains(ti.filter.query))
|
||||
}
|
||||
|
||||
if ti.window.len > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// next returns the next target, if possible.
|
||||
pub fn (mut ti TargetsIterator) next() ?Target {
|
||||
if ti.done {
|
||||
return none
|
||||
}
|
||||
|
||||
// The first call to `next` will cause the sliding window to move to where the requested offset starts
|
||||
if !ti.started {
|
||||
ti.advance_window()
|
||||
|
||||
// Skip all matched targets until the requested offset
|
||||
for !ti.done && ti.filtered_offset + u64(ti.window.len) <= ti.filter.offset {
|
||||
ti.filtered_offset += u64(ti.window.len)
|
||||
ti.advance_window()
|
||||
}
|
||||
|
||||
if ti.done {
|
||||
return none
|
||||
}
|
||||
|
||||
left_inside_window := ti.filter.offset - ti.filtered_offset
|
||||
ti.window_index = left_inside_window
|
||||
ti.filtered_offset += left_inside_window
|
||||
|
||||
ti.started = true
|
||||
}
|
||||
|
||||
return_value := ti.window[ti.window_index]
|
||||
|
||||
ti.window_index++
|
||||
ti.filtered_offset++
|
||||
|
||||
// Next call will be past the requested offset
|
||||
if ti.filter.limit > 0 && ti.filtered_offset == ti.filter.offset + ti.filter.limit {
|
||||
ti.done = true
|
||||
}
|
||||
|
||||
// Ensure the next call has a new valid window
|
||||
if ti.window_index == u64(ti.window.len) {
|
||||
ti.advance_window()
|
||||
ti.window_index = 0
|
||||
}
|
||||
|
||||
return return_value
|
||||
}
|
||||
|
||||
// collect consumes the entire iterator & returns the result as an array.
|
||||
pub fn (mut ti TargetsIterator) collect() []Target {
|
||||
mut out := []Target{}
|
||||
|
||||
for t in ti {
|
||||
out << t
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
|
@ -11,9 +11,9 @@ fn (mut app App) v1_get_targets() web.Result {
|
|||
filter := models.from_params<TargetFilter>(app.query) or {
|
||||
return app.json(.bad_request, new_response('Invalid query parameters.'))
|
||||
}
|
||||
targets := app.db.get_targets(filter)
|
||||
mut iter := app.db.targets(filter)
|
||||
|
||||
return app.json(.ok, new_data_response(targets))
|
||||
return app.json(.ok, new_data_response(iter.collect()))
|
||||
}
|
||||
|
||||
// v1_get_single_target returns the information for a single target.
|
||||
|
@ -81,6 +81,3 @@ fn (mut app App) v1_patch_target(id int) web.Result {
|
|||
|
||||
return app.json(.ok, new_data_response(target))
|
||||
}
|
||||
|
||||
['/api/v1/targets/search'; auth; get; markused]
|
||||
fn (mut app App) v1_search_targets()
|
||||
|
|
|
@ -31,17 +31,8 @@ pub mut:
|
|||
// init_job_queue populates a fresh job queue with all the targets currently
|
||||
// stored in the database.
|
||||
fn (mut app App) init_job_queue() ! {
|
||||
// Initialize build queues
|
||||
mut targets := app.db.get_targets(limit: 25)
|
||||
mut i := u64(0)
|
||||
|
||||
for targets.len > 0 {
|
||||
for target in targets {
|
||||
app.job_queue.insert_all(target)!
|
||||
}
|
||||
|
||||
i += 25
|
||||
targets = app.db.get_targets(limit: 25, offset: i)
|
||||
for target in app.db.targets(limit: 0) {
|
||||
app.job_queue.insert_all(target)!
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue