Mort 2022
It’s been over 4 years from my post about mort and I’m still using it :). Today I want to discuss what has changed in mort and some let’s say interesting bugs that I’ve found
Improvement in mort 0.21.5
tengo scripts
This is the biggest feature that I’ve added to mort. It allows users to write their own URL decoder without rebuilding of mort. For scripting language, I’ve used tengo
Let’s see an example
# https://github.com/aldor007/mort/blob/master/configuration/config.yml
buckets:
tengo:
keys:
- accessKey: "acc"
secretAccessKey: "sec"
transform:
kind: "tengo" # new transform kind
tengoPath: 'parse.tengo' # path to tengo script
storages:
basic:
kind: "http"
url: "https://i.imgur.com/<item>"
headers:
"x--key": "sec"
transform:
kind: "local-meta"
rootPath: "/tmp/mort/"
pathPrefix: "transforms"
Mort will read parse.tengo
file and execute it on each request for bucket tengo
.
Content of parse.tengo
file:
fmt := import("fmt")
text := import("text")
parse := func(reqUrl, bucketConfigF, obj) {
// split by "." to remove object extension
elements := text.split_n(reqUrl.path, ".", 2)
ext := elements[1]
if len(elements) == 1 {
return ""
}
// split by "," to find resize parameters
elements = text.split(elements[0], ",")
// url has no transform
if len(elements) == 1 {
return ""
}
// apply parameters
width := 0
height := 0
parent := elements[0] +"." + ext
trans := elements[1:]
for tran in trans {
if tran[0] == 'w' {
width = tran[1:]
}
if tran[0] == 'h' {
height = tran[1:]
}
}
obj.transforms.resize(int(width), int(height), false, false, false)
return parent
}
parent := parse(url, bucketConfig, obj)
Above script will work for URL http://localhost:8084/tengo/udXmD2T,w100,h100.jpeg
More info on how to write your own parse can be found in doc
Redis lock
From now on mort can use a redis server for indicating the generation of the image. Mort instance which will win race for the lock will notify others when a process has ended, so they can read image from cache or storage. Lock has a similar configuration as redis cache. Example:
server:
lock:
type: "redis"
address:
- "localhost:6379"
clientConfig:
Implementation details can be found here
access logs
Small feature but useful for debugging. To enable
server:
accessLogs: true
support for more storages
Mort now supports
- B2
- Azure
- Oracle
- and even sftp storage
More about it here
CI/CD improvements
For CI instead of travis-ci I’m now using Github Actions. Changes:
- tests executed on each PR and after the merge to master (unit and integrations)
- mort uses now conventional commits and semantic release for tagging of versions
- goreleaser for GitHub release with binary
- auto-deploy to my homelab after docker build
Interesting bugs
During development, I’ve discovered some unintended behaviors of mort.
Issue with serving big files
processTimeout
Processing timeout was applied for all requests not only image creation so if mort was streaming response it has to be completed before the end of processTimeout
processTimeout fix |
http.Server timeout
Timeout for http.Server is for whole response so if mort is proxying huge file it has now 2h to end it
processTimeout fix |
Golang http.Server timeout. source: Cloudflare blog post |
More details about HTTP timeouts of golang net package can be found here https://blog.cloudflare.com/the-complete-guide-to-golang-net-http-timeouts/
Idea what to do next
- support for distributed tracing
- redirect to source storage (so mort will not proxy big files)