View Javadoc
1   /*
2    * Copyright 2016 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
5    * "License"); you may not use this file except in compliance with the License. You may obtain a
6    * copy of the License at:
7    *
8    * https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software distributed under the License
11   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   * or implied. See the License for the specific language governing permissions and limitations under
13   * the License.
14   */
15  
16  package io.netty.handler.codec.redis;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.buffer.Unpooled;
20  import io.netty.util.CharsetUtil;
21  import io.netty.util.collection.LongObjectHashMap;
22  import io.netty.util.collection.LongObjectMap;
23  import io.netty.util.internal.UnstableApi;
24  
25  import java.util.HashMap;
26  import java.util.Map;
27  
28  /**
29   * A default fixed redis message pool.
30   */
31  @UnstableApi
32  public final class FixedRedisMessagePool implements RedisMessagePool {
33  
34      public enum RedisReplyKey {
35          OK, PONG, QUEUED
36      }
37  
38      public enum RedisErrorKey {
39          ERR("ERR"),
40          ERR_IDX("ERR index out of range"),
41          ERR_NOKEY("ERR no such key"),
42          ERR_SAMEOBJ("ERR source and destination objects are the same"),
43          ERR_SYNTAX("ERR syntax error"),
44          BUSY("BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE."),
45          BUSYKEY("BUSYKEY Target key name already exists."),
46          EXECABORT("EXECABORT Transaction discarded because of previous errors."),
47          LOADING("LOADING Redis is loading the dataset in memory"),
48          MASTERDOWN("MASTERDOWN Link with MASTER is down and slave-serve-stale-data is set to 'no'."),
49          MISCONF("MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. " +
50              "Commands that may modify the data set are disabled. Please check Redis logs for details " +
51              "about the error."),
52          NOREPLICAS("NOREPLICAS Not enough good slaves to write."),
53          NOSCRIPT("NOSCRIPT No matching script. Please use EVAL."),
54          OOM("OOM command not allowed when used memory > 'maxmemory'."),
55          READONLY("READONLY You can't write against a read only slave."),
56          WRONGTYPE("WRONGTYPE Operation against a key holding the wrong kind of value"),
57          NOT_AUTH("NOAUTH Authentication required.");
58  
59          private final String msg;
60  
61          RedisErrorKey(String msg) {
62              this.msg = msg;
63          }
64  
65          @Override
66          public String toString() {
67              return msg;
68          }
69      }
70  
71      private static final long MIN_CACHED_INTEGER_NUMBER = RedisConstants.NULL_VALUE; // inclusive
72      private static final long MAX_CACHED_INTEGER_NUMBER = 128; // exclusive
73  
74      // cached integer size cannot larger than `int` range because of Collection.
75      private static final int SIZE_CACHED_INTEGER_NUMBER = (int) (MAX_CACHED_INTEGER_NUMBER - MIN_CACHED_INTEGER_NUMBER);
76  
77      /**
78       * A shared object for {@link FixedRedisMessagePool}.
79       */
80      public static final FixedRedisMessagePool INSTANCE = new FixedRedisMessagePool();
81  
82      // internal caches.
83      private final Map<ByteBuf, SimpleStringRedisMessage> byteBufToSimpleStrings;
84      private final Map<String, SimpleStringRedisMessage> stringToSimpleStrings;
85      private final Map<RedisReplyKey, SimpleStringRedisMessage> keyToSimpleStrings;
86      private final Map<ByteBuf, ErrorRedisMessage> byteBufToErrors;
87      private final Map<String, ErrorRedisMessage> stringToErrors;
88      private final Map<RedisErrorKey, ErrorRedisMessage> keyToErrors;
89      private final Map<ByteBuf, IntegerRedisMessage> byteBufToIntegers;
90      private final LongObjectMap<IntegerRedisMessage> longToIntegers;
91      private final LongObjectMap<byte[]> longToByteBufs;
92  
93      /**
94       * Creates a {@link FixedRedisMessagePool} instance.
95       */
96      private FixedRedisMessagePool() {
97          keyToSimpleStrings = new HashMap<RedisReplyKey, SimpleStringRedisMessage>(RedisReplyKey.values().length, 1.0f);
98          stringToSimpleStrings = new HashMap<String, SimpleStringRedisMessage>(RedisReplyKey.values().length, 1.0f);
99          byteBufToSimpleStrings = new HashMap<ByteBuf, SimpleStringRedisMessage>(RedisReplyKey.values().length, 1.0f);
100         for (RedisReplyKey value : RedisReplyKey.values()) {
101             ByteBuf key = Unpooled.unreleasableBuffer(Unpooled.wrappedBuffer(
102                 value.name().getBytes(CharsetUtil.UTF_8))).asReadOnly();
103             SimpleStringRedisMessage message = new SimpleStringRedisMessage(new String(Unpooled.unreleasableBuffer(
104                 Unpooled.wrappedBuffer(value.name().getBytes(CharsetUtil.UTF_8))).array()));
105             stringToSimpleStrings.put(value.name(), message);
106             keyToSimpleStrings.put(value, message);
107             byteBufToSimpleStrings.put(key, message);
108         }
109 
110         keyToErrors = new HashMap<RedisErrorKey, ErrorRedisMessage>(RedisErrorKey.values().length, 1.0f);
111         stringToErrors = new HashMap<String, ErrorRedisMessage>(RedisErrorKey.values().length, 1.0f);
112         byteBufToErrors = new HashMap<ByteBuf, ErrorRedisMessage>(RedisErrorKey.values().length, 1.0f);
113         for (RedisErrorKey value : RedisErrorKey.values()) {
114             ByteBuf key = Unpooled.unreleasableBuffer(Unpooled.wrappedBuffer(
115                 value.toString().getBytes(CharsetUtil.UTF_8))).asReadOnly();
116             ErrorRedisMessage message = new ErrorRedisMessage(new String(Unpooled.unreleasableBuffer(
117                 Unpooled.wrappedBuffer(value.toString().getBytes(CharsetUtil.UTF_8))).array()));
118             stringToErrors.put(value.toString(), message);
119             keyToErrors.put(value, message);
120             byteBufToErrors.put(key, message);
121         }
122 
123         byteBufToIntegers = new HashMap<ByteBuf, IntegerRedisMessage>(SIZE_CACHED_INTEGER_NUMBER, 1.0f);
124         longToIntegers = new LongObjectHashMap<IntegerRedisMessage>(SIZE_CACHED_INTEGER_NUMBER, 1.0f);
125         longToByteBufs = new LongObjectHashMap<byte[]>(SIZE_CACHED_INTEGER_NUMBER, 1.0f);
126         for (long value = MIN_CACHED_INTEGER_NUMBER; value < MAX_CACHED_INTEGER_NUMBER; value++) {
127             byte[] keyBytes = RedisCodecUtil.longToAsciiBytes(value);
128             ByteBuf keyByteBuf = Unpooled.unreleasableBuffer(Unpooled.wrappedBuffer(keyBytes)).asReadOnly();
129             IntegerRedisMessage cached = new IntegerRedisMessage(value);
130             byteBufToIntegers.put(keyByteBuf, cached);
131             longToIntegers.put(value, cached);
132             longToByteBufs.put(value, keyBytes);
133         }
134     }
135 
136     @Override
137     public SimpleStringRedisMessage getSimpleString(String content) {
138         return stringToSimpleStrings.get(content);
139     }
140 
141     /**
142      * Returns {@link SimpleStringRedisMessage} for the given {@link RedisReplyKey}
143      * or {@code null} if it does not exist.
144      */
145     public SimpleStringRedisMessage getSimpleString(RedisReplyKey key) {
146         return keyToSimpleStrings.get(key);
147     }
148 
149     @Override
150     public SimpleStringRedisMessage getSimpleString(ByteBuf content) {
151         return byteBufToSimpleStrings.get(content);
152     }
153 
154     @Override
155     public ErrorRedisMessage getError(String content) {
156         return stringToErrors.get(content);
157     }
158 
159     /**
160      * Returns {@link ErrorRedisMessage} for the given {@link RedisErrorKey}
161      * or {@code null} if it does not exist.
162      */
163     public ErrorRedisMessage getError(RedisErrorKey key) {
164         return keyToErrors.get(key);
165     }
166 
167     @Override
168     public ErrorRedisMessage getError(ByteBuf content) {
169         return byteBufToErrors.get(content);
170     }
171 
172     @Override
173     public IntegerRedisMessage getInteger(long value) {
174         return longToIntegers.get(value);
175     }
176 
177     @Override
178     public IntegerRedisMessage getInteger(ByteBuf content) {
179         return byteBufToIntegers.get(content);
180     }
181 
182     @Override
183     public byte[] getByteBufOfInteger(long value) {
184         return longToByteBufs.get(value);
185     }
186 }