Skip to content

Commit

Permalink
Introduce explicity resource management to Driver and Session objects (
Browse files Browse the repository at this point in the history
…#1154)

This is a TC39 [proposal](https://github.com/tc39/proposal-explicit-resource-management) which is already implemented in Typescript 5.2, core-js, babel and other polyfill tools.

This feature enables the user create the driver or a session with the `await using` keywords and then do not have to close the resource afterwards, since this resources will be closed after leaving the block which were created at.

For example:

```typescript
await using driver = neo4j.driver(uri, authToken)
await using session = driver.session()

await session.executeRead(tx => "RETURN 1")
```

Since Deno is more strict with typescript, small fixes had to be done in the driver.

* Add a value to Result[Symbol.toStringTag].
* Fix Integer.shiftRight function to add proper integer conversion it.

A package.json file was added to the `neo4j-driver-deno` folder for making easier to integrate the test runners to the environments.
  • Loading branch information
bigmontz authored Oct 26, 2023
1 parent 91dd8bf commit e191168
Show file tree
Hide file tree
Showing 15 changed files with 102 additions and 9 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@
"scripts": {
"clean": "lerna clean -y && lerna run clean",
"build": "lerna bootstrap --ci",
"set_version::deno": "cd ./packages/neo4j-driver-deno && deno run --allow-read --allow-write ./versioning.ts --output=. --filename=current.version.ts",
"build::deno": "cd ./packages/neo4j-driver-deno && deno run --allow-read --allow-write --allow-net ./generate.ts",
"set_version::deno": "lerna run set_version --scope neo4j-driver-deno --stream -- ",
"build::deno": "lerna run build --scope neo4j-driver-deno --stream -- ",
"build::notci": "lerna bootstrap",
"docs": "lerna run docs --stream --concurrency 1",
"test::unit": "lerna run test::unit --stream",
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,12 @@ class Driver {
return Promise.resolve()
}

// eslint-disable-next-line
// @ts-ignore
[Symbol.asyncDispose] (): Promise<void> {
return this.close()
}

/**
* @protected
* @returns {void}
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/integer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -695,10 +695,11 @@ class Integer {
*/
shiftRight (numBits: number | Integer): Integer {
let bitsCount: number = Integer.toNumber(numBits)
const numBitNum: number = Integer.toNumber(numBits)

if ((bitsCount &= 63) === 0) {
return Integer.ZERO
} else if (numBits < 32) {
} else if (numBitNum < 32) {
return Integer.fromBits(
(this.low >>> bitsCount) | (this.high << (32 - bitsCount)),
this.high >> bitsCount
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ class Result<R extends RecordShape = RecordShape> implements Promise<QueryResult
* @param {function()|null} onfinally - function when the promise finished
* @return {Promise} promise.
*/
[Symbol.toStringTag]: string
[Symbol.toStringTag]: string = 'Result'
finally (onfinally?: (() => void) | null): Promise<QueryResult<R>> {
return this._getOrCreatePromise().finally(onfinally)
}
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,12 @@ class Session {
}
}

// eslint-disable-next-line
// @ts-ignore
[Symbol.asyncDispose] (): Promise<void> {
return this.close()
}

_connectionHolderWithMode (mode: SessionMode): ConnectionHolder {
if (mode === ACCESS_MODE_READ) {
return this._readConnectionHolder
Expand Down
6 changes: 6 additions & 0 deletions packages/neo4j-driver-deno/lib/core/driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,12 @@ class Driver {
return Promise.resolve()
}

// eslint-disable-next-line
// @ts-ignore
[Symbol.asyncDispose] (): Promise<void> {
return this.close()
}

/**
* @protected
* @returns {void}
Expand Down
3 changes: 2 additions & 1 deletion packages/neo4j-driver-deno/lib/core/integer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -695,10 +695,11 @@ class Integer {
*/
shiftRight (numBits: number | Integer): Integer {
let bitsCount: number = Integer.toNumber(numBits)
const numBitNum: number = Integer.toNumber(numBits)

if ((bitsCount &= 63) === 0) {
return Integer.ZERO
} else if (numBits < 32) {
} else if (numBitNum < 32) {
return Integer.fromBits(
(this.low >>> bitsCount) | (this.high << (32 - bitsCount)),
this.high >> bitsCount
Expand Down
2 changes: 1 addition & 1 deletion packages/neo4j-driver-deno/lib/core/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ class Result<R extends RecordShape = RecordShape> implements Promise<QueryResult
* @param {function()|null} onfinally - function when the promise finished
* @return {Promise} promise.
*/
[Symbol.toStringTag]: string
[Symbol.toStringTag]: string = 'Result'
finally (onfinally?: (() => void) | null): Promise<QueryResult<R>> {
return this._getOrCreatePromise().finally(onfinally)
}
Expand Down
6 changes: 6 additions & 0 deletions packages/neo4j-driver-deno/lib/core/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,12 @@ class Session {
}
}

// eslint-disable-next-line
// @ts-ignore
[Symbol.asyncDispose] (): Promise<void> {
return this.close()
}

_connectionHolderWithMode (mode: SessionMode): ConnectionHolder {
if (mode === ACCESS_MODE_READ) {
return this._readConnectionHolder
Expand Down
19 changes: 19 additions & 0 deletions packages/neo4j-driver-deno/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "neo4j-driver-deno",
"version": "5.0.0-dev",
"description": "Package just used for running scripts",
"private": true,
"main": "index.js",
"directories": {
"lib": "lib",
"test": "test"
},
"scripts": {
"test": "npm run test::integration",
"test::integration": "deno test --allow-all ./test",
"set_version": "deno run --allow-read --allow-write ./versioning.ts --output=. --filename=current.version.ts",
"build": "deno run --allow-read --allow-write --allow-net ./generate.ts"
},
"author": "Neo4j",
"license": "Apache-2.0"
}
44 changes: 44 additions & 0 deletions packages/neo4j-driver-deno/test/neo4j.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import neo4j from '../lib/mod.ts'

const env = Deno.env.toObject()

const username = env.TEST_NEO4J_USER || 'neo4j'
const password = env.TEST_NEO4J_PASS || 'password'
const hostname = env.TEST_NEO4J_HOST || 'localhost'
const scheme = env.TEST_NEO4J_SCHEME || 'bolt'
const boltPort = env.TEST_NEO4J_BOLT_PORT || 7687
const uri = `${scheme}://${hostname}:${boltPort}`
const authToken = neo4j.auth.basic(username, password)

// Deno will fail with resource leaks
Deno.test('neo4j.driver should be able to use explicity resource management', async () => {
await using driver = neo4j.driver(uri, authToken)

await driver.executeQuery('RETURN 1')
})

// Deno will fail with resource leaks
Deno.test('driver.session should be able to use explicity resource management', async () => {
await using driver = neo4j.driver(uri, authToken)
await using session = driver.session()

await session.executeRead(tx => "RETURN 1")
})
4 changes: 4 additions & 0 deletions packages/neo4j-driver/src/session-rx.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ export default class RxSession {
})
}

[Symbol.asyncDispose] () {
return this.close()
}

/**
* Returns the bookmarks received following the last successfully completed query, which is executed
* either in an {@link RxTransaction} obtained from this session instance or directly through one of
Expand Down
2 changes: 1 addition & 1 deletion packages/testkit-backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"scripts": {
"build": "rollup src/index.js --config rollup.config.js",
"start": "node --version | grep -q v10. && node -r esm src/index.js || node --experimental-specifier-resolution=node src/index.js",
"start::deno": "deno run --allow-read --allow-write --allow-net --allow-env --allow-run deno/index.ts",
"start::deno": "deno run --allow-read --allow-write --allow-net --allow-env --allow-sys --allow-run deno/index.ts",
"clean": "rm -fr node_modules public/index.js",
"prepare": "npm run build",
"node": "node"
Expand Down
2 changes: 1 addition & 1 deletion testkit/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM ubuntu:20.04

ARG NODE_VERSION=10
ARG DENO_VERSION=1.19.3
ARG DENO_VERSION=1.37.2

ENV DEBIAN_FRONTEND noninteractive
ENV NODE_OPTIONS --max_old_space_size=4096 --use-openssl-ca
Expand Down
2 changes: 1 addition & 1 deletion testkit/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def init_monorepo():
def clean_and_build():
run_in_driver_repo(["npm", "run", "clean"], env=os.environ)
run_in_driver_repo(["npm", "run", "build"], env=os.environ)
run_in_driver_repo(["npm", "run", "build::deno", "--",
run_in_driver_repo(["npm", "run", "build::deno", "--", "--",
"--output=lib2/"], env=os.environ)

if is_deno() and is_team_city():
Expand Down

0 comments on commit e191168

Please sign in to comment.