redimo

package module
v1.4.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 27, 2023 License: Apache-2.0 Imports: 18 Imported by: 1

README

REDIMO

Go GoDoc

Redimo is a library that allows you to use the Redis API on DynamoDB. The DynamoDB system is excellent at what it does on a specific set of use cases, but is more difficult to use than it should be because the API is very low level and requires a lot of arcane knowledge. The Redis API, on the other hand, deals with familiar data structures (key-value, sets, sorted sets, lists, streams, hash maps) that you can directly map to your application's data. Redimo bridges the two and translates the Redis API operations into space / time / cost-efficient DynamoDB API calls.

Redimo is especially well suited to serverless environments, since there is no pool of connections to handle and DynamoDB is purpose-built for near-zero management use. But you can use it with regular servers well, especially when you want excellent horizontal scalability. See the section on differences between Redis and DynamoDB below for more information.

Roadmap

The library is currently in v0, so I'm asking for comments and feedback on the interface and data schema. I expect to freeze the API to v1 on the 1st of July, after which all v1 releases will be guaranteed not to break backwards compatibility and work on pre-existing v1 DynamoDB tables without any data migrations. Will also be adding documentation for every method, linking to the corresponding Redis docs and detailing the differences in how they function and what their limitations are.

The first priority is to mirror the Redis API as much as possible, even in the cases where that means the DynamoDB mapping is inefficient. After v1, I'd like to add more efficient operations as extra methods.

This library is the Go version, but I'm thinking of building Ruby, JavaScript, Python and Java versions as well. You can contact me if you'd like to prioritise or sponsor any of them.

Limitations

Some parts of the Redis API are unfeasible (as far as I know, and as of now) on DynamoDB, like the binary / bit twiddling operations and their derivatives, like GETBIT, SETBIT, BITCOUNT, etc. and HyperLogLog. These have been left out of the API for now.

TTL operations are possible, but a little more complicated, and will likely be added soon.

Pub/Sub isn't possible as a DynamoDB feature itself, but it should be possible to add integration with AWS IoT Core or similar in the future. This isn't useful in a serverless environment, though, so it's a lower priority. Contact me if you disagree and want this quickly.

Lua Scripting is currently not applicable - the library runs inside your codebase, so anything you wanted to do with Lua would just be done with normal library calls inside your application, with the data loaded in and out of DynamoDB.

ACLs (access control lists) are not currently supported.

Transactions across arbitrary operations, using MULTI WATCH EXEC, are not yet supported. Talk to me if you need this.

Differences between Redis and DynamoDB

Why bother with this at all? Why not just use Redis?

  • In Redis, the size of your dataset is limited to the RAM available in a single machine, while in DynamoDB it is distributed across many machines and is effectively unlimited. Redis has separate clustering support, but it doesn't match the ease of use and automatic scalability of a managed service like DynamoDB.

  • Redis is connection based and supports a limited (but pretty large) number of connections. DynamoDB has no such limits, so it's great for serverless or very horizontally scaled deployments.

  • Redis bandwidth is limited by the network interface of the server that it's running on, DynamoDB is distributed and doesn't have hard bandwidth limits.

  • In Redis all operations run on a single CPU / thread - so each operation is extremely fast, but there's only one operation running at a time. Slow operations, or a large queue, will block other operations. In DynamoDB, each key's operations runs on different machines, so there's no block of any sort across keys. Operations on the same key will block using optimistic concurrency, though - Redimo will try the operation, but will fail if the data has been unexpectedly modified since the last check. The integrity of the data is still preserved, but in DynamoDB there's more probability that lots of operations on the same keys will step on each other's toes.

  • Redis provides very fast response times (few microseconds), but because all operations are happening one by one, the total throughput is fixed. DynamoDB is relatively slower for each individual operation (few milliseconds) but because of the distributed nature of the system the response time will remain constant at thousands or millions of requests per second. Some workloads even get faster at higher loads because the system starts allocating more servers to your table.

  • In Redis a high-availability system isn't that easy to set up, while on DynamoDB it's the default.

  • The DynamoDB Global Tables feature allows you to have your data eventually replicated in many regions across the world, enabling master-master (both reads and writes) at any region. Note that if you use Global Tables, the data structures that require lots of transactional updates – streams and lists particularly – cannot work reliably across regions. Mostly because the speed of light isn't as fast as we'd like it to be. This is still excellent for publishing metadata or any information that doesn't change every often throughout the world at low latency. Key-values, hashes, sets, sorted sets, etc. work great in a write-here and read-everywhere scenario.

  • The persistence guarantees offered by DynamoDB allows you to use the Redimo service as your primary / only ACID database, while Redis has a periodic file system sync (so you might lose data since the last sync). While you can switch Redis to wait for a file system write in all cases or set up a quorum cluster, DynamoDB has much higher reliability, including Multi-AZ guarantees, right out of the box.

  • With Redis you'll need to run servers and either buy or rent fixed units of CPU and RAM, while with DynamoDB you have the option of paying on-demand (per request) or setting up auto-scaling bandwidth slabs.

  • With DynamoDB, being a distributed system, you will not get a lot of the transactional and atomic behaviour that comes freely and easily with Redis. The Redimo library uses whatever transactional APIs are available where necessary, so the limits of those APIs will be passed on to you - in DynamoDB you can only have up to 25 items in a transaction, for example, so the MSET operation on has a 25 key limit when using Redimo / DynamoDB. Redis does not have any such limitations.

  • DynamoDB is geared towards having lots of small objects across many partitions, while Redis is workload agnostic. For example, with Redis if you can do N writes per second across one or many keys, in DynamoDB you can do only one tenth of that on a single key – but you could operate at millions of times N across all your keys, if you have a lot of keys.

  • In the same vein, Redis allows all keys and values to be up to 512MB (although big keys or values is always a bad idea and will naturally cause bandwidth constraints on a single server) - in DynamoDB your keys (and set members) can only be up to 1KB, while your values can only be up to 400KB in size.

So there's no clear-cut answer to which is better – it depends entirely on your application and expected workload. The point of this library is not to promote one over the other – it's to make your development much easier and more comfortable if you decide to use DynamoDB, because the Redis API is much more approachable and easier to model with.

If you still need help with making a decision, you can contact me at sudhir.j@gmail.com

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrXGroupNotInitialized = errors.New("consumer group not initialized with XGROUP")

Functions

func ToValueMap added in v1.2.1

func ToValueMap(data interface{}) map[string]Value

func ToValueMapE added in v1.2.4

func ToValueMapE(data interface{}) (map[string]Value, error)

Types

type BytesValue

type BytesValue struct {
	B []byte
}

BytesValue is a convenience wrapper for a byte slice, usable as

BytesValue{[]byte{1,2,3}}

func (BytesValue) ToAV

func (bv BytesValue) ToAV() types.AttributeValue

type Client

type Client struct {
	// contains filtered or unexported fields
}

func NewClient

func NewClient(service *dynamodb.Client) Client

func (Client) Attributes

func (c Client) Attributes(pk string, sk string, skN string) Client

func (Client) CreatePayPerRequestTable added in v1.3.4

func (c Client) CreatePayPerRequestTable() error

func (Client) CreateProvisionedTable added in v1.2.8

func (c Client) CreateProvisionedTable(readCapacity int64, writeCapacity int64) error

func (Client) CreateTable added in v1.2.5

func (c Client) CreateTable(readCapacity int64, writeCapacity int64) error

func (Client) DECR

func (c Client) DECR(key string) (after int64, err error)

DECR decrements the number stored at the key by 1 (n = n - 1) and returns the new value. If the key does not exist, it will be initialized with zero before applying the operation.

If there is an existing value at the key with a non-numeric type (string, bytes, etc.) the operation will throw an error. If the existing value is numeric, the operation can continue irrespective of how it was initially set.

Cost is O(1) or 1 WCU.

Works similar to https://redis.io/commands/decr

func (Client) DECRBY

func (c Client) DECRBY(key string, delta int64) (after int64, err error)

DECRBY decrements the number stored at the key with the given delta (n = n - delta) and returns the new value. If the key does not exist, it will be initialized with zero before applying the operation.

If there is an existing value at the key with a non-numeric type (string, bytes, etc.) the operation will throw an error. If the existing value is numeric, the operation can continue irrespective of how it was initially set.

Cost is O(1) or 1 WCU.

Works similar to https://redis.io/commands/decrby

func (Client) DEL added in v1.1.0

func (c Client) DEL(key string) (deletedFields []string, err error)

func (Client) EXISTS added in v1.1.0

func (c Client) EXISTS(key string) (exists bool, err error)

func (Client) EventuallyConsistent

func (c Client) EventuallyConsistent() Client

func (Client) ExistsTable added in v1.2.6

func (c Client) ExistsTable() (bool, error)

func (Client) GEOADD

func (c Client) GEOADD(key string, members map[string]GLocation) (newlyAddedMembers map[string]GLocation, err error)

GEOADD adds the given members into the key. Members are represented by a map of name to GLocation, which is just a wrapper for latitude and longitude. If a member already exists, its location will be updated. The method only returns the members that were added as part of the operation and did not already exist.

Cost is O(1) / 1 WCU for each member being added or updated.

