From 34c3bcd35ec65131462c749906381512cc551f4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Wed, 25 Mar 2020 00:54:39 +0100 Subject: [PATCH 01/16] Travis: Build against 5.2 Drop older 4.x versions, add Xcode 11. --- .travis.yml | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2401c79..3d4bdc2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,22 +7,16 @@ matrix: include: - os: Linux dist: trusty - env: SWIFT_SNAPSHOT_NAME="https://swift.org/builds/swift-4.0.3-release/ubuntu1404/swift-4.0.3-RELEASE/swift-4.0.3-RELEASE-ubuntu14.04.tar.gz" + env: SWIFT_SNAPSHOT_NAME="https://swift.org/builds/swift-4.2-branch/ubuntu1404/swift-4.2-DEVELOPMENT-SNAPSHOT-2018-07-24-a/swift-4.2-DEVELOPMENT-SNAPSHOT-2018-07-24-a-ubuntu14.04.tar.gz" sudo: required - os: Linux - dist: trusty - env: SWIFT_SNAPSHOT_NAME="https://swift.org/builds/swift-4.1.1-release/ubuntu1404/swift-4.1.1-RELEASE/swift-4.1.1-RELEASE-ubuntu14.04.tar.gz" + dist: xenial + env: SWIFT_SNAPSHOT_NAME="https://swift.org/builds/swift-5.2-release/ubuntu1604/swift-5.2-RELEASE/swift-5.2-RELEASE-ubuntu16.04.tar.gz" sudo: required - - os: Linux - dist: trusty - env: SWIFT_SNAPSHOT_NAME="https://swift.org/builds/swift-4.2-branch/ubuntu1404/swift-4.2-DEVELOPMENT-SNAPSHOT-2018-07-24-a/swift-4.2-DEVELOPMENT-SNAPSHOT-2018-07-24-a-ubuntu14.04.tar.gz" - sudo: required - - os: osx - osx_image: xcode9 - - os: osx - osx_image: xcode9.3 - os: osx osx_image: xcode9.4 + - os: osx + osx_image: xcode11 before_install: From de9d43c86e5d770a1bf0b60ec05a77bef08373b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Mon, 29 Jan 2024 14:23:14 +0100 Subject: [PATCH 02/16] Attempt to replace travis w/ github actions ... will it work? --- .github/workflows/swift.yml | 43 +++++++++++++++++++++++++++++++++++++ .travis.yml | 34 ----------------------------- 2 files changed, 43 insertions(+), 34 deletions(-) create mode 100644 .github/workflows/swift.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml new file mode 100644 index 0000000..d05a9fc --- /dev/null +++ b/.github/workflows/swift.yml @@ -0,0 +1,43 @@ +name: Build and Test + +on: + push: + pull_request: + schedule: + - cron: "52 10 * * 1" + +jobs: + linux: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + image: + - swift:4.2.4 + - swift:5.6.1-bionic + - swift:5.9.2-focal + container: ${{ matrix.image }} + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + - name: Build Swift Debug Package + run: swift build -c debug + - name: Build Swift Release Package + run: swift build -c release + - name: Run Tests + run: swift test + nextstep: + runs-on: macos-13 + steps: + - name: Select latest available Xcode + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '15.0.0' + - name: Checkout Repository + uses: actions/checkout@v3 + - name: Build Swift Debug Package + run: swift build -c debug + - name: Build Swift Release Package + run: swift build -c release + - name: Run Tests + run: swift test diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3d4bdc2..0000000 --- a/.travis.yml +++ /dev/null @@ -1,34 +0,0 @@ -language: generic - -notifications: - slack: nozeio:LIFY1Jtkx0FRcLq3u1WliHRZ - -matrix: - include: - - os: Linux - dist: trusty - env: SWIFT_SNAPSHOT_NAME="https://swift.org/builds/swift-4.2-branch/ubuntu1404/swift-4.2-DEVELOPMENT-SNAPSHOT-2018-07-24-a/swift-4.2-DEVELOPMENT-SNAPSHOT-2018-07-24-a-ubuntu14.04.tar.gz" - sudo: required - - os: Linux - dist: xenial - env: SWIFT_SNAPSHOT_NAME="https://swift.org/builds/swift-5.2-release/ubuntu1604/swift-5.2-RELEASE/swift-5.2-RELEASE-ubuntu16.04.tar.gz" - sudo: required - - os: osx - osx_image: xcode9.4 - - os: osx - osx_image: xcode11 - - -before_install: - - ./.travis.d/before-install.sh - -install: - - ./.travis.d/install.sh - -script: - - export PATH="$HOME/usr/bin:$PATH" - - export SWIFTENV_ROOT="$HOME/.swiftenv" - - export PATH="${SWIFTENV_ROOT}/bin:${SWIFTENV_ROOT}/shims:$PATH" - - swift build -c release - - swift build -c debug - From 0d581805aeff0adb32b4278635025fe1e8c95289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Mon, 29 Jan 2024 14:30:58 +0100 Subject: [PATCH 03/16] GHA: Attempt to fix Xcode build ... --- .github/workflows/swift.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index d05a9fc..bc2ae36 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -27,12 +27,12 @@ jobs: - name: Run Tests run: swift test nextstep: - runs-on: macos-13 + runs-on: macos-latest steps: - name: Select latest available Xcode uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '15.0.0' + xcode-version: '~15.0' - name: Checkout Repository uses: actions/checkout@v3 - name: Build Swift Debug Package From e711ec5490c8bc439e56ddc8a44ed59aff0858e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Mon, 29 Jan 2024 14:32:10 +0100 Subject: [PATCH 04/16] No tests in the binary package ... so don't attempt to run them in GHA. --- .github/workflows/swift.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index bc2ae36..3d2cbc2 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -24,8 +24,6 @@ jobs: run: swift build -c debug - name: Build Swift Release Package run: swift build -c release - - name: Run Tests - run: swift test nextstep: runs-on: macos-latest steps: @@ -39,5 +37,3 @@ jobs: run: swift build -c debug - name: Build Swift Release Package run: swift build -c release - - name: Run Tests - run: swift test From 1d74a8b60c4d3978bb8834647d23dc0359f04527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Mon, 29 Jan 2024 22:26:21 +0100 Subject: [PATCH 05/16] Adjust for changes SwiftNIO APIs - whenComplete takes an argument - write(string:) etc are now called writeString(_:) - use new BB `writeRepeatingByte()` - handler.write(ctx:) etc is now write(context:) - newAddressResolving is now makeAddressResolvingHost - .then is now .flatMap - use ByteBuffer init's that don't require an allocator --- .../RedisServer/Commands/ServerCommands.swift | 9 +++-- .../RedisServer/Commands/StringCommands.swift | 12 +++---- .../Server/RedisCommandContext.swift | 11 ++++--- .../Server/RedisCommandHandler.swift | 33 ++++++++++--------- Sources/RedisServer/Server/RedisServer.swift | 19 +++++------ Sources/RedisServer/Values/RedisValue.swift | 7 ++-- .../RedisServer/Values/RedisValueCoding.swift | 25 +++----------- 7 files changed, 47 insertions(+), 69 deletions(-) diff --git a/Sources/RedisServer/Commands/ServerCommands.swift b/Sources/RedisServer/Commands/ServerCommands.swift index 966efcb..40eaa07 100644 --- a/Sources/RedisServer/Commands/ServerCommands.swift +++ b/Sources/RedisServer/Commands/ServerCommands.swift @@ -2,7 +2,7 @@ // // This source file is part of the swift-nio-redis open source project // -// Copyright (c) 2018 ZeeZide GmbH. and the swift-nio-redis project authors +// Copyright (c) 2018-2024 ZeeZide GmbH. and the swift-nio-redis project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -102,7 +102,7 @@ extension Commands { ctx.context.channel.close(mode: .input, promise: nil) ctx.context.writeAndFlush(NIOAny(RESPValue.ok)) - .whenComplete { + .whenComplete { _ in ctx.context.channel.close(promise: nil) } } @@ -167,7 +167,6 @@ extension Commands { // do not block the server listQueue.async { - let nl : [ UInt8 ] = [ 10 ] var count = clients.count guard count > 0 else { return ctx.write("") } // Never @@ -177,8 +176,8 @@ extension Commands { assert(count > 0) count -= 1 - result.write(string: info.redisClientLogLine) - result.write(bytes: nl) + result.writeString(info.redisClientLogLine) + result.writeInteger(10 as UInt8) if count == 0 { ctx.write(.bulkString(result)) diff --git a/Sources/RedisServer/Commands/StringCommands.swift b/Sources/RedisServer/Commands/StringCommands.swift index 8c54ad2..1bfe649 100644 --- a/Sources/RedisServer/Commands/StringCommands.swift +++ b/Sources/RedisServer/Commands/StringCommands.swift @@ -2,7 +2,7 @@ // // This source file is part of the swift-nio-redis open source project // -// Copyright (c) 2018 ZeeZide GmbH. and the swift-nio-redis project authors +// Copyright (c) 2018-2024 ZeeZide GmbH. and the swift-nio-redis project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -25,10 +25,10 @@ extension Commands { { guard var bb = value.byteBuffer else { throw RedisError.wrongType } - let result : Int = try ctx.writeInDatabase { db in - if let oldValue = db[key] { + let result : Int = try ctx.writeInDatabase { ( db : Databases.Database ) in + if let oldValue : RedisValue = db[key] { guard case .string(var s) = oldValue else { throw RedisError.wrongType } - s.write(buffer: &bb) + s.writeBuffer(&bb) db[key] = .string(s) return s.readableBytes } @@ -253,11 +253,11 @@ extension Commands { if index > s.readableBytes { // if index > count, 0-padded!!! let countToWrite = index - s.readableBytes - s.write(bytes: repeatElement(UInt8(0), count: countToWrite)) + s.writeRepeatingByte(0, count: countToWrite) } s.moveWriterIndex(to: s.readerIndex + index) - s.write(buffer: &bb) + s.writeBuffer(&bb) db[key] = .string(s) return s.readableBytes diff --git a/Sources/RedisServer/Server/RedisCommandContext.swift b/Sources/RedisServer/Server/RedisCommandContext.swift index 1e95f92..98f03b1 100644 --- a/Sources/RedisServer/Server/RedisCommandContext.swift +++ b/Sources/RedisServer/Server/RedisCommandContext.swift @@ -2,7 +2,7 @@ // // This source file is part of the swift-nio-redis open source project // -// Copyright (c) 2018 ZeeZide GmbH. and the swift-nio-redis project authors +// Copyright (c) 2018-2024 ZeeZide GmbH. and the swift-nio-redis project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -118,12 +118,13 @@ public struct RedisCommandContext { let handler = self.handler if eventLoop.inEventLoop { - handler.write(ctx: context, value: value.toRESPValue(), promise: nil) + handler.write(context: context, value: value.toRESPValue(), promise: nil) if flush { context.channel.flush() } } else { eventLoop.execute { - handler.write(ctx: context, value: value.toRESPValue(), promise: nil) + handler.write(context: context, value: value.toRESPValue(), + promise: nil) if flush { context.channel.flush() } } } @@ -134,12 +135,12 @@ public struct RedisCommandContext { let handler = self.handler if eventLoop.inEventLoop { - handler.write(ctx: context, value: value, promise: nil) + handler.write(context: context, value: value, promise: nil) if flush { context.channel.flush() } } else { eventLoop.execute { - handler.write(ctx: context, value: value, promise: nil) + handler.write(context: context, value: value, promise: nil) if flush { context.channel.flush() } } } diff --git a/Sources/RedisServer/Server/RedisCommandHandler.swift b/Sources/RedisServer/Server/RedisCommandHandler.swift index 453c13f..5d85d1e 100644 --- a/Sources/RedisServer/Server/RedisCommandHandler.swift +++ b/Sources/RedisServer/Server/RedisCommandHandler.swift @@ -2,7 +2,7 @@ // // This source file is part of the swift-nio-redis open source project // -// Copyright (c) 2018 ZeeZide GmbH. and the swift-nio-redis project authors +// Copyright (c) 2018-2024 ZeeZide GmbH. and the swift-nio-redis project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -65,15 +65,15 @@ final class RedisCommandHandler : RESPChannelHandler { // MARK: - Channel Activation - override public func channelActive(ctx: ChannelHandlerContext) { - eventLoop = ctx.eventLoop - remoteAddress = ctx.remoteAddress - channel = ctx.channel + override public func channelActive(context: ChannelHandlerContext) { + eventLoop = context.eventLoop + remoteAddress = context.remoteAddress + channel = context.channel - super.channelActive(ctx: ctx) + super.channelActive(context: context) } - override public func channelInactive(ctx: ChannelHandlerContext) { + override public func channelInactive(context: ChannelHandlerContext) { if let channels = subscribedChannels, !channels.isEmpty { subscribedChannels = nil @@ -93,7 +93,7 @@ final class RedisCommandHandler : RESPChannelHandler { } } - super.channelInactive(ctx: ctx) + super.channelInactive(context: context) server.Q.async { self.server._unregisterClient(self) @@ -116,7 +116,8 @@ final class RedisCommandHandler : RESPChannelHandler { // MARK: - Reading - override public func channelRead(ctx: ChannelHandlerContext, value: RESPValue) + override public func channelRead(context: ChannelHandlerContext, + value: RESPValue) { lastActivity = Date() do { @@ -137,26 +138,26 @@ final class RedisCommandHandler : RESPChannelHandler { let cmdctx = RedisCommandContext(command : command, handler : self, - context : ctx, + context : context, databases : dbs) try callCommand(command, with: args, in: cmdctx) } catch let error as RESPError { - self.write(ctx: ctx, value: error.toRESPValue(), promise: nil) + self.write(context: context, value: error.toRESPValue(), promise: nil) } catch let error as RESPEncodable { - self.write(ctx: ctx, value: error.toRESPValue(), promise: nil) + self.write(context: context, value: error.toRESPValue(), promise: nil) } catch { let respError = RESPError(message: "\(error)") - self.write(ctx: ctx, value: respError.toRESPValue(), promise: nil) + self.write(context: context, value: respError.toRESPValue(), promise: nil) } } - override public func errorCaught(ctx: ChannelHandlerContext, error: Error) { - super.errorCaught(ctx: ctx, error: error) + override public func errorCaught(context: ChannelHandlerContext, error: Error) { + super.errorCaught(context: context, error: error) server.logger.error("Channel", error) - ctx.close(promise: nil) + context.close(promise: nil) } diff --git a/Sources/RedisServer/Server/RedisServer.swift b/Sources/RedisServer/Server/RedisServer.swift index e37187a..980dda4 100644 --- a/Sources/RedisServer/Server/RedisServer.swift +++ b/Sources/RedisServer/Server/RedisServer.swift @@ -2,7 +2,7 @@ // // This source file is part of the swift-nio-redis open source project // -// Copyright (c) 2018 ZeeZide GmbH. and the swift-nio-redis project authors +// Copyright (c) 2018-2024 ZeeZide GmbH. and the swift-nio-redis project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -135,7 +135,7 @@ open class RedisServer { if let host = configuration.host { address = try SocketAddress - .newAddressResolving(host: host, port: configuration.port) + .makeAddressResolvingHost(host, port: configuration.port) } else { var addr = sockaddr_in() @@ -217,9 +217,7 @@ open class RedisServer { let logPacket : RESPValue = { let logStr = info.redisClientLogLine - - var bb = ByteBufferAllocator().buffer(capacity: logStr.utf8.count + 1) - bb.write(string: logStr) + let bb = ByteBuffer(string: logStr) return RESPValue.simpleString(bb) }() @@ -248,9 +246,9 @@ open class RedisServer { // Set the handlers that are applied to the accepted Channels .childChannelInitializer { channel in channel.pipeline - .add(name: "com.apple.nio.backpressure", - handler: BackPressureHandler()) // Oh well :-) - .then { + .addHandler(BackPressureHandler() /* Oh well :-) */, + name: "com.apple.nio.backpressure") + .flatMap { let cid = clientID.add(1) let handler = RedisCommandHandler(id: cid, server: self) @@ -258,9 +256,8 @@ open class RedisServer { self._registerClient(handler) } - return channel.pipeline.add(name: - "de.zeezide.nio.redis.server.client", - handler: handler) + return channel.pipeline + .addHandler(handler, name:"de.zeezide.nio.redis.server.client") } } diff --git a/Sources/RedisServer/Values/RedisValue.swift b/Sources/RedisServer/Values/RedisValue.swift index 60eaf49..258a794 100644 --- a/Sources/RedisServer/Values/RedisValue.swift +++ b/Sources/RedisServer/Values/RedisValue.swift @@ -2,7 +2,7 @@ // // This source file is part of the swift-nio-redis open source project // -// Copyright (c) 2018 ZeeZide GmbH. and the swift-nio-redis project authors +// Copyright (c) 2018-2024 ZeeZide GmbH. and the swift-nio-redis project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -219,10 +219,7 @@ extension Collection where Element == RESPValue { extension ByteBuffer { static func makeFromIntAsString(_ value: Int) -> ByteBuffer { - let utf8 = String(value).utf8 - var buffer = sharedAllocator.buffer(capacity: utf8.count) - buffer.write(bytes: utf8) - return buffer + return ByteBuffer(string: String(value)) } var stringAsInteger: Int? { guard readableBytes > 0 else { return nil } diff --git a/Sources/RedisServer/Values/RedisValueCoding.swift b/Sources/RedisServer/Values/RedisValueCoding.swift index 0c02e81..efffd36 100644 --- a/Sources/RedisServer/Values/RedisValueCoding.swift +++ b/Sources/RedisServer/Values/RedisValueCoding.swift @@ -2,7 +2,7 @@ // // This source file is part of the swift-nio-redis open source project // -// Copyright (c) 2018 ZeeZide GmbH. and the swift-nio-redis project authors +// Copyright (c) 2018-2024 ZeeZide GmbH. and the swift-nio-redis project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -17,50 +17,33 @@ import struct NIO.ByteBuffer import struct NIO.ByteBufferAllocator import NIOFoundationCompat -extension ByteBuffer : Encodable { - - public func encode(to encoder: Encoder) throws { - let data = getData(at: readerIndex, length: readableBytes)! - var container = encoder.singleValueContainer() - try container.encode(data) - } -} - extension KeyedDecodingContainer { func decodeByteBuffer(forKey key: Key) throws -> ByteBuffer { let data = try decode(Data.self, forKey: key) - var bb = ByteBufferAllocator().buffer(capacity: data.count + 1) - bb.write(bytes: data) - return bb + return ByteBuffer(data: data) } func decodeByteBufferArray(forKey key: Key) throws -> ContiguousArray { let datas = try decode(Array.self, forKey: key) - let alloc = ByteBufferAllocator() var buffers = ContiguousArray() buffers.reserveCapacity(datas.count + 1) for data in datas { - var bb = alloc.buffer(capacity: data.count + 1) - bb.write(bytes: data) - buffers.append(bb) + buffers.append(ByteBuffer(data: data)) } return buffers } func decodeByteBufferHash(forKey key: Key) throws -> [ Data : ByteBuffer ] { let datas = try decode(Dictionary.self, forKey: key) - let alloc = ByteBufferAllocator() var buffers = [ Data : ByteBuffer ]() buffers.reserveCapacity(datas.count + 1) for ( key, data ) in datas { - var bb = alloc.buffer(capacity: data.count + 1) - bb.write(bytes: data) - buffers[key] = bb + buffers[key] = ByteBuffer(data: data) } return buffers } From 7741a214abacdd4535570973f03b5af7489b0eec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Mon, 29 Jan 2024 22:29:33 +0100 Subject: [PATCH 06/16] Fix duplicate public warning In extension. --- Sources/RedisServer/Helpers/RedisLogger.swift | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/Sources/RedisServer/Helpers/RedisLogger.swift b/Sources/RedisServer/Helpers/RedisLogger.swift index dd17e88..6be2006 100644 --- a/Sources/RedisServer/Helpers/RedisLogger.swift +++ b/Sources/RedisServer/Helpers/RedisLogger.swift @@ -2,7 +2,7 @@ // // This source file is part of the swift-nio-redis open source project // -// Copyright (c) 2018 ZeeZide GmbH. and the swift-nio-redis project authors +// Copyright (c) 2018-2024 ZeeZide GmbH. and the swift-nio-redis project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -22,22 +22,26 @@ public protocol RedisLogger { public extension RedisLogger { - public func error(_ msg: @autoclosure () -> String, _ values: Any?...) { + @inlinable + func error(_ msg: @autoclosure () -> String, _ values: Any?...) { primaryLog(.Error, msg, values) } - public func warn (_ msg: @autoclosure () -> String, _ values: Any?...) { + @inlinable + func warn (_ msg: @autoclosure () -> String, _ values: Any?...) { primaryLog(.Warn, msg, values) } - public func log (_ msg: @autoclosure () -> String, _ values: Any?...) { + @inlinable + func log (_ msg: @autoclosure () -> String, _ values: Any?...) { primaryLog(.Log, msg, values) } - public func info (_ msg: @autoclosure () -> String, _ values: Any?...) { + @inlinable + func info (_ msg: @autoclosure () -> String, _ values: Any?...) { primaryLog(.Info, msg, values) } - public func trace(_ msg: @autoclosure () -> String, _ values: Any?...) { + @inlinable + func trace(_ msg: @autoclosure () -> String, _ values: Any?...) { primaryLog(.Trace, msg, values) } - } public enum RedisLogLevel : Int8 { @@ -86,10 +90,13 @@ fileprivate let redisLogDateFmt : DateFormatter = { return formatter }() +private let pid = getpid() + public struct RedisPrintLogger : RedisLogger { public let logLevel : LogLevel + @inlinable public init(logLevel: LogLevel = .Log) { self.logLevel = logLevel } @@ -100,7 +107,6 @@ public struct RedisPrintLogger : RedisLogger { { guard logLevel.rawValue <= self.logLevel.rawValue else { return } - let pid = getpid() let now = Date() let prefix = From cec9a71df40cbf459feacf37b2d5ea3ea2f04ee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Mon, 29 Jan 2024 22:30:05 +0100 Subject: [PATCH 07/16] `index(where:)` => `firstIndex(where:)` ... --- Sources/RedisServer/Server/PubSub.swift | 6 ++++-- Sources/redi-s/main.swift | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Sources/RedisServer/Server/PubSub.swift b/Sources/RedisServer/Server/PubSub.swift index 8fb639d..576f4f9 100644 --- a/Sources/RedisServer/Server/PubSub.swift +++ b/Sources/RedisServer/Server/PubSub.swift @@ -2,7 +2,7 @@ // // This source file is part of the swift-nio-redis open source project // -// Copyright (c) 2018 ZeeZide GmbH. and the swift-nio-redis project authors +// Copyright (c) 2018-2024 ZeeZide GmbH. and the swift-nio-redis project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -138,7 +138,9 @@ class PubSub { guard var loopToSubscribers = registry[key] else { return } guard var subscribers = loopToSubscribers[loopID] else { return } - guard let idx = subscribers.index(where: { $0 === handler }) else { return } + guard let idx = subscribers.firstIndex(where: { $0 === handler }) else { + return + } subscribers.remove(at: idx) if subscribers.isEmpty { diff --git a/Sources/redi-s/main.swift b/Sources/redi-s/main.swift index 2b9dc8c..7d87554 100644 --- a/Sources/redi-s/main.swift +++ b/Sources/redi-s/main.swift @@ -2,7 +2,7 @@ // // This source file is part of the swift-nio-redis open source project // -// Copyright (c) 2018 ZeeZide GmbH. and the swift-nio-redis project authors +// Copyright (c) 2018-2024 ZeeZide GmbH. and the swift-nio-redis project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -34,7 +34,9 @@ if args.contains("--help") || args.contains("-h") { } let cmdLinePort : Int? = { - guard let idx = args.index(where: { [ "-p", "--port" ].contains($0) }) else { + guard let idx = + args.firstIndex(where: { [ "-p", "--port" ].contains($0) }) else + { return nil } guard (idx + 1) < args.endIndex, let port = UInt16(args[idx + 1]) else { From a6bca9e4c747e17a16b2e359f144ed74a9f41405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Mon, 29 Jan 2024 22:30:26 +0100 Subject: [PATCH 08/16] Bump nio-redis version, and Package.swift to 5.0 ... --- .gitignore | 1 + Package.swift | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 1a41dca..562b343 100644 --- a/.gitignore +++ b/.gitignore @@ -70,4 +70,5 @@ fastlane/test_output .build-linux* Package.resolved dump*.json +.swiftpm diff --git a/Package.swift b/Package.swift index 0372e0b..0dd34c2 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:4.0 +// swift-tools-version:5.0 import PackageDescription @@ -10,7 +10,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/SwiftNIOExtras/swift-nio-redis.git", - from: "0.9.0") + from: "0.10.3") ], targets: [ .target(name: "RedisServer", dependencies: [ "NIORedis" ]), From 47ae0ce3defad3228365ab199cfb4519ddabd5da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Mon, 29 Jan 2024 22:33:56 +0100 Subject: [PATCH 09/16] GHA: Now requires Swift 5, drop 4.2 Just in time for Swift 6 ... --- .github/workflows/swift.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 3d2cbc2..6bf0cf8 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -13,7 +13,6 @@ jobs: fail-fast: false matrix: image: - - swift:4.2.4 - swift:5.6.1-bionic - swift:5.9.2-focal container: ${{ matrix.image }} From 384817e0d8c57687bd4aac96a624d865e959b7c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Mon, 29 Jan 2024 22:35:21 +0100 Subject: [PATCH 10/16] GHA: Run on macOS-13, not latest ... require for Xcode 15. --- .github/workflows/swift.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 6bf0cf8..00aff72 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -24,7 +24,7 @@ jobs: - name: Build Swift Release Package run: swift build -c release nextstep: - runs-on: macos-latest + runs-on: macos-13 steps: - name: Select latest available Xcode uses: maxim-lobanov/setup-xcode@v1 From 8348f2f249f89a4a4b46df147078f7ad637410ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Mon, 29 Jan 2024 22:48:59 +0100 Subject: [PATCH 11/16] Use swift-atomics No idea whether the changes are proper, feedback would be nice. --- Package.swift | 5 +++-- .../RedisServer/Commands/ServerCommands.swift | 8 +++++--- .../RedisServer/Server/RedisCommandHandler.swift | 6 +++--- Sources/RedisServer/Server/RedisServer.swift | 16 +++++++++------- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/Package.swift b/Package.swift index 0dd34c2..10b1eae 100644 --- a/Package.swift +++ b/Package.swift @@ -10,10 +10,11 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/SwiftNIOExtras/swift-nio-redis.git", - from: "0.10.3") + from: "0.10.3"), + .package(url: "https://github.com/apple/swift-atomics", from: "1.2.0") ], targets: [ - .target(name: "RedisServer", dependencies: [ "NIORedis" ]), + .target(name: "RedisServer", dependencies: [ "NIORedis", "Atomics" ]), .target(name: "redi-s", dependencies: [ "RedisServer" ]) ] ) diff --git a/Sources/RedisServer/Commands/ServerCommands.swift b/Sources/RedisServer/Commands/ServerCommands.swift index 40eaa07..8bca358 100644 --- a/Sources/RedisServer/Commands/ServerCommands.swift +++ b/Sources/RedisServer/Commands/ServerCommands.swift @@ -91,10 +91,12 @@ extension Commands { static func MONITOR(_ ctx: CommandContext) throws { let client = ctx.handler - guard !client.isMonitoring.load() else { return ctx.write(RESPValue.ok) } + guard !client.isMonitoring.load(ordering: .relaxed) else { + return ctx.write(RESPValue.ok) + } - client.isMonitoring.store(true) - _ = client.server.monitors.add(1) + client.isMonitoring.store(true, ordering: .relaxed) + client.server.monitors.wrappingIncrement(ordering: .relaxed) ctx.write(RESPValue.ok) } diff --git a/Sources/RedisServer/Server/RedisCommandHandler.swift b/Sources/RedisServer/Server/RedisCommandHandler.swift index 5d85d1e..d1b5078 100644 --- a/Sources/RedisServer/Server/RedisCommandHandler.swift +++ b/Sources/RedisServer/Server/RedisCommandHandler.swift @@ -14,7 +14,7 @@ import NIO import NIORedis -import class NIOConcurrencyHelpers.Atomic +import class Atomics.ManagedAtomic import struct Foundation.Data import struct Foundation.Date import struct Foundation.TimeInterval @@ -50,7 +50,7 @@ final class RedisCommandHandler : RESPChannelHandler { var lastCommand : String? var name : String? var databaseIndex = 0 - var isMonitoring = Atomic(value: false) + var isMonitoring = ManagedAtomic(false) var subscribedChannels : Set? var subscribedPatterns : Set? @@ -123,7 +123,7 @@ final class RedisCommandHandler : RESPChannelHandler { do { let ( command, args ) = try parseCommandCall(value) - if server.monitors.load() > 0 { + if server.monitors.load(ordering: .relaxed) > 0 { let info = MonitorInfo(db: databaseIndex, addr: remoteAddress, call: value) server.notifyMonitors(info: info) diff --git a/Sources/RedisServer/Server/RedisServer.swift b/Sources/RedisServer/Server/RedisServer.swift index 980dda4..43d3494 100644 --- a/Sources/RedisServer/Server/RedisServer.swift +++ b/Sources/RedisServer/Server/RedisServer.swift @@ -17,7 +17,7 @@ import struct Foundation.URL import struct Foundation.TimeInterval import class Foundation.FileManager import class Foundation.JSONDecoder -import class NIOConcurrencyHelpers.Atomic +import class Atomics.ManagedAtomic import enum NIORedis.RESPValue import NIO @@ -66,9 +66,9 @@ open class RedisServer { var databases : Databases? let Q = DispatchQueue(label: "de.zeezide.nio.redisd.clients") - let clientID = Atomic(value: 0) + let clientID = ManagedAtomic(0) var clients = [ ObjectIdentifier : RedisCommandHandler ]() - var monitors = Atomic(value: 0) + var monitors = ManagedAtomic(0) let pubSub : PubSub public init(configuration: Configuration = Configuration()) { @@ -206,7 +206,9 @@ open class RedisServer { func _unregisterClient(_ client: RedisCommandHandler) { // Q! let oid = ObjectIdentifier(client) clients.removeValue(forKey: oid) - if client.isMonitoring.load() { _ = monitors.add(-1) } + if client.isMonitoring.load(ordering: .relaxed) { + monitors.wrappingDecrement(ordering: .relaxed) + } } @@ -223,8 +225,8 @@ open class RedisServer { Q.async { for ( _, client ) in self.clients { - guard client.isMonitoring.load() else { continue } - guard let channel = client.channel else { continue } + guard client.isMonitoring.load(ordering: .relaxed) else { continue } + guard let channel = client.channel else { continue } channel.writeAndFlush(logPacket, promise: nil) } } @@ -249,7 +251,7 @@ open class RedisServer { .addHandler(BackPressureHandler() /* Oh well :-) */, name: "com.apple.nio.backpressure") .flatMap { - let cid = clientID.add(1) + let cid = clientID.wrappingIncrementThenLoad(ordering: .relaxed) let handler = RedisCommandHandler(id: cid, server: self) self.Q.async { From 31fbd75b2e039be2949e67a90c444bdcd2ae93f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Mon, 29 Jan 2024 22:53:42 +0100 Subject: [PATCH 12/16] Lower required atomics version to 1.0 1.2 requires Swift 5.9 --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 10b1eae..dfc520a 100644 --- a/Package.swift +++ b/Package.swift @@ -11,7 +11,7 @@ let package = Package( dependencies: [ .package(url: "https://github.com/SwiftNIOExtras/swift-nio-redis.git", from: "0.10.3"), - .package(url: "https://github.com/apple/swift-atomics", from: "1.2.0") + .package(url: "https://github.com/apple/swift-atomics", from: "1.0.0") ], targets: [ .target(name: "RedisServer", dependencies: [ "NIORedis", "Atomics" ]), From 9a0a50609a3aba359e80c03263f49d73864d2011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Mon, 29 Jan 2024 23:27:08 +0100 Subject: [PATCH 13/16] Drop travis configs ... --- .travis.d/before-install.sh | 22 ---------------------- .travis.d/install.sh | 32 -------------------------------- 2 files changed, 54 deletions(-) delete mode 100755 .travis.d/before-install.sh delete mode 100755 .travis.d/install.sh diff --git a/.travis.d/before-install.sh b/.travis.d/before-install.sh deleted file mode 100755 index f8d4b74..0000000 --- a/.travis.d/before-install.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -if [[ "$TRAVIS_OS_NAME" == "Linux" ]]; then - sudo apt-get -q update - sudo apt-get install -y wget \ - clang-3.8 libc6-dev make git libicu52 libicu-dev \ - git autoconf libtool pkg-config \ - libblocksruntime-dev \ - libkqueue-dev \ - libpthread-workqueue-dev \ - systemtap-sdt-dev \ - libbsd-dev libbsd0 libbsd0-dbg \ - curl libcurl4-openssl-dev \ - libssl-dev \ - libedit-dev \ - libpython2.7 \ - python2.7 python2.7-dev \ - libxml2 - - sudo update-alternatives --quiet --install /usr/bin/clang clang /usr/bin/clang-3.8 100 - sudo update-alternatives --quiet --install /usr/bin/clang++ clang++ /usr/bin/clang++-3.8 100 -fi diff --git a/.travis.d/install.sh b/.travis.d/install.sh deleted file mode 100755 index 2d1b622..0000000 --- a/.travis.d/install.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -# Install Swift - -wget "${SWIFT_SNAPSHOT_NAME}" - -TARBALL="`ls swift-*.tar.gz`" -echo "Tarball: $TARBALL" - -TARPATH="$PWD/$TARBALL" - -cd $HOME # expand Swift tarball in $HOME -tar zx --strip 1 --file=$TARPATH -pwd - -export PATH="$PWD/usr/bin:$PATH" -which swift - -if [ `which swift` ]; then - echo "Installed Swift: `which swift`" -else - echo "Failed to install Swift?" - exit 42 -fi -swift --version - - -# Environment - -TT_SWIFT_BINARY=`which swift` - -echo "${TT_SWIFT_BINARY}" From 9644fab6f20076e3cf6812a5985aa74bdde792bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Tue, 30 Jan 2024 00:07:30 +0100 Subject: [PATCH 14/16] Use swift-nio-redis 0.11.0 Has an important bugfix that was blocking redis-client. --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index dfc520a..b0dac14 100644 --- a/Package.swift +++ b/Package.swift @@ -10,7 +10,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/SwiftNIOExtras/swift-nio-redis.git", - from: "0.10.3"), + from: "0.11.0"), .package(url: "https://github.com/apple/swift-atomics", from: "1.0.0") ], targets: [ From b139e508b3e29d357892672201ebc2eb417b2b51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Tue, 30 Jan 2024 00:08:04 +0100 Subject: [PATCH 15/16] Add but don't implement a few COMMAND subcmds Add an own error for known but unsupported commands. --- .../RedisServer/Commands/CommandTable.swift | 2 +- .../RedisServer/Commands/ServerCommands.swift | 18 ++++++++++++++++-- Sources/RedisServer/Values/RedisError.swift | 7 +++++-- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Sources/RedisServer/Commands/CommandTable.swift b/Sources/RedisServer/Commands/CommandTable.swift index 3ed27d1..0db1d84 100644 --- a/Sources/RedisServer/Commands/CommandTable.swift +++ b/Sources/RedisServer/Commands/CommandTable.swift @@ -23,7 +23,7 @@ extension RedisServer { static let defaultCommandTable : RedisCommandTable = [ // command is funny in that arity is 0 Command(name : "COMMAND", - type : .optionalValue(Commands.COMMAND), // FIXME: multivalue + type : .optionalValue(Commands.COMMAND), // FIXME: multivalue! flags : [ .loading, .stale ]), Command(name : "PING", diff --git a/Sources/RedisServer/Commands/ServerCommands.swift b/Sources/RedisServer/Commands/ServerCommands.swift index 8bca358..86ef1d0 100644 --- a/Sources/RedisServer/Commands/ServerCommands.swift +++ b/Sources/RedisServer/Commands/ServerCommands.swift @@ -24,13 +24,27 @@ extension Commands { let commandTable = ctx.handler.server.commandTable if let value = value { + // TODO: Only supports one parameter here. guard let s = value.stringValue else { throw RedisError.unknownSubcommand // TBD? ProtocolError? } switch s.uppercased() { - case "COUNT": ctx.write(commandTable.count) - default: throw RedisError.unknownSubcommand + case "COUNT": + ctx.write(commandTable.count) + case "LIST": + // TODO: [FILTERBY ] + ctx.write(commandTable.map(\.name)) + case "DOCS": + throw RedisError.unsupportedSubcommand + case "GETKEYS": + throw RedisError.unsupportedSubcommand + case "GETKEYSANDFLAGS": + throw RedisError.unsupportedSubcommand + case "INFO": + throw RedisError.unsupportedSubcommand + default: + throw RedisError.unknownSubcommand } } else { diff --git a/Sources/RedisServer/Values/RedisError.swift b/Sources/RedisServer/Values/RedisError.swift index d78fd70..a28ac83 100644 --- a/Sources/RedisServer/Values/RedisError.swift +++ b/Sources/RedisServer/Values/RedisError.swift @@ -2,7 +2,7 @@ // // This source file is part of the swift-nio-redis open source project // -// Copyright (c) 2018 ZeeZide GmbH. and the swift-nio-redis project authors +// Copyright (c) 2018-2024 ZeeZide GmbH. and the swift-nio-redis project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -21,6 +21,7 @@ enum RedisError : Swift.Error, RESPEncodable { case indexOutOfRange case notAnInteger case unknownSubcommand + case unsupportedSubcommand case syntaxError case dbIndexOutOfRange case invalidDBIndex @@ -58,7 +59,9 @@ enum RedisError : Swift.Error, RESPEncodable { return "value is not an integer or out of range" case .unknownSubcommand: return "Unknown subcommand or wrong number of arguments." - + case .unsupportedSubcommand: + return "The subcommand is known, but unsupported." + case .wrongNumberOfArguments(let command): if let command = command { return "wrong number of arguments for: \(command.uppercased())" From fa9610e4ff92b10b62e1154dd8a15418d52db2dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Tue, 30 Jan 2024 00:22:15 +0100 Subject: [PATCH 16/16] Update timings for new version ... --- README.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d9c05aa..6f09b16 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,25 @@ Contributions welcome!! A lot of the missing stuff is really easy to add! Performance differs, e.g. lists are implemented using arrays (hence RPUSH is okayish, LPUSH is very slow). But looking at just the simple GET/SET, it is surprisingly close to the -highly optimized C implementation: +highly optimized C implementation. + +### 2024-01-30 Swift 5.9.2 + +Redi/S (1 NIO thread on M1 Mac Mini): +``` +helge@M1ni ~ $ redis-benchmark -p 1337 -t SET,GET,RPUSH,INCR -n 500000 -q +WARNING: Could not fetch server CONFIG +SET: 163345.31 requests per second, p50=0.255 msec +GET: 167336.02 requests per second, p50=0.239 msec +INCR: 158780.56 requests per second, p50=0.239 msec +RPUSH: 157480.31 requests per second, p50=0.271 msec +``` + +Note that more threads end up being worse. Not entirely sure why. + +### Those Are Older Numbers from 2018 + +- using Swift 4.2 on Intel, IIRC Redi/S (2 NIO threads on MacPro 3,7 GHz Quad-Core Intel Xeon E5): ```