-
Notifications
You must be signed in to change notification settings - Fork 29
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
Change the way redis_client deserializes empty strings. #24
Comments
I believe they're both stored in Redis as a Is there a use-case/example that shows an issue of using null? |
Well.. if you simply want to store a String, and don't check if it's empty yourself and read it again, you'll encounter a null pointer exception. So everytime you store a String you have to check if it's not empty. I've been thinking about the whole serializer, and I'm wondering if it wouldn't make more sense to provide the serializer function to serialize objects, instead of doing it automatically. Right now, the JsonEncoder actually fails if you store a String like What if I actually want to store a String like |
I can't think of a use case, where I don't know that I wanted to store a map (or something else serialized) and I think it's a lot more safe and less confusing if the calls where like this then: client.set("mykey", serialize(myData));
var value = deserialize(client.get("myKey")); |
Sorry for the delay, been held up in meetings all day...
Not sure which friction point you're referring to. If we follow the SET operation down we see that the value gets delegated to the underlying raw native client and value gets passed to toBytes:
toBytes should probably return an empty The native client delegates down sendExpectSuccess:
If key is null or an empty string keyBytes throws an error as expected.
We need to make a decision between convenience and purity here. In order to provide a high-level API most objects are serialized as JSON, it seems wrong to do this for strings since this would be a client library opinion to JSON encode every string, which I doubt other redis bindings would be doing so JSON encoding strings would reduce interoperability. The way to get it out as a raw string would be to use the Native Client API, e.g:
But this wouldn't be symmetrical to how they're set so it's a little inconsistent, so maybe what we need to do is publish string-specific getString/setString/etc APIs on redis string operations that just stores/retrieves raw strings?
In a way the RedisNativeClient lets you do this, i.e. it doesn't add any encoding itself. IMO the RedisClient should ideally provide a convenience wrapper where this boilerplate shouldn't be necessary. |
At the moment, the behaviour is: client.set("mykey", "");
var myString = client.get("mykey");
assert(myString == null); Since In my opinion, What you're saying, is that all user entered data (and Strings in general) must be handled with the IMO this will lead to problems because most of the users won't be aware of this problem and it will result in At the same time, most of the other functionality ( My proposal is to introduce the high level functions:
Since the other methods (
IMO unpredictability is the wrong decision. |
I believe this is the correct behavior. The api takes an Object and there's no difference in what gets sent over the wire between
What about serializing and deserializing other data types e.g. a
There's a lot more than 2 methods you would have to add, e.g. every method containing As I prefer to use a high-level client that saves boilerplate when using Redis as an Object data store, I think instead of creating mismatched overloads to fit in all in one client we should look at creating a new client (inheriting form RedisNativeClient) that just deals with unencoded Strings. So we could rename this client to There could also be an convenience property on
|
That is incorrect. Redis returns a Since the current implementation always returns
Well, that is exactly my point: when you set objects, you know that they are going to be serialized. The most obvious solution is of course client.set("key", serialize(object));
assert(client.get("key") == serialize(object));
// but this can't be guaranteed:
assert(object == deserialize(client.get("key"))); // Can potentially fail.
That's true. But there aren't that many other methods (about 5) because all the integer commands can be skipped. I think I'd probably go for a named argument like:
Well. I agree with you that boilerplate code should be handled by the library. I just think that doing magic, behind the scenes, that results in unexpected behaviour is a bad solution and can even lead to serious app instabilities and vulnerabilities. Storing data inside Redis is a misuse of redis. Redis is meant to store strings. The fact that you can store objects, serialized as Strings in Redis is just a nice addition, but the intent should be clear when used in a program. So I think that using |
Well it applies to collections at least:
So what should happen when you try to SET a Requiring I still prefer having a separate high-level |
I like this compromise, allowing for different encoders is a good solution |
A client like |
Yep, will change it so values are always encoded so strings with a JsonEncoder is stored as |
So, the |
Would you extend the Basically, the only methods that would make sense in such a wrapper object are the methods that (de)serialize data, right? So extending the What about a What are your thoughts? |
Nope, I don't see It would just an extra file (like RedisClient.dart) so not worried about trying to force impl sharing, and I expect the hierarchy to just look like:
To access one from the other we could expose getter properties like:
and
|
The |
The |
Hi Matias, if you've got any uncommitted changes locally can you commit them in the next few days, as I might have some time free soon to pick up anything that's left to do. |
@mythz Hi, will do |
I somehow hadn't seen this issue until a couple of days ago, but I think I've got most of your concerns covered. My main concern was not having the library impose a security threat when deserializing as what @enyo mentioned what would happen when something that is not a string would be deserialized as an instance. That's why I did not write a recursive method for deserialization. Luckily I found the only places I have to deserialize anything beyond one level of depth is the places where Redis already provides an abstraction via its datatypes already; the lists, sets and hashes. I was thinking about what to do with custom classes and thinking about a similar approach to what you did before with DateTimes (prepending "Date(" and appending ")"). I kept that date (de-)serialization in but might remove that too and leave it all to the user, since if we want to provide deserialization options beyond that abstraction we would be have to make users aware of how this information is stored since other functionality Redis offers (like operations on strings) might behave unexpectedly when they are prepended with type information. Now we could make our library store type information in a separate hash or something, but then we would be making an ORM (which would be pretty cool to me) but looking through the rest of the Redis API's listed on www.redis.io/clients ORM's get special mention below the clients. |
In the current version, redis_client deserializes empty values to
null
.Since redis doesn't support null values by itself that's all empty Strings are thus converted to
null
.I propose to change this behaviour so
null
and""
(empty String) both deserialize to""
(empty String). I think it prevents errors, and theoretically redis always stores an empty String, not anull
value.@dartist/owners what do you say?
The text was updated successfully, but these errors were encountered: