From deddf29036742f583f3563199ad9f9f48805e940 Mon Sep 17 00:00:00 2001 From: c-cesar Date: Sun, 23 Mar 2025 01:36:12 -0300 Subject: [PATCH 1/3] Add veb. V lang default framework --- frameworks/V/veb/README.md | 93 ++++++++++++++++++++++++++ frameworks/V/veb/benchmark_config.json | 27 ++++++++ frameworks/V/veb/config.toml | 16 +++++ frameworks/V/veb/main.v | 73 ++++++++++++++++++++ frameworks/V/veb/run.sh | 7 ++ frameworks/V/veb/veb.dockerfile | 12 ++++ 6 files changed, 228 insertions(+) create mode 100755 frameworks/V/veb/README.md create mode 100755 frameworks/V/veb/benchmark_config.json create mode 100644 frameworks/V/veb/config.toml create mode 100644 frameworks/V/veb/main.v create mode 100644 frameworks/V/veb/run.sh create mode 100644 frameworks/V/veb/veb.dockerfile diff --git a/frameworks/V/veb/README.md b/frameworks/V/veb/README.md new file mode 100755 index 00000000000..4a2e814703e --- /dev/null +++ b/frameworks/V/veb/README.md @@ -0,0 +1,93 @@ +# Congratulations! + +You have successfully built a new test in the suite! + +There are some remaining tasks to do before you are ready to open a pull request, however. + +## Next Steps + +1. Gather your source code. + +You will need to ensure that your source code is beneath this directory. The most common solution is to include a `src` directory and place your source code there. + +2. Edit `benchmark_config.json` + +You will need alter `benchmark_config.json` to have the appropriate end-points and port specified. + +3. Create `veb.dockerfile` + +This is the dockerfile that is built into a docker image and run when a benchmark test is run. Specifically, this file tells the suite how to build and start your test application. + +You can create multiple implementations and they will all conform to `[name in benchmark_config.json].dockerfile`. For example, the `default` implementation in `benchmark_config.json` will be `veb.dockerfile`, but if you wanted to make another implementation that did only the database tests for MySQL, you could make `veb-mysql.dockerfile` and have an entry in your `benchmark_config.json` for `veb-mysql`. + +4. Test your application + + $ tfb --mode verify --test veb + +This will run the suite in `verify` mode for your test. This means that no benchmarks will be captured and we will test that we can hit your implementation end-points specified by `benchmark_config.json` and that the response is correct. + +Once you are able to successfully run your test through our suite in this way **and** your test passes our validation, you may move on to the next step. + +5. Add your test to `.github/workflows/build.yml` + +Edit `.github/workflows/build.yml` to ensure that Github Actions will automatically run our verification tests against your new test. This file is kept in alphabetical order, so find where `TESTDIR=V/veb` should be inserted under `env > matrix` and put it there. + +6. Fix this `README.md` and open a pull request + +Starting on line 49 is your actual `README.md` that will sit with your test implementation. Update all the dummy values to their correct values so that when people visit your test in our Github repository, they will be greated with information on how your test implementation works and where to look for useful source code. + +After you have the real `README.md` file in place, delete everything above line 59 and you are ready to open a pull request. + +Thanks and Cheers! + + + + + + + +# veb Benchmarking Test + +### Test Type Implementation Source Code + +* [JSON](Relative/Path/To/Your/Source/File) +* [PLAINTEXT](Relative/Path/To/Your/Source/File) +* [DB](Relative/Path/To/Your/Source/File) +* [QUERY](Relative/Path/To/Your/Source/File) +* [CACHED QUERY](Relative/Path/To/Your/Source/File) +* [UPDATE](Relative/Path/To/Your/Source/File) +* [FORTUNES](Relative/Path/To/Your/Source/File) + +## Important Libraries +The tests were run with: +* [Software](https://www.example1.com/) +* [Example](http://www.example2.com/) + +## Test URLs +### JSON + +http://localhost:8080/json + +### PLAINTEXT + +http://localhost:8080/plaintext + +### DB + +http://localhost:8080/db + +### QUERY + +http://localhost:8080/query?queries= + +### CACHED QUERY + +http://localhost:8080/cached_query?queries= + +### UPDATE + +http://localhost:8080/update?queries= + +### FORTUNES + +http://localhost:8080/fortunes diff --git a/frameworks/V/veb/benchmark_config.json b/frameworks/V/veb/benchmark_config.json new file mode 100755 index 00000000000..0ed4d08e9b3 --- /dev/null +++ b/frameworks/V/veb/benchmark_config.json @@ -0,0 +1,27 @@ +{ + "framework": "veb", + "tests": [ + { + "default": { + "json_url": "/json", + "plaintext_url": "/plaintext", + "db_url": "/db", + "port": 8080, + "approach": "Realistic", + "classification": "Fullstack", + "database": "Postgres", + "framework": "veb", + "language": "V", + "flavor": "None", + "orm": "Full", + "platform": "None", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "veb", + "notes": "", + "versus": "None" + } + } + ] +} diff --git a/frameworks/V/veb/config.toml b/frameworks/V/veb/config.toml new file mode 100644 index 00000000000..dc0377bd437 --- /dev/null +++ b/frameworks/V/veb/config.toml @@ -0,0 +1,16 @@ +[framework] +name = "veb" + +[main] +urls.plaintext = "/plaintext" +urls.json = "/json" +urls.db = "/db" +approach = "Realistic" +classification = "Fullstack" +database = "Postgres" +database_os = "Linux" +os = "Linux" +orm = "Full" +platform = "None" +webserver = "None" +versus = "None" diff --git a/frameworks/V/veb/main.v b/frameworks/V/veb/main.v new file mode 100644 index 00000000000..8abb8c654c5 --- /dev/null +++ b/frameworks/V/veb/main.v @@ -0,0 +1,73 @@ +import veb +import time +import rand +import db.pg + +import log + +pub struct Context { + veb.Context +} + +pub struct App { +pub mut: + db pg.DB +} + +pub fn (app &App) plaintext(mut ctx Context) veb.Result { + log.error('plain') + ctx.set_header(.date, time.now().as_utc().custom_format('ddd, DD MMM YYYY HH:MM:ss') + ' GMT') + ctx.set_header(.server, 'veb') + return ctx.text('Hello, World!') +} + +pub fn (app &App) json(mut ctx Context) veb.Result { + log.error('json') + obj := {'message': 'Hello, World!'} + ctx.set_header(.date, time.now().as_utc().custom_format('ddd, DD MMM YYYY HH:MM:ss') + ' GMT') + ctx.set_header(.server, 'veb') + return ctx.json(obj) +} + +struct World { + id int @[primary; sql: serial] + randomnumber int +} + +pub fn (app &App) db(mut ctx Context) veb.Result { + r := rand.int_in_range(1, 10000) or { return ctx.text('rand error') } + result := sql app.db { + select from World where id == r + } or { return ctx.text(app.db.last_id().str()) } + ctx.set_header(.date, time.now().as_utc().custom_format('ddd, DD MMM YYYY HH:MM:ss') + ' GMT') + ctx.set_header(.server, 'veb') + return ctx.json({'id': r, 'randomNumber': result[0].randomnumber}) +} + +fn main() { + mut app := &App{ + db: pg.connect(pg.Config{ + host: 'tfb-database' + port: 5432 + user: 'benchmarkdbuser' + password: 'benchmarkdbpass' + dbname: 'hello_world' + }) ! + //db: pg.connect_with_conninfo('postgresql://benchmarkdbuser:benchmarkdbpass@tfb-database:5432/hello_world?sslmode=disable') ! + } + veb.run[App, Context](mut app, 8080) +} + +/* +pub fn (app &App) queries(mut ctx Context) veb.Result { + return +} + +pub fn (app &App) updates(mut ctx Context) veb.Result { + return +} + +pub fn (app &App) fortunes(mut ctx Context) veb.Result { + return +} +*/ diff --git a/frameworks/V/veb/run.sh b/frameworks/V/veb/run.sh new file mode 100644 index 00000000000..3df59112523 --- /dev/null +++ b/frameworks/V/veb/run.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +for i in $(seq 0 $(nproc --ignore=1)); do + taskset -c $i ./main & +done + +wait diff --git a/frameworks/V/veb/veb.dockerfile b/frameworks/V/veb/veb.dockerfile new file mode 100644 index 00000000000..c8db4791b41 --- /dev/null +++ b/frameworks/V/veb/veb.dockerfile @@ -0,0 +1,12 @@ +FROM thevlang/vlang:debian-dev +RUN apt update && apt install -y libpq-dev + +# Compile veb +WORKDIR /app +COPY ./main.v run.sh ./ +RUN v -prod -cflags '-std=gnu11 -Wall -O3 -march=native -mtune=native -flto' main.v + +# Run veb +EXPOSE 8080 +CMD sh run.sh +#CMD ./main \ No newline at end of file From c5204ed87a59c0bcca0db12b4586906c2c65fd2b Mon Sep 17 00:00:00 2001 From: c-cesar Date: Sun, 23 Mar 2025 01:46:14 -0300 Subject: [PATCH 2/3] Update Readme, config and so --- frameworks/V/veb/README.md | 70 ++--------------------- frameworks/V/veb/benchmark_config.json | 5 +- frameworks/V/veb/config.toml | 5 +- frameworks/V/veb/main.v | 79 ++++++++++++++++++-------- frameworks/V/veb/veb.dockerfile | 5 +- 5 files changed, 71 insertions(+), 93 deletions(-) diff --git a/frameworks/V/veb/README.md b/frameworks/V/veb/README.md index 4a2e814703e..467345c1431 100755 --- a/frameworks/V/veb/README.md +++ b/frameworks/V/veb/README.md @@ -1,67 +1,13 @@ -# Congratulations! +# [veb](https://modules.vlang.io/veb.html) Benchmarking Test -You have successfully built a new test in the suite! - -There are some remaining tasks to do before you are ready to open a pull request, however. - -## Next Steps - -1. Gather your source code. - -You will need to ensure that your source code is beneath this directory. The most common solution is to include a `src` directory and place your source code there. - -2. Edit `benchmark_config.json` - -You will need alter `benchmark_config.json` to have the appropriate end-points and port specified. - -3. Create `veb.dockerfile` - -This is the dockerfile that is built into a docker image and run when a benchmark test is run. Specifically, this file tells the suite how to build and start your test application. - -You can create multiple implementations and they will all conform to `[name in benchmark_config.json].dockerfile`. For example, the `default` implementation in `benchmark_config.json` will be `veb.dockerfile`, but if you wanted to make another implementation that did only the database tests for MySQL, you could make `veb-mysql.dockerfile` and have an entry in your `benchmark_config.json` for `veb-mysql`. - -4. Test your application - - $ tfb --mode verify --test veb - -This will run the suite in `verify` mode for your test. This means that no benchmarks will be captured and we will test that we can hit your implementation end-points specified by `benchmark_config.json` and that the response is correct. - -Once you are able to successfully run your test through our suite in this way **and** your test passes our validation, you may move on to the next step. - -5. Add your test to `.github/workflows/build.yml` - -Edit `.github/workflows/build.yml` to ensure that Github Actions will automatically run our verification tests against your new test. This file is kept in alphabetical order, so find where `TESTDIR=V/veb` should be inserted under `env > matrix` and put it there. - -6. Fix this `README.md` and open a pull request - -Starting on line 49 is your actual `README.md` that will sit with your test implementation. Update all the dummy values to their correct values so that when people visit your test in our Github repository, they will be greated with information on how your test implementation works and where to look for useful source code. - -After you have the real `README.md` file in place, delete everything above line 59 and you are ready to open a pull request. - -Thanks and Cheers! - - - - - - - -# veb Benchmarking Test +Veb is the default V language web framework. This test uses veb and V native ORM. ### Test Type Implementation Source Code -* [JSON](Relative/Path/To/Your/Source/File) -* [PLAINTEXT](Relative/Path/To/Your/Source/File) -* [DB](Relative/Path/To/Your/Source/File) -* [QUERY](Relative/Path/To/Your/Source/File) -* [CACHED QUERY](Relative/Path/To/Your/Source/File) -* [UPDATE](Relative/Path/To/Your/Source/File) -* [FORTUNES](Relative/Path/To/Your/Source/File) +All tests in a 95 lines [main.v] and a html [template](fortunes.html) +### Database -## Important Libraries -The tests were run with: -* [Software](https://www.example1.com/) -* [Example](http://www.example2.com/) +PostgresQL ## Test URLs ### JSON @@ -78,11 +24,7 @@ http://localhost:8080/db ### QUERY -http://localhost:8080/query?queries= - -### CACHED QUERY - -http://localhost:8080/cached_query?queries= +http://localhost:8080/query?q= ### UPDATE diff --git a/frameworks/V/veb/benchmark_config.json b/frameworks/V/veb/benchmark_config.json index 0ed4d08e9b3..e5ca19da42c 100755 --- a/frameworks/V/veb/benchmark_config.json +++ b/frameworks/V/veb/benchmark_config.json @@ -6,6 +6,9 @@ "json_url": "/json", "plaintext_url": "/plaintext", "db_url": "/db", + "query_url": "/queries?q=", + "fortune_url": "/fortunes", + "update_url": "/update?q=", "port": 8080, "approach": "Realistic", "classification": "Fullstack", @@ -13,7 +16,7 @@ "framework": "veb", "language": "V", "flavor": "None", - "orm": "Full", + "orm": "Micro", "platform": "None", "webserver": "None", "os": "Linux", diff --git a/frameworks/V/veb/config.toml b/frameworks/V/veb/config.toml index dc0377bd437..ea461db93e4 100644 --- a/frameworks/V/veb/config.toml +++ b/frameworks/V/veb/config.toml @@ -5,12 +5,15 @@ name = "veb" urls.plaintext = "/plaintext" urls.json = "/json" urls.db = "/db" +urls.query = "/queries?q=" +urls.update = "/update?q=" +urls.fortune = "/fortunes" approach = "Realistic" classification = "Fullstack" database = "Postgres" database_os = "Linux" os = "Linux" -orm = "Full" +orm = "Micro" platform = "None" webserver = "None" versus = "None" diff --git a/frameworks/V/veb/main.v b/frameworks/V/veb/main.v index 8abb8c654c5..63d405e5e83 100644 --- a/frameworks/V/veb/main.v +++ b/frameworks/V/veb/main.v @@ -3,8 +3,6 @@ import time import rand import db.pg -import log - pub struct Context { veb.Context } @@ -15,14 +13,12 @@ pub mut: } pub fn (app &App) plaintext(mut ctx Context) veb.Result { - log.error('plain') - ctx.set_header(.date, time.now().as_utc().custom_format('ddd, DD MMM YYYY HH:MM:ss') + ' GMT') + ctx.set_header(.date, time.now().as_utc().custom_format('ddd, DD MMM YYYY HH:MM:ss') + ' GMT') ctx.set_header(.server, 'veb') return ctx.text('Hello, World!') } pub fn (app &App) json(mut ctx Context) veb.Result { - log.error('json') obj := {'message': 'Hello, World!'} ctx.set_header(.date, time.now().as_utc().custom_format('ddd, DD MMM YYYY HH:MM:ss') + ' GMT') ctx.set_header(.server, 'veb') @@ -31,17 +27,69 @@ pub fn (app &App) json(mut ctx Context) veb.Result { struct World { id int @[primary; sql: serial] +mut: randomnumber int } pub fn (app &App) db(mut ctx Context) veb.Result { r := rand.int_in_range(1, 10000) or { return ctx.text('rand error') } - result := sql app.db { + mut world := sql app.db { select from World where id == r - } or { return ctx.text(app.db.last_id().str()) } + } or { return ctx.text('db error') } + ctx.set_header(.date, time.now().as_utc().custom_format('ddd, DD MMM YYYY HH:MM:ss') + ' GMT') + ctx.set_header(.server, 'veb') + return ctx.json(world.first()) +} + +pub fn (app &App) queries(mut ctx Context) veb.Result { + mut q := ctx.query['q'].int() + if q < 1 { q = 1 } else if q > 500 { q = 500 } + mut world := []World{} + for _ in 0..q { + r := rand.int_in_range(1, 10000) or { return ctx.text('rand error') } + world << sql app.db { + select from World where id == r + } or { return ctx.text('db error') } + } + ctx.set_header(.date, time.now().as_utc().custom_format('ddd, DD MMM YYYY HH:MM:ss') + ' GMT') + ctx.set_header(.server, 'veb') + return ctx.json(world) +} + +pub fn (app &App) update(mut ctx Context) veb.Result { + mut q := ctx.query['q'].int() + if q < 1 { q = 1 } else if q > 500 { q = 500 } + mut world := []World{} + for _ in 0..q { + r := rand.int_in_range(1, 10000) or { return ctx.text('rand error') } + world << sql app.db { + select from World where id == r + } or { return ctx.text('db error') } + world.last().randomnumber = rand.int_in_range(1, 10000) or { return ctx.text('rand error') } + sql app.db { + update World set randomnumber = world.last().randomnumber where id == world.last().id + } or { return ctx.text('db error') } + } + ctx.set_header(.date, time.now().as_utc().custom_format('ddd, DD MMM YYYY HH:MM:ss') + ' GMT') + ctx.set_header(.server, 'veb') + return ctx.json(world) +} + +struct Fortune { + id int @[primary; sql: serial] + message string +} + +pub fn (app &App) fortunes(mut ctx Context) veb.Result { + mut fortunes := sql app.db { + select from Fortune + } or { return ctx.text('db error') } + fortunes.insert(0, Fortune{id: 0, message: 'Additional fortune added at request time.'}) + fortunes.sort(a.message < b.message) ctx.set_header(.date, time.now().as_utc().custom_format('ddd, DD MMM YYYY HH:MM:ss') + ' GMT') ctx.set_header(.server, 'veb') - return ctx.json({'id': r, 'randomNumber': result[0].randomnumber}) + ctx.content_type = 'text/html; charset=utf-8' + return $veb.html() } fn main() { @@ -53,21 +101,6 @@ fn main() { password: 'benchmarkdbpass' dbname: 'hello_world' }) ! - //db: pg.connect_with_conninfo('postgresql://benchmarkdbuser:benchmarkdbpass@tfb-database:5432/hello_world?sslmode=disable') ! } veb.run[App, Context](mut app, 8080) } - -/* -pub fn (app &App) queries(mut ctx Context) veb.Result { - return -} - -pub fn (app &App) updates(mut ctx Context) veb.Result { - return -} - -pub fn (app &App) fortunes(mut ctx Context) veb.Result { - return -} -*/ diff --git a/frameworks/V/veb/veb.dockerfile b/frameworks/V/veb/veb.dockerfile index c8db4791b41..d3313b4515e 100644 --- a/frameworks/V/veb/veb.dockerfile +++ b/frameworks/V/veb/veb.dockerfile @@ -1,12 +1,9 @@ FROM thevlang/vlang:debian-dev RUN apt update && apt install -y libpq-dev -# Compile veb WORKDIR /app -COPY ./main.v run.sh ./ +COPY ./main.v run.sh fortunes.html ./ RUN v -prod -cflags '-std=gnu11 -Wall -O3 -march=native -mtune=native -flto' main.v -# Run veb EXPOSE 8080 CMD sh run.sh -#CMD ./main \ No newline at end of file From 81d1439e83329f8f5302234cc92f11098de49406 Mon Sep 17 00:00:00 2001 From: c-cesar Date: Sun, 23 Mar 2025 04:14:55 -0300 Subject: [PATCH 3/3] set headers with a middleware to reduce code repetition --- frameworks/V/veb/fortunes.html | 12 ++++++++++ frameworks/V/veb/main.v | 43 ++++++++++++++++------------------ frameworks/V/veb/run.sh | 2 +- 3 files changed, 33 insertions(+), 24 deletions(-) create mode 100644 frameworks/V/veb/fortunes.html diff --git a/frameworks/V/veb/fortunes.html b/frameworks/V/veb/fortunes.html new file mode 100644 index 00000000000..51e1d42989a --- /dev/null +++ b/frameworks/V/veb/fortunes.html @@ -0,0 +1,12 @@ + + +Fortunes + + + +@for m in fortunes + +@end +
idmessage
@m.id@m.message
+ + \ No newline at end of file diff --git a/frameworks/V/veb/main.v b/frameworks/V/veb/main.v index 63d405e5e83..1fa0b3e5d0f 100644 --- a/frameworks/V/veb/main.v +++ b/frameworks/V/veb/main.v @@ -3,26 +3,14 @@ import time import rand import db.pg -pub struct Context { - veb.Context -} - -pub struct App { -pub mut: - db pg.DB -} - pub fn (app &App) plaintext(mut ctx Context) veb.Result { - ctx.set_header(.date, time.now().as_utc().custom_format('ddd, DD MMM YYYY HH:MM:ss') + ' GMT') - ctx.set_header(.server, 'veb') - return ctx.text('Hello, World!') + s := 'Hello, World!' + return ctx.text(s) } pub fn (app &App) json(mut ctx Context) veb.Result { obj := {'message': 'Hello, World!'} - ctx.set_header(.date, time.now().as_utc().custom_format('ddd, DD MMM YYYY HH:MM:ss') + ' GMT') - ctx.set_header(.server, 'veb') - return ctx.json(obj) + return ctx.json(obj) } struct World { @@ -36,8 +24,6 @@ pub fn (app &App) db(mut ctx Context) veb.Result { mut world := sql app.db { select from World where id == r } or { return ctx.text('db error') } - ctx.set_header(.date, time.now().as_utc().custom_format('ddd, DD MMM YYYY HH:MM:ss') + ' GMT') - ctx.set_header(.server, 'veb') return ctx.json(world.first()) } @@ -51,8 +37,6 @@ pub fn (app &App) queries(mut ctx Context) veb.Result { select from World where id == r } or { return ctx.text('db error') } } - ctx.set_header(.date, time.now().as_utc().custom_format('ddd, DD MMM YYYY HH:MM:ss') + ' GMT') - ctx.set_header(.server, 'veb') return ctx.json(world) } @@ -70,8 +54,6 @@ pub fn (app &App) update(mut ctx Context) veb.Result { update World set randomnumber = world.last().randomnumber where id == world.last().id } or { return ctx.text('db error') } } - ctx.set_header(.date, time.now().as_utc().custom_format('ddd, DD MMM YYYY HH:MM:ss') + ' GMT') - ctx.set_header(.server, 'veb') return ctx.json(world) } @@ -86,12 +68,26 @@ pub fn (app &App) fortunes(mut ctx Context) veb.Result { } or { return ctx.text('db error') } fortunes.insert(0, Fortune{id: 0, message: 'Additional fortune added at request time.'}) fortunes.sort(a.message < b.message) - ctx.set_header(.date, time.now().as_utc().custom_format('ddd, DD MMM YYYY HH:MM:ss') + ' GMT') - ctx.set_header(.server, 'veb') ctx.content_type = 'text/html; charset=utf-8' return $veb.html() } +pub struct Context { + veb.Context +} + +pub struct App { + veb.Middleware[Context] +pub mut: + db pg.DB +} + +pub fn header(mut ctx Context) bool { + ctx.set_header(.date, time.now().as_utc().custom_format('ddd, DD MMM YYYY HH:MM:ss') + ' GMT') + ctx.set_header(.server, 'veb') + return true +} + fn main() { mut app := &App{ db: pg.connect(pg.Config{ @@ -102,5 +98,6 @@ fn main() { dbname: 'hello_world' }) ! } + app.use(handler: header) veb.run[App, Context](mut app, 8080) } diff --git a/frameworks/V/veb/run.sh b/frameworks/V/veb/run.sh index 3df59112523..5979570b9dc 100644 --- a/frameworks/V/veb/run.sh +++ b/frameworks/V/veb/run.sh @@ -1,6 +1,6 @@ #!/bin/sh -for i in $(seq 0 $(nproc --ignore=1)); do +for i in $(seq 0 $(nproc)); do taskset -c $i ./main & done