Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f5bd3dd
fix: use sendall() to prevent partial sends and reconnect if conn is …
luisremis Apr 19, 2026
5f1abd5
fix formatting issue
luisremis Apr 19, 2026
3d18bdb
Merge branch 'develop' into fix-send
luisremis Apr 19, 2026
2f405f0
fix formatting issue
luisremis Apr 20, 2026
60b72dd
fix connection issues
ad-claw000 Apr 20, 2026
2028304
fix test mocks
ad-claw000 Apr 20, 2026
d4ff52f
fix formatting
ad-claw000 Apr 20, 2026
5b0ac70
fix: restore conn check and sendall without condition
ad-claw000 Apr 20, 2026
fb71adf
fix test constraints
ad-claw000 Apr 20, 2026
35188c8
fix mock connection refused error
ad-claw000 Apr 20, 2026
4f0c293
fix: restrict session renew to connected state and fix mock exceptions
ad-claw000 Apr 20, 2026
b087053
Fix CI: Add .dockerignore to prevent permission denied on test/apertu…
ad-claw000 Apr 20, 2026
61aacb2
Fix CI: cleanup before checkout
ad-claw000 Apr 20, 2026
c0185cf
Fix CI: Remove docker/ directories from .dockerignore so docker build…
ad-claw000 Apr 20, 2026
f45ef6f
debug: add prints to test_sessionRenew
ad-claw000 Apr 21, 2026
1a33ebb
fix: check session status before querying
ad-claw000 Apr 21, 2026
613cf8a
revert debug prints
ad-claw000 Apr 21, 2026
43e216e
fix: proactively renew session before query
ad-claw000 Apr 21, 2026
82c417c
Address Copilot PR comments for aperturedb-python #651
ad-claw000 Apr 21, 2026
014dd11
fix: correct indentation after replacing _send_msg check
ad-claw000 Apr 21, 2026
e5457ba
fix: update test assertions for connection retry counts
ad-claw000 Apr 21, 2026
b6ae3f6
fix: increase adb timing test threshold to 3.0 seconds to avoid flaky…
ad-claw000 Apr 21, 2026
7cbe641
test: isolate cert volumes and remove strict unbound var check for lo…
ad-claw000 Apr 21, 2026
1dd7863
fix: restore timing threshold, update dockerignore and make Session t…
ad-claw000 Apr 21, 2026
55a9b12
test: ensure db connection is established before monkeypatching to ma…
ad-claw000 Apr 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 156 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# VSCode
.vscode/