Works similar to https://redis.io/commands/geoadd

func (Client) GEODIST

func (c Client) GEODIST(key string, member1, member2 string, unit GUnit) (distance float64, ok bool, err error)

GEODIST returns the scalar distance between the two members, converted to the given unit. If either of the members or the key is missing, ok will be false. Each GUnit also has convenience methods to convert distances into other units.

Cost is O(1) / 1 RCU for each of the two members.

Works similar to https://redis.io/commands/geodist

func (Client) GEOHASH

func (c Client) GEOHASH(key string, members ...string) (geohashes map[string]string, err error)

GEOHASH returns the Geohash strings (see https://en.wikipedia.org/wiki/Geohash) of the given members. If any members were not found, they will not be present in the returned map.

Cost is O(1) / 1 RCU for each member.

Works similar to https://redis.io/commands/geohash

func (Client) GEOPOS

func (c Client) GEOPOS(key string, members ...string) (locations map[string]GLocation, err error)

GEOPOS returns the stored locations for each of the given members, as a map of member to location. If a member cannot be found, it will not be present in the returned map.

Cost is O(1) / 1 RCU for each member.

Works similar to https://redis.io/commands/geopos

func (Client) GEORADIUS

func (c Client) GEORADIUS(key string, center GLocation, radius float64, radiusUnit GUnit, count int32) (positions map[string]GLocation, err error)

GEORADIUS returns the members (limited to the given count) that are located within the given radius of the given center. Note that the positions are returned in no particular order – if there are more members inside the given radius than the given count, the method *does not* guarantee that the returned locations are the closest.

The GLocation type has convenience methods to calculate the distance between points, this can be used to sort the locations as required.

Cost is O(N) where N is the number of locations inside the square / bounding box that contains the circle we're searching inside.

Works similar to https://redis.io/commands/georadius

func (Client) GEORADIUSBYMEMBER

func (c Client) GEORADIUSBYMEMBER(key string, member string, radius float64, radiusUnit GUnit, count int32) (positions map[string]GLocation, err error)

GEORADIUSBYMEMBER returns the members (limited to the given count) that are located within the given radius of the given member. Note that the positions are returned in no particular order – if there are more members inside the given radius than the given count, the method *does not* guarantee that the returned locations are the closest.

The GLocation type has convenience methods to calculate the distance between points, this can be used to sort the locations as required.

Cost is O(N) where N is the number of locations inside the square / bounding box that contains the circle we're searching inside.

Works similar to https://redis.io/commands/georadiusbymember

func (Client) GET

func (c Client) GET(key string) (val ReturnValue, err error)

GET fetches the value at the given key. If the key does not exist, the ReturnValue will be Empty().

Works similar to https://redis.io/commands/get

func (Client) GETSET

func (c Client) GETSET(key string, value Value) (oldValue ReturnValue, err error)

GETSET gets the value at the key and atomically sets it to a new value.

Works similar to https://redis.io/commands/getset

func (Client) HDEL

func (c Client) HDEL(key string, fields ...string) (deletedFields []string, err error)

func (Client) HEXISTS

func (c Client) HEXISTS(key string, field string) (exists bool, err error)

func (Client) HGET

func (c Client) HGET(key string, field string) (val ReturnValue, err error)

func (Client) HGETALL

func (c Client) HGETALL(key string) (fieldValues map[string]ReturnValue, err error)

func (Client) HINCRBY

func (c Client) HINCRBY(key string, field string, delta int64) (after int64, err error)

func (Client) HINCRBYFLOAT

func (c Client) HINCRBYFLOAT(key string, field string, delta float64) (after float64, err error)

func (Client) HKEYS

func (c Client) HKEYS(key string) (keys []string, err error)

func (Client) HLEN

func (c Client) HLEN(key string) (count int32, err error)

func (Client) HMGET

func (c Client) HMGET(key string, fields ...string) (values map[string]ReturnValue, err error)

func (Client) HMSET

func (c Client) HMSET(key string, vFieldMap interface{}) (err error)

func (Client) HSET

func (c Client) HSET(key string, vFieldMap interface{}) (newlySavedFields map[string]Value, err error)

func (Client) HSETNX

func (c Client) HSETNX(key string, field string, value Value) (ok bool, err error)

func (Client) HVALS

func (c Client) HVALS(key string) (values []ReturnValue, err error)

func (Client) INCR

func (c Client) INCR(key string) (after int64, err error)

INCR increments the number stored at the key by 1 (n = n + 1) and returns the new value. If the key does not exist, it will be initialized with zero before applying the operation.

If there is an existing value at the key with a non-numeric type (string, bytes, etc.) the operation will throw an error. If the existing value is numeric, the operation can continue irrespective of how it was initially set.

Cost is O(1) or 1 WCU.

Works similar to https://redis.io/commands/incr

func (Client) INCRBY

func (c Client) INCRBY(key string, delta int64) (after int64, err error)

INCRBY increments the number stored at the key with the given delta (n = n + delta) and returns the new value. If the key does not exist, it will be initialized with zero before applying the operation.

If there is an existing value at the key with a non-numeric type (string, bytes, etc.) the operation will throw an error. If the existing value is numeric, the operation can continue irrespective of how it was initially set.

Cost is O(1) or 1 WCU.

Works similar to https://redis.io/commands/incrby

func (Client) INCRBYFLOAT

func (c Client) INCRBYFLOAT(key string, delta float64) (after float64, err error)

INCRBYFLOAT increments the number stored at the key with the given float64 delta (n = n + delta) and returns the new value. If the key does not exist, it will be initialized with zero before applying the operation.

The delta can be positive or negative, and a zero delta is effectively a no-op.

If there is an existing value at the key with a non-numeric type (string, bytes, etc.) the operation will throw an error. If the existing value is numeric, the operation can continue irrespective of how it was initially set.

Cost is O(1) or 1 WCU.

Works similar to https://redis.io/commands/incrbyfloat

func (Client) Index added in v1.2.5

func (c Client) Index(indexName string) Client

func (Client) LINDEX

func (c Client) LINDEX(key string, index int64) (element ReturnValue, err error)

func (Client) LINSERT

func (c Client) LINSERT(key string, side LSide, vPivot, vElement interface{}) (newLength int64, done bool, err error)

LINSERT inserts the given element on the given side of the pivot element.

func (Client) LLEN

func (c Client) LLEN(key string) (length int64, err error)

func (Client) LPOP

func (c Client) LPOP(key string) (element ReturnValue, err error)

func (Client) LPUSH

func (c Client) LPUSH(key string, vElements ...interface{}) (newLength int64, err error)

func (Client) LPUSHX

func (c Client) LPUSHX(key string, vElements ...interface{}) (newLength int64, err error)

func (Client) LRANGE

func (c Client) LRANGE(key string, start, stop int64) (elements []ReturnValue, err error)

func (Client) LREM

func (c Client) LREM(key string, side LSide, vElement interface{}) (newLength int64, done bool, err error)

LREM removes the first occurrence on the given side of the given element.

func (Client) LSET

func (c Client) LSET(key string, index int64, element string) (ok bool, err error)

func (Client) MGET

func (c Client) MGET(keys ...string) (values map[string]ReturnValue, err error)

MGET fetches the given keys atomically in a transaction. The call is limited to 25 keys and 4MB. See https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactGetItems.html

Works similar to https://redis.io/commands/mget

func (Client) MSET

func (c Client) MSET(vFieldMap interface{}) (err error)

MSET sets the given keys and values atomically in a transaction. The call is limited to 25 keys and 4MB. See https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactWriteItems.html

Works similar to https://redis.io/commands/mset

func (Client) MSETNX

func (c Client) MSETNX(vFieldMap interface{}) (ok bool, err error)

MSETNX sets the given keys and values atomically in a transaction, but only if none of the given keys exist. If one or more of the keys already exist, nothing will be changed and MSETNX will return false.

Works similar to https://redis.io/commands/msetnx

func (Client) RPOP

func (c Client) RPOP(key string) (element ReturnValue, err error)

func (Client) RPOPLPUSH

func (c Client) RPOPLPUSH(sourceKey string, destinationKey string) (element ReturnValue, err error)

func (Client) RPUSH

func (c Client) RPUSH(key string, vElements ...interface{}) (newLength int64, err error)

func (Client) RPUSHX

func (c Client) RPUSHX(key string, vElements ...interface{}) (newLength int64, err error)

func (Client) SADD

func (c Client) SADD(key string, members ...string) (addedMembers []string, err error)

SADD adds the given string members to the set at the given key.

Returns that members that were actually added and did not already exist in the set.

Cost is O(1) / 1 WCU for each member, whether it already exists or not.

Works similar to https://redis.io/commands/sadd

func (Client) SCARD

func (c Client) SCARD(key string) (count int32, err error)

SCARD returns the cardinality (the number of elements) in the set at key.

Cost is O(size) / 1 WCU per 4KB of data counted.

Works similar to https://redis.io/commands/scard

func (Client) SDIFF

func (c Client) SDIFF(key string, subtractKeys ...string) (members []string, err error)

func (Client) SDIFFSTORE

func (c Client) SDIFFSTORE(destinationKey string, sourceKey string, subtractKeys ...string) (count int32, err error)

func (Client) SET

func (c Client) SET(key string, vValue interface{}, flags ...Flag) (ok bool, err error)

SET stores the given Value at the given key. If called as SET("key", "value", None), SET is unconditional and is not expected to fail.

The condition flags IfNotExists and IfAlreadyExists can be specified, and if they are the SET becomes conditional and will return false if the condition fails.

Works similar to https://redis.io/commands/set

func (Client) SETNX

func (c Client) SETNX(key string, value Value) (ok bool, err error)

SETNX is equivalent to SET(key, value, Flags{IfNotExists})

Works similar to https://redis.io/commands/setnx

func (Client) SINTER

func (c Client) SINTER(key string, otherKeys ...string) (members []string, err error)

func (Client) SINTERSTORE

func (c Client) SINTERSTORE(destinationKey string, sourceKey string, otherKeys ...string) (count int32, err error)

func (Client) SISMEMBER

func (c Client) SISMEMBER(key string, member string) (ok bool, err error)

func (Client) SMEMBERS

func (c Client) SMEMBERS(key string) (members []string, err error)

func (Client) SMOVE

func (c Client) SMOVE(sourceKey string, destinationKey string, member string) (ok bool, err error)

func (Client) SPOP

func (c Client) SPOP(key string, count int32) (members []string, err error)

func (Client) SRANDMEMBER

func (c Client) SRANDMEMBER(key string, count int32) (members []string, err error)

func (Client) SREM

func (c Client) SREM(key string, members ...string) (removedMembers []string, err error)

func (Client) SUNION

func (c Client) SUNION(keys ...string) (members []string, err error)

func (Client) SUNIONSTORE

func (c Client) SUNIONSTORE(destinationKey string, sourceKeys ...string) (count int32, err error)

func (Client) StronglyConsistent

func (c Client) StronglyConsistent() Client

func (Client) Table

func (c Client) Table(tableName string) Client

func (Client) TransactionActions added in v1.2.11

func (c Client) TransactionActions(actions int) Client

func (Client) XACK

func (c Client) XACK(key string, group string, ids ...XID) (acknowledgedIds []XID, err error)

func (Client) XADD

func (c Client) XADD(key string, id XID, fields map[string]Value) (returnedID XID, err error)

XADD adds the given fields as a item on the stream at key. If the stream does not exist, it will be initialized.

If the XID passed in is XAutoID, an ID will be automatically generated on the current time and a sequence generator.

Note that if you pass in your own ID, the stream will never allow you to insert an item with an ID less than the greatest ID present in the stream – the stream can only move forwards. This guarantees that if you've read entries up to a given XID using XREAD, you can always continue reading from that last XID without fear of missing anything, because the IDs are always increasing.

Works similar to https://redis.io/commands/xadd

func (Client) XCLAIM

func (c Client) XCLAIM(key string, group string, consumer string, lastDeliveredBefore time.Time, ids ...XID) (items []StreamItem, err error)

func (Client) XDEL

func (c Client) XDEL(key string, ids ...XID) (deletedItems []XID, err error)

XDEL removes the given IDs and returns the IDs that were actually deleted as part of this operation.

Note that this operation is not atomic across given IDs – it's possible that an error is returned based on a problem deleting one of the IDs when the others have been deleted. Even when an error is returned, the items that were deleted will still be populated.

Works similar to https://redis.io/commands/xdel

func (Client) XGROUP

func (c Client) XGROUP(key string, group string, start XID) (err error)

XGROUP creates a new group for the stream at the given key. Specifying the start XID as XStart will cause consumers of the group to read from the beginning of the stream, and any existing or generated XID can be used to denote a custom starting point.

This is a required initialization step before the group can be used. Trying to use XREADGROUP without using XGROUP to initialize the group will return an error.

Cost is O(1) / 1 WCU.

Works similar to https://redis.io/commands/xgroup

func (Client) XLEN

func (c Client) XLEN(key string, start, stop XID) (count int32, err error)

XLEN counts the number of items in the stream with XIDs between the given XIDs. To count the entire stream, pass XStart and XEnd as the start and end XIDs.

Cost is O(N) or ~N RCUs where N is the number / size of items counted.

Works similar to https://redis.io/commands/xlen

func (Client) XPENDING

func (c Client) XPENDING(key string, group string, count int32) (pendingItems []PendingItem, err error)

func (Client) XRANGE

func (c Client) XRANGE(key string, start, stop XID, count int32) (streamItems []StreamItem, err error)

XRANGE fetches the stream records between two XIDs, inclusive of both the start and end IDs, limited to the count.

If you receive the entire count you've asked for, it's reasonable to suppose there might be more items in the given range that were not returned because they would exceed the count – in this case you can call the XID.Next() method on the last received stream ID for an XID to use as the start of the next call.

Common uses include fetching a single item based on XID, which would be

XRANGE(key, id, id, 1)

or fetching records in the month of February, like

XRANGE(key, NewTimeXID(beginningOfFebruary).First(), NewTimeXID(endOfFebruary).Last(), 1000)
XRANGE(key, NewTimeXID(beginningOfFebruary).First(), NewTimeXID(beginningOfMarch).First(), 1000)

Note that the two calls are equivalent, because this operation uses the DynamoDB BETWEEN operator, which translates to

start <= id <= end

There is are no offset or pagination parameters required, because when the full count is hit the next page of items can be fetched as follows:

XRANGE(key, lastFetchedItemID.Next(), NewTimeXID(endOfFebruary).Last(), 1000)

See the XID docs for more information on how to generate start and stop XIDs based on time.

Works similar to https://redis.io/commands/xrange

func (Client) XREAD

func (c Client) XREAD(key string, from XID, count int32) (items []StreamItem, err error)

XREAD reads items sequentially from a stream. The structure of a stream guarantees that the XIDs are always increasing. This implies that calling XREAD in a loop and passing in the XID of the last item read will allow iteration over all items reliably.

To start reading a stream from the beginning, use the special XStart XID.

Works similar to https://redis.io/commands/xread

func (Client) XREADGROUP

func (c Client) XREADGROUP(key string, group string, consumer string, option XReadOption, maxCount int32) (items []StreamItem, err error)

func (Client) XREVRANGE

func (c Client) XREVRANGE(key string, end, start XID, count int32) (streamItems []StreamItem, err error)

XREVRANGE is similar to XRANGE, but in reverse order. The stream items in descending chronological order. Using the same example as XRANGE, when fetching items in reverse order there are some differences when paginating. The first set of records can be fetched using:

XRANGE(key, NewTimeXID(endOfFebruary).Last(), NewTimeXID(beginningOfFebruary).First(), 1000)

he next page can be fetched using

XRANGE(key, lastFetchedItemID.Prev(), NewTimeXID(beginningOfFebruary).First(), 1000)

Works similar to https://redis.io/commands/xrevrange

func (Client) XTRIM

func (c Client) XTRIM(key string, newCount int32) (deletedCount int32, err error)

func (Client) ZADD

func (c Client) ZADD(key string, membersWithScores map[string]float64, flags Flags) (addedMembers []string, err error)

func (Client) ZCARD

func (c Client) ZCARD(key string) (count int32, err error)

func (Client) ZCOUNT

func (c Client) ZCOUNT(key string, minScore, maxScore float64) (count int32, err error)

func (Client) ZINCRBY

func (c Client) ZINCRBY(key string, member string, delta float64) (newScore float64, err error)

func (Client) ZINTER

func (c Client) ZINTER(sourceKeys []string, aggregation ZAggregation, weights map[string]float64) (membersWithScores map[string]float64, err error)

func (Client) ZINTERSTORE

func (c Client) ZINTERSTORE(destinationKey string, sourceKeys []string, aggregation ZAggregation, weights map[string]float64) (membersWithScores map[string]float64, err error)

func (Client) ZLEXCOUNT

func (c Client) ZLEXCOUNT(key string, min string, max string) (count int32, err error)

func (Client) ZPOPMAX

func (c Client) ZPOPMAX(key string, count int32) (membersWithScores map[string]float64, err error)

func (Client) ZPOPMIN

func (c Client) ZPOPMIN(key string, count int32) (membersWithScores map[string]float64, err error)

func (Client) ZRANGE

func (c Client) ZRANGE(key string, start, stop int32) (membersWithScores map[string]float64, err error)

func (Client) ZRANGEBYLEX

func (c Client) ZRANGEBYLEX(key string, min, max string, offset, count int32) (membersWithScores map[string]float64, err error)

func (Client) ZRANGEBYSCORE

func (c Client) ZRANGEBYSCORE(key string, min, max float64, offset, count int32) (membersWithScores map[string]float64, err error)

func (Client) ZRANK

func (c Client) ZRANK(key string, member string) (rank int32, found bool, err error)

func (Client) ZREM

func (c Client) ZREM(key string, members ...string) (removedMembers []string, err error)

func (Client) ZREMRANGEBYLEX

func (c Client) ZREMRANGEBYLEX(key string, min, max string) (removedMembers []string, err error)

func (Client) ZREMRANGEBYRANK

func (c Client) ZREMRANGEBYRANK(key string, start, stop int32) (removedMembers []string, err error)

func (Client) ZREMRANGEBYSCORE

func (c Client) ZREMRANGEBYSCORE(key string, min, max float64) (removedMembers []string, err error)

func (Client) ZREVRANGE

func (c Client) ZREVRANGE(key string, start, stop int32) (membersWithScores map[string]float64, err error)

func (Client) ZREVRANGEBYLEX

func (c Client) ZREVRANGEBYLEX(key string, max, min string, offset, count int32) (membersWithScores map[string]float64, err error)

func (Client) ZREVRANGEBYSCORE

func (c Client) ZREVRANGEBYSCORE(key string, max, min float64, offset, count int32) (membersWithScores map[string]float64, err error)

func (Client) ZREVRANK

func (c Client) ZREVRANK(key string, member string) (rank int32, found bool, err error)

func (Client) ZSCORE

func (c Client) ZSCORE(key string, member string) (score float64, found bool, err error)

func (Client) ZUNION

func (c Client) ZUNION(sourceKeys []string, aggregation ZAggregation, weights map[string]float64) (membersWithScores map[string]float64, err error)

func (Client) ZUNIONSTORE

func (c Client) ZUNIONSTORE(destinationKey string, sourceKeys []string, aggregation ZAggregation, weights map[string]float64) (membersWithScores map[string]float64, err error)

type Flag

type Flag string
const (
	None            Flag = "-"
	Unconditionally      = None
	IfAlreadyExists Flag = "XX"
	IfNotExists     Flag = "NX"
)

type Flags

type Flags []Flag

type FloatValue

type FloatValue struct {
	F float64
}

FloatValue is a convenience value wrapper for a float64, usable as

FloatValue{3.14}

func (FloatValue) ToAV

func (fv FloatValue) ToAV() types.AttributeValue

type GLocation

type GLocation struct {
	Lat float64
	Lon float64
}

func (GLocation) DistanceTo

func (l GLocation) DistanceTo(other GLocation, unit GUnit) (distance float64)

func (GLocation) Geohash

func (l GLocation) Geohash() string

type GUnit

type GUnit float64
const (
	Meters     GUnit = 1.0
	Kilometers GUnit = 1000.0
	Miles      GUnit = 1609.34
	Feet       GUnit = 0.3048
)

func (GUnit) To

func (from GUnit) To(to GUnit, d float64) float64

type IntValue

type IntValue struct {
	I int64
}

IntValue is a convenience value wrapper for an int64, usable as

IntValue{42}

func (IntValue) ToAV

func (iv IntValue) ToAV() types.AttributeValue

type LSide

type LSide string
const (
	Left  LSide = "LEFT"
	Right LSide = "RIGHT"
)

type PendingItem

type PendingItem struct {
	ID            XID
	Consumer      string
	LastDelivered time.Time
	DeliveryCount int32
}

type ReturnValue

type ReturnValue struct {
	// contains filtered or unexported fields
}

ReturnValue holds a value returned by DynamoDB. There are convenience methods used to coerce the held value into common types, but you can also retrieve the raw types.AttributeValue by calling ToAV if you would like to do custom decoding.

func (ReturnValue) Bytes

func (rv ReturnValue) Bytes() []byte

Bytes returns the value as a byte slice. Will be nil if the value is not actually a byte slice.

func (ReturnValue) Empty

func (rv ReturnValue) Empty() bool

Empty returns true if the value is empty or uninitialized. This indicates that the underlying DynamoDB operation did not return a value.

func (ReturnValue) Equals

func (rv ReturnValue) Equals(ov ReturnValue) bool

Equals checks equality by comparing the underlying dynamodb.AttributeValues. If they both hold the same value, as indicated by the rules of reflect.DeepEqual, Equals will return true.

func (ReturnValue) Float

func (rv ReturnValue) Float() float64

Float returns the value as float64. Will be zero-valued if the value is not numeric. If the value was originally stored as an int, it will be converted to float64 based on parsing the string representation, so there is some scope for overflows being corrected silently.

func (ReturnValue) Int

func (rv ReturnValue) Int() int64

Int returns the value as int64. Will be zero-valued if the value is not actually numeric. The value was originally a float, it will be truncated.

func (ReturnValue) Interface added in v1.3.0

func (rv ReturnValue) Interface() interface{}

Interface returns the value as an interface{}. This is useful if you are not sure what type

func (ReturnValue) Present

func (rv ReturnValue) Present() bool

Present returns true if a value is present. It indicates that the underlying DynamoDB AttributeValue has a data in any one of its fields. If you already know the type of your value, you can call the convenience method (like String() or Int()) or you can retrieve the underlying types.AttributeValue struct with ToAV and perform your down decoding.

func (ReturnValue) String

func (rv ReturnValue) String() string

String returns the value as a string. If the value was not stored as a string, a zero-value / empty string will the returned. This method will not coerce numeric of byte values.

func (ReturnValue) ToAV

func (rv ReturnValue) ToAV() types.AttributeValue

ToAV returns the underlying types.AttributeValue, allow custom deserialization.

type StreamItem

type StreamItem struct {
	ID     XID
	Fields map[string]ReturnValue
}

type StringValue

type StringValue struct {
	S string
}

StringValue is a convenience value wrapper for a string, usable as

StringValue{"hello"}

func (StringValue) ToAV

func (sv StringValue) ToAV() types.AttributeValue

type Value

type Value interface {
	ToAV() types.AttributeValue
}

Value allows you to store values of any type supported by DynamoDB, as long as they implement this interface and encode themselves into a types.AttributeValue returned by ToAV.

Every Redimo operation that stores data will accept the data as a Value. Some common value wrappers are provided, like StringValue, FloatValue, IntValue and BytesValue, allowing you to easily wrap the data you store.

The output of most operations is a ReturnValue which has convenience methods to decode the data into these common types. ReturnValue also implements Value so you can call ToAV to access the raw types.AttributeValue, allowing you to do custom de-serialization.

If you have a data that does not fit cleanly into one of the provide convenience wrapper types, you can implement the ToAV() method on any type to implement custom encoding. When you receive the data wrapped in a ReturnValue, the ToAV method can be used to access the raw dynamo.AttributeValue struct, allowing you to do custom decoding.

func ToValue added in v1.2.0

func ToValue(data interface{}) Value

func ToValueE added in v1.2.4

func ToValueE(data interface{}) (value Value, err error)

func ToValues added in v1.2.1

func ToValues(data []interface{}) []Value

func ToValuesE added in v1.3.2

func ToValuesE(data []interface{}) ([]Value, error)

type XID

type XID string

XID holds a stream item ID, and consists of a timestamp (one second resolution) and a sequence number.

Most code will not need to generate XIDs – using XAutoID with XADD is the most common usage. But if you do need to generate XIDs for insertion with XADD, the NewXID methods creates a complete XID.

To generate time based XIDs for time range queries with XRANGE or XREVRANGE, use NewTimeXID(startTime).First() and NewTimeXID(endTime).Last(). Calling Last() is especially important because without it none of the items in the last second of the range will match – you need the last possible sequence number in the last second of the range, which is what the Last() method provides.

const XAutoID XID = "*"
const XEnd XID = "99999999999999999999-99999999999999999999"
const XStart XID = "00000000000000000000-00000000000000000000"

func NewTimeXID

func NewTimeXID(ts time.Time) XID

NewTimeXID creates an XID with the given timestamp. To get the first or the last XID in this timestamp, use the First() or the Last() methods. This is especially important when using constructed XIDs inside a range call like XRANGE or XREVRANGE.

func NewXID

func NewXID(ts time.Time, seq uint64) XID

NewXID creates an XID with the given timestamp and sequence number.

func (XID) First

func (xid XID) First() XID

First returns the first valid XID at this timestamp. Useful for the start parameter of XRANGE or XREVRANGE.

func (XID) Last

func (xid XID) Last() XID

Last returns the last valid XID at this timestamp. Useful for the end parameter of XRANGE or XREVRANGE. Note that if the XID used as an end in the range simply based on the timestamp, the sequence number will be zero, so the query will exclude all the items in end second. This will effectively transform the query to '< endTime' instead of '<= endTime'. Using Last() prevents this mistake, if that is your intention.

func (XID) Next

func (xid XID) Next() XID

Next returns the next valid XID at the same time – it simply returns a new XID with the next sequence number.

func (XID) Prev

func (xid XID) Prev() XID

Prev returns the previous valid XID at the same time – it simply returns a new XID with the previous sequence number.

func (XID) Seq

func (xid XID) Seq() uint64

Seq returns the sequence number represented by this XID. To get the next and previous valid XIDs for range pagination, see Next() and Prev().

func (XID) String

func (xid XID) String() string

func (XID) Time

func (xid XID) Time() time.Time

Time returns the time represented by this XID, accurate to one second.

type XReadOption

type XReadOption string
const (
	XReadPending    XReadOption = "PENDING"
	XReadNew        XReadOption = "READ_NEW"
	XReadNewAutoACK XReadOption = "READ_NEW_NO_ACK"
)

type ZAggregation

type ZAggregation string
const (
	ZAggregationSum ZAggregation = "SUM"
	ZAggregationMin ZAggregation = "MIN"
	ZAggregationMax ZAggregation = "MAX"
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL