Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow keyed cache access without data wrapper via KeyedCacheSupporting #140

Open
wlisac opened this issue Mar 1, 2019 · 2 comments
Open
Labels
enhancement New feature or request
Projects

Comments

@wlisac
Copy link

wlisac commented Mar 1, 2019

The KeyedCacheSupporting implementation wraps all Codable values in a data object.

I can see why this is needed for String, Int, etc, when using Codable with JSON.

However, when getting data from a redis cache that is shared with different (non-vapor) services, I'd like to be able to skip the data wrapper and still use Codable for my own objects.

Is there a way to get or set Codable values without this data wrapper and still use keyed cache API?

let cache = try req.keyedCache(for: .redis)
return cache.get("my-key", as: MyModel.self)

My current workaround is to get raw values using the RedisClient directly:

req.withPooledConnection(to: .redis) { redis in
    redis.jsonGet("my-key", as: MyModel.self)
}

Here is the current KeyedCacheSupporting implementation that uses the data wrapper:

extension RedisDatabase: KeyedCacheSupporting {
    /// See `KeyedCacheSupporting`.
    public static func keyedCacheGet<D>(_ key: String, as decodable: D.Type, on conn: RedisClient) throws -> EventLoopFuture<D?> where D : Decodable {
        return conn.jsonGet(key, as: _DWrapper<D>.self).map { $0?.data }
    }

    /// See `KeyedCacheSupporting`.
    public static func keyedCacheSet<E>(_ key: String, to encodable: E, on conn: RedisClient) throws -> EventLoopFuture<Void> where E : Encodable {
        return conn.jsonSet(key, to: _EWrapper(encodable))
    }

    /// See `KeyedCacheSupporting`.
    public static func keyedCacheRemove(_ key: String, on conn: RedisClient) throws -> EventLoopFuture<Void> {
        return conn.delete(key)
    }
}


private struct _EWrapper<T>: Encodable where T: Encodable {
    var data: T
    init(_ data: T) { self.data = data }
}
private struct _DWrapper<T>: Decodable where T: Decodable {
    var data: T
    init(_ data: T) { self.data = data }
}
@tanner0101 tanner0101 added the enhancement New feature or request label Mar 1, 2019
@tanner0101 tanner0101 added this to To Do in Vapor 4 via automation Mar 1, 2019
@tanner0101
Copy link
Member

No, that's not possible with the current API. As you mentioned, we must wrap everything because Swift's JSON coders don't support top-level fragments. The workaround you found seems like the best option.

I think it's worth trying to find a better solution here, but that would need to be targeted to the next release. Some ideas:

  • Find a workaround to en/decoding top-level JSON fragments. (I like this the best)
  • Only wrap values if they are single value.

@wlisac
Copy link
Author

wlisac commented Mar 1, 2019

Awesome. Thanks @tanner0101.

The workaround is pretty usable for now 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
Vapor 4
  
Backlog
Development

No branches or pull requests

2 participants