Search code examples
tarantooltarantool-cartridge

How can I perform hot reload code in tarantool cartridge?


How can I perform hot reload code in tarantool cartridge without restarting application ?


Solution

  • In order to find the best solution to your problem it is important to understand what you are trying to achieve. There are 2 possible scenarios:

    • You redeploy your app using a package manager and need to reload the code from a filesystem
    • You want to push new code through database API, via network

    The first one can be done by unloading a module and loading it again. All modules, when loaded, place themselves to the 'package.loaded' table. So all you need is to update it:

    package.loaded['mymodule'] = nil
    require('mymodule')
    

    This is a low-level approach that you can generalize: loop over contents of 'package.loaded', unload everything and load it back again. You need to be careful here to not unload the modules that are not present on a filesystem. There is a module that can help you with this: https://github.com/moonlibs/package-reload

    While that module will help you with the basics, there are other things you need to consider. In Lua it is very easy to store function pointers inside global objects. If you reload the function itself, you won't magically update all the places that have the pointer to the old function. For example, let's consider the http server:

    -- in mymodule.lua
    local function handler(req)
        local resp = req:render({text = req.method..' '..req.path })
        resp.headers['x-test-header'] = 'test';
        resp.status = 201
        return resp
    end
    
    -- somewhere else
    router:route({ path = '/test', method = 'GET' }, mymodule.handler)
    

    If you reload mymodule.lua, and not call the router:route again to re-register the handler, HTTP requests will still call the old function.

    In cartridge, you usually register functions in apply_config() or init(). See here for example. In order to re-register the callbacks you need to call init() or apply_config() of your roles again. To get a list of roles, you can use cartridge.roles.get_known_roles() . You need to loop over them and re-init them.

    In order to call the function that reloads the code, you'll either need to connect through the binary protocol, or use admin socket. Admin socket allows you to write a simple shell script for that. You can get the idea by looking at the tarantool_is_up script . It demonstrates the approach that you can adapt for your use-case.

    The second way to achieve this will be to use cartridge-extensions that allows you to push new code through network. It already has some niceties like simplified binding to the public endpoints.