{"id":21187593,"url":"https://github.com/pb82/lua-experiments","last_synced_at":"2025-03-14T20:23:07.753Z","repository":{"id":146898048,"uuid":"127663258","full_name":"pb82/lua-experiments","owner":"pb82","description":"some experiments with lua's c api","archived":false,"fork":false,"pushed_at":"2018-05-01T23:41:28.000Z","size":285,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-21T13:08:30.909Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pb82.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-04-01T19:10:41.000Z","updated_at":"2018-05-01T23:41:29.000Z","dependencies_parsed_at":null,"dependency_job_id":"09c53931-5f64-49c6-bbb3-1f272fce3f11","html_url":"https://github.com/pb82/lua-experiments","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pb82%2Flua-experiments","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pb82%2Flua-experiments/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pb82%2Flua-experiments/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pb82%2Flua-experiments/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pb82","download_url":"https://codeload.github.com/pb82/lua-experiments/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243640290,"owners_count":20323635,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-20T18:38:26.243Z","updated_at":"2025-03-14T20:23:07.730Z","avatar_url":"https://github.com/pb82.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Experimental Function-as-a-Service Server\n\nAn Openwhisk like server based on Lua and libuv. Very small memory footprint (binary is ~2.7M, memory usage is usually \u003c 10M). Mongodb is used for persistence.\n\n*All of this is purely experimental / for fun and not at all usable in production*\n\n## How does it work\n\nThe server can run `actions` where every action is a snippet of Lua code. When a new action is added it will be compiled and stored as bytecode in Mongodb.\nWhen an action is run, it's bytecode will be retrieved and the `main` function will be invoked with the submitted parameters. Code execution always happens in a Sandbox.\nThe Sandbox disables dangerous APIs and can enforce memory (in kilobytes) and runtime (in milliseconds) restrictions for every action.\n\nActions also have access to [plugins](https://github.com/pb82/lua-experiments/tree/master/libs) through the `call` API. Plugins are shared libraries that are loaded when the server starts (and they are configured).\nConfiguration is done in Lua which allows for a [very nice config syntax](https://github.com/pb82/lua-experiments/blob/master/config.lua).\n\n## How to interact with the server\n\nThe server exposes a http API (port 8080) that is designed to work with it's [CLI](https://www.npmjs.com/package/@pb82/slcli). You can also use cURL but the CLI makes things easier.\nYou can add, delete and run actions. When running an action you can either wait for the result or run it asynchronously.\n\n## Example\n\nQuick walktrough:\n\n### Install the CLI\n\nWith yarn:\n\n```sh\n$ yarn global add @pb82/slcli\n```\n\nor npm:\n\n```sh\n$ npm install -g @pb82/slcli\n```\n\nThe code for the CLI can be found [here](https://github.com/pb82/lua-experiments/tree/master/cli).\n\n### Run the Server\n\nIt's best to use the provided [docker compose file](https://github.com/pb82/lua-experiments/blob/master/docker-compose.yaml). Copy this file to some location and run `docker-compose up`.\nThis will start two containers, the Mongodb database and the Server itself.\nThat's it, you're ready to go.\n\n### Basic cli commands\n\nFirst you will need to connect to the server:\n\n```sh\n$ slcli connect http://localhost:8080\ninfo: Operation successful\n```\n\nAt this point you should see a `Ping request received` entry in the server logs. You can send another ping with:\n\n```sh\n$ slcli ping\n```\n\nTo get a list of all available actions run:\n\n```sh\n$ slcli list\ninfo: Available actions\n[]\n```\n\nIf this is your first start this should be empty. Now let's add a new action. Create a file named `hello.lua`:\n\n```lua\nfunction main(args)\n\tif args.name == nil then\n\t\targs.name = 'World'\n\tend\n\t\n\treturn 'Hello ' .. args.name\nend\n```\n\nand add it as an action using:\n\n```sh\n$ slcli create hello -f hello.lua \n```\n\nCreate another file named `payload.json`:\n\n```json\n{\n\t\"name\": \"from Lua\"\n}\n```\n\nFinally, run the action:\n\n```\n$ slcli run hello -b -p payload.json\ninfo: Operation successful\nHello from Lua\n```\n\nThe payload is transformed to a Lua table and passed to the `main` function as `args`. The `-b` flag means `block`. Use it to wait until the result is received.\nIf you run the command without the `-b` flag you will get an `invocation ID`:\n\n```sh\n$ slcli run hello -p payload.json\ninfo: Operation successful\n{ InvocationId: \u003cyour ID\u003e }\n```\n\nThis will return immediately and you can use the invocation ID to retrieve the result anytime you want:\n\n```sh\n$ slcli resolve \u003cyour ID\u003e\nHello from Lua\n```\n\nNow try:\n\n```sh\n$ slcli list\n[ { maxmem: 0, name: 'hello', size: 192, timeout: 0 } ]\n```\n\nYou can see the available actions (only hello). `size` is the bytecode size of the action in bytes. There are two other interesting properties:\n\n* `maxmem`: The maximum amount of memory (RAM) the action can use (in kilobytes)\n* `timeout`: The maximum amount of time an action is allowed to take (in milliseconds)\n\nLet's try this. Create another file named `evil.lua`:\n\n```lua\nfunction main(args)\n\tstrings = {}\n\twhile true do\n\t\ttable.insert(strings, 'evil')\n\tend\nend\n```\n\nThis will allocate strings in an endless loop consuming lots of memory (Lua is garbage collected but can't collect the `strings` table here because the reference is still valid.\n\nCreate the action using the `-m` flag which allows us to set a memory limit:\n\n```sh\n$ slcli create evil -f evil.lua -m 100000\n```\n\nIn this case we set 100 Megabytes as the limit. Now run the action and observe the server logs:\n\n```\n$ slcli run evil -b\nerror: Error: Request failed with status code 500\n```\nYou should see the 500 error in the console after a second or two and you should see the following entry in the server logs: `Action 'evil' aborted after violating memory constraints`.\nIf you want to try a timeout too you can use the `-t` flag.\n\n## Calling plugins\n\nRunning isolated Lua code would be pretty useles if it could not interact with external services. This is achieved through plugins (in contrast to Openwhisk which allows you to basically run a whole project with all dependencies and do whatever you want).\nFor this POC only two plugins exist: [skeleton](https://github.com/pb82/lua-experiments/tree/master/libs/skeleton) which is just there to showcase the plugin API.\nAnd [mongo](https://github.com/pb82/lua-experiments/tree/master/libs/mongo) which allows you to talk to a real mongo database. Plugins are configured in [config.lua](https://github.com/pb82/lua-experiments/tree/master/libs/mongo) but if you use docker compose everything is pre-confgured to use the included Mongo instance.\n\nInside your action you can use `call` to talk to interact with plugins. `call` takes three arguments:\n\n```\ncall(pluginName: \u003cstring\u003e, action: \u003cstring\u003e, payload \u003cany\u003e)\n```\n\n`pluginName` should be obvious. `action` can be used to differentiate between intents in the plugin (the mongo plugin accepts `insert` and `query`). Payload can be any value and will be passed to the plugin.\n\nLet's write an action that inserts a person:\n\n```lua\nfunction getAge()\n\treturn math.floor(math.random() * 100)\nend\n\nfunction main(args)\n\tif args.name == nil then\n\t\targs.name = 'noname'\n\tend\n\n\tlocal payload = {\n\t\tcollection = 'mongoplugin',\n\t\tdata = {\n\t\t\tname = args.name,\n\t\t\tage = getAge()\n\t\t}\n\t}\n\n\treturn call('mongo', 'insert', payload)\nend\n```\n\nand create it with `$ slcli create insert -f insert.lua`\n\nRun this a few times:\n\n```sh\n$ slcli run insert -b\n5ae8f10fbb57530001473b36\n```\n\nThe `insert` intent will return the ObjectID of the inserted document. Now let's write an action to query the data:\n\n```lua\nfunction main(args)\n\tlocal payload = {\n\t\tcollection = 'mongoplugin',\n\t\tquery = {\n\t\t\tage = {\n\t\t\t\t['$gt'] = 50\n\t\t\t}\n\t\t}\n\t}\n\n\treturn call('mongo', 'query', payload)\nend\n```\n\nLua tables can be used in a similar way to JSON objects which suits Mongodb very well. Create \u0026 run it for some results:\n\n```sh\n$ slcli create query -f query.lua\n$ slcli run query -b\ninfo: Operation successful\n{ '1': \n   { _id: { '$oid': '5ae8f10fbb57530001473b36' },\n     age: 78,\n     name: 'noname' },\n  '2': \n   { _id: { '$oid': '5ae8f1a2bb57530001473b37' },\n     age: 79,\n     name: 'noname' },\n  '3': \n   { _id: { '$oid': '5ae8f1a3bb57530001473b38' },\n     age: 91,\n     name: 'noname' }\n```\n\nThe CLI has one other feature that has not been covered: `delete` to delete actions (`$ slcli delete \u003caction name\u003e`).\n\n## How fast is it?\n\nThe code is all quick and dirty and completely unoptimized. I did some benchmarking with `wrk` and on my 7 year old Desktop PC i get ~3000 requests per second.\nThis could be improved easily by caching actions (the bytecode is loaded from mongo every time) and fixing some of the locking issues.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpb82%2Flua-experiments","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpb82%2Flua-experiments","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpb82%2Flua-experiments/lists"}