Skip to content

Commit 564243b

Browse files
authored
ci, tests, readme
Added annotations, simple tests, readme and gh actions
1 parent 384e8cd commit 564243b

12 files changed

Lines changed: 700 additions & 39 deletions

File tree

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: Setup Tarantool Test Environment
2+
description: 'Sets up Tarantool and tt'
3+
4+
inputs:
5+
tarantool-version:
6+
description: 'Tarantool version to install'
7+
required: true
8+
default: '2.11'
9+
10+
runs:
11+
using: "composite"
12+
steps:
13+
- name: setup tarantool
14+
uses: tarantool/setup-tarantool@v3
15+
with:
16+
tarantool-version: ${{ inputs.tarantool-version }}
17+
- name: add tarantool/modules repo
18+
shell: bash
19+
run: |
20+
os=$(. /etc/os-release && echo $ID)
21+
dist=$(. /etc/os-release && echo $VERSION_CODENAME)
22+
curl -L "https://download.tarantool.org/tarantool/modules/gpgkey" | sudo apt-key add -
23+
apt_source_path="/etc/apt/sources.list.d/tarantool.list"
24+
echo "deb https://download.tarantool.org/tarantool/modules/${os}/ ${dist} main" | sudo tee ${apt_source_path}
25+
- name: install tt
26+
shell: bash
27+
run: sudo apt-get update && sudo apt-get install -y tt
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: Setup Tarantool Test Environment
2+
description: 'Sets up Tarantool and dependencies for testing'
3+
4+
inputs:
5+
tarantool-version:
6+
description: 'Tarantool version to install'
7+
required: true
8+
9+
runs:
10+
using: "composite"
11+
steps:
12+
- name: setup tarantool
13+
uses: ./.github/actions/setup-tarantool
14+
with:
15+
tarantool-version: ${{ inputs.tarantool-version }}
16+
- name: install luatest scm-1
17+
shell: bash
18+
run: tt rocks install luatest 1.1.0

.github/workflows/lint.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: Linting with luacheck
2+
3+
on:
4+
- push
5+
- pull_request
6+
7+
jobs:
8+
run-luacheck-linter:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v4
12+
- uses: ./.github/actions/setup-tarantool
13+
with:
14+
tarantool-version: '2.11'
15+
- name: install tarantool/luacheck and execute it
16+
run: tt rocks install luacheck && .rocks/bin/luacheck .
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: Create and push rockspec for moonlibs/connection
2+
3+
on:
4+
workflow_run:
5+
workflows:
6+
- "Linting with luacheck"
7+
- "Testing with unit tests"
8+
types:
9+
- completed
10+
push:
11+
tags:
12+
- '*'
13+
14+
env:
15+
ROCK_NAME: connection
16+
17+
jobs:
18+
pack-and-push-tagged-rockspec:
19+
runs-on: ubuntu-latest
20+
if: ${{ github.event.workflow_run.conclusion == 'success' }} && startsWith(github.ref, 'refs/tags/') }}
21+
steps:
22+
- uses: actions/checkout@v4
23+
- uses: ./.github/actions/setup-tarantool
24+
25+
# https://stackoverflow.com/questions/58177786/get-the-current-pushed-tag-in-github-actions
26+
- name: Set env
27+
run: echo "TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
28+
29+
- run: tt rocks new_version --tag=${{ env.TAG }} ${{ env.ROCK_NAME }}-dev-1.rockspec ${{ env.TAG }} "git+https://github.com/${{ github.repository }}.git"
30+
- run: tt rocks install ${{ env.ROCK_NAME }}-${{ env.TAG }}-1.rockspec
31+
- run: tt rocks pack ${{ env.ROCK_NAME }}-${{ env.TAG }}-1.rockspec
32+
33+
- uses: unfor19/install-aws-cli-action@v1.0.3
34+
- run:
35+
|
36+
mkdir .build
37+
cp ${{ env.ROCK_NAME }}-dev-1.rockspec ${{ env.ROCK_NAME }}-${{ env.TAG }}-1.rockspec \
38+
.build/
39+
cp *.src.rock .build/
40+
- name: rebuild and publish s3 luarocks server
41+
env:
42+
AWS_ACCESS_KEY_ID: ${{ secrets.MOONLIBS_S3_ACCESS_KEY_ID }}
43+
AWS_SECRET_ACCESS_KEY: ${{ secrets.MOONLIBS_S3_SECRET_KEY}}
44+
AWS_EC2_METADATA_DISABLED: true
45+
run: |
46+
cd .build && \
47+
aws s3 sync s3://moonlibs/ ./ && \
48+
tt rocks admin make_manifest . && \
49+
aws s3 sync --acl public-read ./ s3://moonlibs/;
50+
- uses: "marvinpinto/action-automatic-releases@latest"
51+
with:
52+
repo_token: "${{ secrets.GITHUB_TOKEN }}"
53+
prerelease: false
54+
files: |
55+
README.md
56+
${{env.ROCK_NAME}}-dev-1.rockspec
57+
${{env.ROCK_NAME}}-${{env.TAG}}-1.rockspec
58+
${{env.ROCK_NAME}}-${{env.TAG}}-1.src.rock