#Data files
*.adb.csv
*.jpg
*.npy
test/aperturedb/db*/
test/input/blobs/
docs/examples/
examples/*/coco
examples/*/classification.txt
kaggleds/
examples/*/kaggleds/
docs/*/*.svg
test/aperturedb/log*
adb-python/*
/test/input/
/test/input/images/

.aperturedb
test/data/
test/aperturedb/certificate*/
.devcontainer/aperturedb/
.devcontainer/ca/
test/*_ca/
4 changes: 4 additions & 0 deletions .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ jobs:

steps:

- name: Cleanup previous run
run: docker run --rm -v ${{ github.workspace }}:/workspace alpine sh -c "rm -rf /workspace/test/aperturedb/db*"
continue-on-error: true

- uses: actions/checkout@v3

- name: Login to DockerHub
Expand Down
32 changes: 19 additions & 13 deletions aperturedb/Connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,7 @@ def _send_msg(self, data):

sent_len = struct.pack(MESSAGE_LENGTH_FORMAT,
len(data)) # send size first
x = self.conn.send(sent_len + data)
return x == len(data) + MESSAGE_LENGTH_SIZE
self.conn.sendall(sent_len + data)

def _recv_msg(self):
recv_len = self.conn.recv(MESSAGE_LENGTH_SIZE) # get message size
Expand Down Expand Up @@ -451,7 +450,8 @@ def connect(self, details: str = None):
self._connect()
except socket.error as e:
logger.error(
f"Error connecting to server: {self.config} \r\n{details}. {e=}",
f"Error connecting to server: "
f"{self.config} \r\n{details}. {e=}",
exc_info=True,
stack_info=True)

Expand All @@ -477,18 +477,21 @@ def _query(self, query, blob_array = [], try_resume=True):
# Serialize with protobuf and send
data = query_msg.SerializeToString()

if self.conn is None:
self.connect(details="Initial connect from _query")

# this is for session refresh attempts
tries = 0
while tries < self.config.retry_max_attempts:
try:
if self._send_msg(data):
response = self._recv_msg()
if response is not None:
querRes = queryMessage.queryMessage()
queryMessage.ParseFromString(querRes, response)
response_blob_array = [b for b in querRes.blobs]
self.last_response = json.loads(querRes.json)
break
self._send_msg(data)
response = self._recv_msg()
if response is not None:
querRes = queryMessage.queryMessage()
queryMessage.ParseFromString(querRes, response)
response_blob_array = [b for b in querRes.blobs]
self.last_response = json.loads(querRes.json)
break
except ssl.SSLEOFError as ssle:
# this can happen when working in a notebook.
# we log if this isn't the first try, or if
Expand Down Expand Up @@ -537,7 +540,7 @@ def _query(self, query, blob_array = [], try_resume=True):
# For example aperturedb server is restarted, or network is lost.
# While this is useful bit of code, when executed in a refresh token
# path, this can cause a deadlock. Hence the try_resume flag.
if try_resume:
if try_resume and self.connected:
self._renew_session()
if tries == self.config.retry_max_attempts:
# We have tried enough times, and failed. Log some state info.
Expand Down Expand Up @@ -565,6 +568,7 @@ def query(self, q, blobs=[]):
Returns:
_type_: _description_
"""
self._renew_session()
if self.should_authenticate:
self.authenticate(
shared_data=self.shared_data,
Expand Down Expand Up @@ -602,7 +606,9 @@ def _renew_session(self):
break
except UnauthorizedException as e:
logger.warning(
f"[Attempt {count + 1} of {RENEW_SESSION_MAX_ATTEMPTS}] Failed to refresh token.",
f"[Attempt {count + 1} of "
f"{RENEW_SESSION_MAX_ATTEMPTS}] "
"Failed to refresh token.",
exc_info=True,
stack_info=True)
time.sleep(RENEW_SESSION_RETRY_INTERVAL_SEC)
Expand Down
6 changes: 3 additions & 3 deletions test/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ services:
openssl req -new -key /cert/tls.key -out /ca/http.csr -days 3650 -subj \"/C=US/ST=NY/L=NYC/O=instance/OU=instanceDB/CN=${DB_HTTP_CN:-localhost}\"
openssl x509 -req -CA /ca/ca.crt -CAkey /ca/ca.key -in /ca/http.csr -out /cert/http.crt -passin pass:1234"
volumes:
- ./aperturedb/certificate:/cert
- ./aperturedb/certificate_${RUNNER_NAME}:/cert
- ./${RUNNER_NAME}_ca:/ca

lenz:
Expand All @@ -38,7 +38,7 @@ services:
LNZ_CERTIFICATE_PATH: /etc/lenz/certificate/tcp.crt
LNZ_PRIVATE_KEY_PATH: /etc/lenz/certificate/tls.key
volumes:
- ./aperturedb/certificate:/etc/lenz/certificate
- ./aperturedb/certificate_${RUNNER_NAME}:/etc/lenz/certificate

aperturedb:
image: $ADB_REPO:$ADB_TAG
Expand Down Expand Up @@ -71,7 +71,7 @@ services:
- source: nginx.conf
target: /etc/nginx/conf.d/default.conf
volumes:
- ./aperturedb/certificate:/etc/nginx/certificate
- ./aperturedb/certificate_${RUNNER_NAME}:/etc/nginx/certificate

configs:
nginx.conf:
Expand Down
1 change: 0 additions & 1 deletion test/run_test_container.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#!/bin/bash

set -u
set -e

function check_containers_networks(){
Expand Down
16 changes: 9 additions & 7 deletions test/test_Session.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,7 @@ def mock_connect(host, port):
# Check the exception is not an obscure one.
assert "self.connected=False" in e.args[0]

# Check that we tried to connect 3 times.
assert connect_attempts == 3
assert connect_attempts == 4

def test_socket_send_error_initial(self, monkeypatch):
send_attempts = 0
Expand All @@ -124,7 +123,7 @@ def mock_send(x, buff):
nonlocal send_attempts
send_attempts += 1
raise socket.error("Connection broke when send")
monkeypatch.setattr(socket.socket, "send", mock_send)
monkeypatch.setattr(socket.socket, "sendall", mock_send)

# Create new db connection.
new_db = Connector(
Expand All @@ -141,8 +140,8 @@ def mock_send(x, buff):
# Check the exception is not an obscure one.
assert "self.connected=False" in e.args[0]

# Check that we tried to send 5 (connect hello:2) + query:3) times.
assert send_attempts == 5
# Should be exactly 7 attempts (1 initial + 3 retries, each doing query send + reconnect send)
assert send_attempts == 7

def test_socket_recv_error_initial(self, monkeypatch):
connect_attempts = 0
Expand Down Expand Up @@ -170,11 +169,12 @@ def mock_recv(x, buff):
# Check the exception is not an obscure one.
assert "self.connected=False" in e.args[0]

# Check that we tried to connect 3 times.
assert connect_attempts == 3
assert connect_attempts == 4

def test_con_close_on_send_query(self, db: Connector, monkeypatch):
if not isinstance(db, ConnectorRest):
if db.conn is None:
db.connect()
original_send_msg = db._send_msg
count = 0

Expand Down Expand Up @@ -205,6 +205,8 @@ def mock_send_msg(msg):

def test_con_close_on_recv_query(self, db: Connector, monkeypatch):
if not isinstance(db, ConnectorRest):
if db.conn is None:
db.connect()
original_recv_msg = db._recv_msg
count = 0

Expand Down
Loading