.github/workflows/tests.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Testing with unit tests
2+
3+
on:
4+
- push
5+
- pull_request
6+
7+
jobs:
8+
run-unit-tests:
9+
runs-on: ubuntu-22.04
10+
strategy:
11+
matrix:
12+
version: ["1.10.15", "2.10.7", "2.11.2", "3.0.1"]
13+
steps:
14+
- uses: actions/checkout@v4
15+
- uses: ./.github/actions/setup-test
16+
with:
17+
tarantool-version: ${{matrix.version}}
18+
- name: build lib
19+
run: tt rocks make connection-dev-1.rockspec
20+
- name: run tests
21+
run: .rocks/bin/luatest --no-capture -v

.luacheckrc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
std="tarantool"
2+
3+
codes=true
4+
max_line_length=140
5+
include_files = {"connection.lua"}
6+
7+
ignore = {
8+
"212", -- unused argument
9+
"431", -- shadwing upvalue
10+
"432", -- shadowing upvalue argument
11+
"542", -- empty if branch
12+
}

.luacov

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
runreport = false
2+
deletestats = false
3+
4+
exclude = {
5+
"%.rocks/",
6+
"builtin/",
7+
"t/.+%.test",
8+
}
9+
10+
pathcorrect = {
11+
{ "^/source/connection/", "" },
12+
}
13+
14+
coveralls = {
15+
root = "/",
16+
debug = true,
17+
pathcorrect = {
18+
{ "^/home/runner/work/connection/connection/", "" },
19+
{ "^/source/connection", "" },
20+
},
21+
}

.rocks/config-5.1.lua

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
rocks_servers = {
2+
"https://moonlibs.org",
3+
"https://moonlibs.github.io/rocks",
4+
"https://rocks.tarantool.org",
5+
"https://luarocks.org",
6+
}

README.md

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
# Connection - Base Class for TCP Connections
2+
3+
A Lua module providing a foundation for managing TCP socket connections in Tarantool applications. This library offers an object-oriented approach to handling network connections with support for asynchronous operations, automatic reconnection, and customizable event handling.
4+
5+
## Features
6+
7+
- Asynchronous TCP connection management
8+
- Automatic reconnection with configurable timeout
9+
- Buffer management for both reading and writing
10+
- Event-based architecture with callbacks
11+
- Non-blocking I/O
12+
13+
## Installation
14+
15+
### Using Tarantool Rocks
16+
17+
```bash
18+
tt rocks --server https://moonlibs.org install connection
19+
```
20+
21+
### Manual Installation
22+
23+
Clone the repository and install using the rockspec:
24+
25+
```bash
26+
git clone https://github.com/moonlibs/connection
27+
cd connection
28+
tt rocks make
29+
```
30+
31+
## Usage
32+
33+
### Basic Connection
34+
35+
```lua
36+
local connection = require('connection')
37+
38+
-- Create a new connection
39+
local conn = connection.new('example.com', 8080)
40+
41+
-- Wait for connection to be established (if needed)
42+
conn.connwait:get(timeout)
43+
44+
-- Send data
45+
conn:push_write("Hello, server!")
46+
conn:flush()
47+
48+
-- Close connection when done
49+
conn:close()
50+
```
51+
52+
### Connection Options
53+
54+
```lua
55+
local default_options = {
56+
timeout = 1/3, -- Connection and request timeout in seconds
57+
autoconnect = true, -- Connect immediately when created
58+
reconnect = 1/3, -- Reconnect after 1/3 seconds if connection fails
59+
maxbuf = 2 * 1024 * 1024 -- 2MB read buffer size for read
60+
}
61+
62+
local conn = connection.new('example.com', 8080, {
63+
timeout = 1, -- Connection and request timeout in seconds
64+
autoconnect = true, -- Connect immediately when created
65+
reconnect = 0.5, -- Reconnect after 0.5 seconds if connection fails
66+
maxbuf = 2 * 1024 * 1024 -- 2MB read buffer size for read
67+
})
68+
```
69+
70+
### Custom Event Handlers
71+
72+
```lua
73+
local obj = require('obj')
74+
local connection = require('connection')
75+
76+
local MyConnection = obj.class({}, 'MyConnection', connection)
77+
78+
function MyConnection:on_connected()
79+
self:log('I', 'Successfully connected!')
80+
-- Initialize session, authenticate, etc.
81+
end
82+
83+
function MyConnection:on_disconnect(err)
84+
self:log('W', 'Disconnected: %s', err)
85+
-- Clean up resources
86+
end
87+
88+
function MyConnection:on_read(is_last)
89+
-- Process received data
90+
local data = ffi.string(self.rbuf, self.avail)
91+
self:log('D', 'Received data: %s', data)
92+
93+
-- Important: reset buffer position after processing
94+
self.avail = 0
95+
end
96+
97+
-- Create instance of your connection
98+
local conn = MyConnection:new('example.com', 8080)
99+
```
100+
101+
## Connection States
102+
103+
The connection can be in one of the following states:
104+
105+
- `NOTCONNECTED` (0) - Initial state, not connected
106+
- `CONNECTING` (1) - Connection attempt in progress
107+
- `CONNECTED` (2) - Successfully connected
108+
- `RECONNECTING` (3) - Attempting to reconnect after failure
109+
110+
You can check the current state:
111+
112+
```lua
113+
if conn.state == connection.S2S.CONNECTED then
114+
-- Connection is established
115+
end
116+
```
117+
118+
## API Reference
119+
120+
### Methods (all methods never yields)
121+
122+
- `new(host, port, options)` - Create a new connection
123+
- `connect()` - Initiate connection
124+
- `push_write(buf, len)` - Queue data to be sent, reallocates write buffer
125+
- `flush()` - Send queued data
126+
- `close()` - Close the connection, connection can be reused
127+
- `destroy()` - Clean up resources, makes connection unusable
128+
- `fdno()` - Returns file descriptor (or -1)
129+
130+
### Callbacks (executed in separate fiber)
131+
132+
- `on_connected()` - Called when connection is established, executed in separate fiber
133+
- `on_disconnect(err: string)` - Called when connection is closed, executed in separate fiber
134+
- `on_connect_failed(errno_code)` - Called when connection attempt fails, reconnection logic is performed here
135+
- `on_connect_reset(errno_code)` - Called when connection is reset (closed due to an error)
136+
137+
### Reading data
138+
139+
- `on_read(is_last)` - Called when data is available for reading, from rw (read worker) fiber.
140+
- **Important**: `on_read` is called directly from the read worker fiber. If your callback yields (using `fiber.sleep()` or other yielding operations), reading from the socket is stopped until the callback returns.
141+
- If an exception is raised inside `on_read`, the connection will be reset. The connection can be reestablished automatically if the `reconnect` option is set.
142+
143+
### Protected methods
144+
145+
- `_cleanup(errno_code)` - Executes cleanup of all resources, closes socket, cancels fibers, drains buffers
146+
147+
## Example: Echo Client
148+
149+
Here's a simple echo client implementation that demonstrates basic usage:
150+
151+
```lua
152+
local connection = require('connection')
153+
local fiber = require('fiber')
154+
local ffi = require('ffi')
155+
local obj = require('obj')
156+
157+
local EchoClient = obj.class({}, 'EchoClient', connection)
158+
159+
function EchoClient:on_connected()
160+
self:log('I', 'Connected to echo server')
161+
-- Send a message when connected
162+
self:push_write("Hello, Echo Server!\n")
163+
self:flush()
164+
end
165+
166+
function EchoClient:on_read(is_last)
167+
-- Process the received echo response
168+
local data = ffi.string(self.rbuf, self.avail)
169+
self:log('I', 'Received echo: %s', data)
170+
171+
-- Clear the buffer after processing
172+
self.avail = 0
173+
174+
-- Send another message
175+
if not is_last then
176+
-- yielding in on_read callback stops read from socket.
177+
fiber.sleep(1)
178+
self:push_write("Another message!\n")
179+
self:flush()
180+
end
181+
end
182+
183+
function EchoClient:on_disconnect(err)
184+
self:log('W', 'Disconnected from echo server: %s', err)
185+
end
186+
187+
-- Usage example
188+
local function test_echo_client()
189+
local client = EchoClient:new('localhost', 7777)
190+
client.connwait:get(2) -- Wait up to 2 seconds for connection
191+
192+
-- Keep the client running for a while
193+
fiber.sleep(5)
194+
195+
-- Close the connection
196+
client:close()
197+
end
198+
199+
fiber.create(test_echo_client)
200+
```
201+
202+
## Real-World Examples
203+
204+
Some real world examples
205+
206+
[connection-legacy](https://github.com/moonlibs/connection-legacy) repository, which implements a backward compatible API on top of this module.
207+
208+
[connection-scribe](https://github.com/moonlibs/connection-scribe) repository, which implements Scribe protocol
209+
210+
[tarantool1.5-replica](https://github.com/ochaton/migrate/blob/master/migrate/replica.lua) repository, which implements replication protocol of Tarantool 1.5

0 commit comments

Comments
 (0)