Releases: albrow/zoom
Version 0.13.1
This release fixes a critical bug in UpdateFields
which caused fields other than those specified to be updated. There were tests written for this feature, but unfortunately there was also a subtle bug in the tests which prevented this issue from being discovered. I have also updated the test so that this does not happen again.
Update urgency is high, and this release is 100% backwards compatible with version 0.13.0.
Full Changelog
- Fixed a critical bug in
UpdateFields
Version 0.13.0
This version introduces some new features which make Zoom more flexible without sacrificing simplicity. It is 100% backwards compatible with versions 0.12.x and 0.11.x.
There is a new FindFields
method on Collection
and Transaction
which works similarly to UpdateFields
. It allows you to efficiently find and set only certain fields of a model. See the README for example usage.
Zoom now exposes the fallback encoding used to encode objects which cannot easily be converted to bytes. You can choose the fallback encoding by setting the FallbackMarshalerUnmarshaler
option in CollectionOptions
. By default, Zoom will use GobMarshalerUnmarshaler
, which uses the builtin gob package. This is the same encoding that has always been used by default. Now, Zoom also provides an alternative implementation out of the box, JSONMarshalerUnmarshaler
, which as you might have guessed uses the builtin json package. You can also provide your own implementation by implementing the MarshalerUnmarshaler
interface:
type MarshalerUnmarshaler interface {
// Marshal returns a byte-encoded representation of v
Marshal(v interface{}) ([]byte, error)
// Unmarshal parses byte-encoded data and store the result in the value
// pointed to by v.
Unmarshal(data []byte, v interface{}) error
}
Full Changelog
- Added a new
FindFields
method toCollection
andTransaction
. - Added a new
FallbackMarshalerUnmarshaler
option toCollectionOptions
. - Created two out of the box implementations of
MarshalerUnmarshaler
:GobMarshalerUnmarshaler
(the default) andJSONMarshalerUnmarshaler
.
Version 0.12.1
This version fixes a bug that occurred in Find
and Save
when certain types of field values were nil. It is 100% backwards compatible with version 0.12.0 and 0.11.0. Specifically, the bug is known to have occurred with map
, slice
, and interface
values.
Full Changelog
- Fix a bug that occurred when certain types of fields were nil
Version 0.12.0
This version fixes bugs and introduces some important new features. It is 100% backwards compatible with v0.11.0.
Previous versions of Zoom erroneously saved unexported fields in Redis. This wasn't the documented behavior and the fields were not retrievable because the reflect package does not allow you to set unexported fields. The bug has been fixed and this version only saves exported fields.
A new method, UpdateFields
has been added to Collection
and Transaction
. It allows you to only update certain fields in the model instead of overwriting them all. UpdateFields
uses "last write wins" semantics, and fields that are not specified will be left unchanged.
In some cases redis.ErrNil
was being returned where the documentation stated a zoom.ModelNotFoundError
should be returned. The bug has been fixed and zoom.ModelNotFoundError
is now returned whenever appropriate.
I've also added Circle CI integration as an extra safety measure to make sure the tests are always passing on the master branch. It will also make it easier to contribute since the tests will be run automatically when you submit a PR. There's a new Circle CI badge in the README :)
Transaction.Delete
and Transaction.DeleteAll
now accept nil
for the final argument. If you don't care whether or not a model was deleted or about the number of models deleted, you can just pass in nil
. Previously, you would still need to create a new variable and pass a pointer to those methods even if the value was never used.
One final note - I have decided to release version 1.0 sooner than I had previously planned. Some features which were intended for version 1.0 will be put off until later versions. The value of reaching version 1.0 sooner is that Zoom can begin to use proper semantic versioning. (Prior to version 1.0 semantic versioning allows any breaking changes, whereas after 1.0 breaking changes must be indicated by releasing a new major version). Truthfully, I feel that Zoom is almost ready for production environments and will soon deserve the connotation of a 1.0 release!
Full Changelog
- Fixed bug where unexporeted fields were being saved in Redis.
- Implemented a new
UpdateFields
method. zoom.ModelNotFoundError
is returned in places whereredis.ErrNil
was mistakenly being returned.- Added CircleCI integration
Transaction.Delete
andTransaction.DeleteAll
now acceptnil
for the final argument.
Version 0.11.0
This version introduces some backwards-incompatible naming changes that make zoom more intuitive and consistent. The big one is that ModelType
has been renamed to Collection
. Relatedly, instead of "registering a model type" with Register
or RegisterName
, you now should "create a new collection" with the NewCollection
method. This change makes talking about zoom much easier, because there is no longer any confusion between the ModelType
type and the specific concrete type of some Model
.
NewCollection
takes two arguments, a Model
and a CollectionOptions
. CollectionOptions
works similarly to PoolOptions
. Whereas in the previous version you would specify a custom name for a ModelType
using the RegisterName
method, you would now specify a custom name by passing in a CollectionOptions
with the Name field set. Having a struct of options makes the API more future-compatible, as it is easy to add new options to collections.
Speaking of which, there is a new option, CollectionOptions.Index
which was not present in earlier versions. If Index
is true, the id of every model you save for the collection will be added to a set, identified by Collection.IndexKey
. That set will be used in queries, as well as the FindAll
, Count
, and DeleteAll
methods. We felt this was an important change to enable zoom to be used in projects that don't require any querying functionality. It is also now safe to manually issue a redis EXPIRE
command to expire the key corresponding to a particular model, iff CollectionOptions.Index
is false. Previously, using EXPIRE
on the model key would create garbage in redis because the model id would still be indexed.
_IMPORTANT_: If you want to use queries, or the FindAll
, Count
, and DeleteAll
methods, you must explicitly set CollectionOptions.Index
to true. By default, it is false and you will not be able to use that functionality (zoom will return a readable error if you call any unsupported methods). In the future, zoom may use an alternative implementation that uses pattern matches on keys. In effect, you would still be able use those methods, but they would just be a lot slower than with an indexed collection.
Full Changelog
ModelType
has been renamedCollection
.Register
andRegisterName
have been replaced byNewCollection
.- The old
AllIndexMethod
ofModelType
is nowCollection.IndexKey
- There is a new type,
CollectionOptions
for configuring collections. - In addition to
CollectionOptions.Name
(for which there was equivalent functionality in previous versions), there is a new optionCollectionOptions.Index
which lets you control whether or not model ids are indexed in a set for each collection. PoolConfig
has been renamed toPoolOptions
for consistency.- In previous versions,
Query.Run
and other query finishers did not always return errors that occurred when constructing the query. This bug has been fixed and all errors are returned. - Parts of the README have been improved.
Version 0.10.0
This release introduces the ability to have multiple pools connected to multiple databases. It was motivated by #16. There are some minor breaking changes.
In older versions, you would connect to a redis database using zoom.Init
. Now you should use zoom.NewPool
instead:
pool = zoom.NewPool(nil)
defer func() {
if err := pool.Close(); err != nil {
// handle error
}
}()
NewPool
takes *zoom.PoolConfig
as an argument, which is identical to *zoom.Config
in older versions.
In addition, some functions which used to be in the global namespace are now methods on Pool
. For example: Pool.Register
and Pool.NewTransaction
.
Each pool manages its own set of models and its own connections. If you want to share a model type between different pools, you will need to call Register
for each pool.
Full Changelog
- Added support for multiple pools.
zoom.Init
has been replaced withzoom.NewPool
, which returns aPool
object.Register
,RegisterName
,NewTransaction
, andNewConn
are now methods onPool
.- Small changes to README.
- Created a CONTRIBUTING.md file with detailed instructions for contributing.
Version 0.9.3
This release changes the way lua scripts are parsed internally. Previous versions had problems with people who were using multiple paths in GOPATH or were using certain dependency management tools. See #9, #10, and #11. This also makes Zoom safe for inclusion in binary executables (e.g. CLIs) without having to include the scripts folder.
There is one small breaking change. Because scripts are no longer loaded from files, Init
does not return an error.
Full Changelog
- Change the way lua scripts are loaded for greater compatibility.
Init
does not return an error.
Version 0.9.2
This release brings some improvements to the way ids work. In short, id generation is now less likely to produce collisions, and it uses base58 encoding to result in shorter ids (compared to the base36 encoding previously used for some components) which are also easier for humans to use.
In addition, there are also some minor breaking changes: some exported structs and methods that have to do with ids have been renamed. See the full changelog below for more information.
Decreased Likelihood of Collisions
Ids generated by Zoom are now much less likely to result in collisions. Whereas the old implementation used only two components (unix time and a generated sequence of 16 characters) ids generated with the new implementation now consist of 4 components:
- The current UTC unix time with second precision
- An atomic counter which is always 4 characters long and cycles
through the range of 0 to 11,316,495 - A unique hardware identifier based on the MAC address of the
current machine - A pseudo-randomly generated sequence of 6 characters
Collisions are still possible, but are incredibly unlikely. In order for a collision to occur, all of the following would need to happen:
- On a single machine (or between machines with MAC addresses which result in the same crc32 checksum), at least 11,316,496 models would need to be saved within a time period of one second. If this does not happen, collisions are impossible.
- In addition, one or more of the models created during this time period would need to result in a collision of the randomly generated 6 base58 characters. This character sequence is produced via the crypto/rand package, which reads from dev/urandom when possible. There are 58^6 = 38,068,692,544 possible values, so collisions are extremely unlikely.
Performance Impact
The function which generates ids is roughly 2-2.4x slower, but the effect is largely negligible compared to network latency and other performance factors. The trade-off is one that I believe makes sense, as in some applications id collisions could be really harmful.
Breaking Changes
The embeddable struct DefaultData
has been renamed to RandomId
to better reflect its behavior. This also opens the door for other id implementations to be included in the future (e.g. AutoIncrementedId
).
The Id
field of RandomId
is now exported. The motivation for this change was to make models with an embedded RandomId
more easily serializable. For example, with the old implementation, the id
field was not included if you serialized a model to json.
The methods of the Model
interface have been renamed to ModelId
and SetModelId
. This is more idiomatic in go and was necessitated by the fact that the Id
field of RandomId
is now exported.
Id generation now happens the first time you call ModelId
as opposed to during the Save
method. The reason for this change was to allow for other id implementations to be easily dropped in. Without this behavior, we would need to do something special during the Save
method depending on which type of struct was embedded, which adds undesirable complexity and makes custom implementations nearly impossible. This way, the current implementation, any future implementations provided by Zoom, and any custom implementations all work the same way.
Ids generated using the old implementation will still work fine, so changes related to the way ids are generated are fully backwards-compatible.
Full Changelog
DefaultData
has been renamed toRandomId
.- The
Id
field ofRandomId
is now exported. - The methods of the
Model
interface have been renamed toModelId
andSetModelId
. - Id generation is now less likely to produce collisions.
- Id generation now happens the first time you call
ModelId
as opposed to during theSave
method. - Tests now catch a previously uncaught error when calling the
Init
function. - Generated ids now use base58 encoding.
Version 0.9.1
This release changes the way that scripts are initialized. Instead of an automatically run init
function in the scripts.go file, scripts are now initialized in an initScripts
function which is called inside of Init
. As a result, there is a small backwards-incompatible change. Both Init
and Close
now return an error.
The reason for this change is that with github.com/soroushjp/humble, we want to be able to share models across the server and client. On the client, we don't want to initialize scripts (or use Zoom at all). So this change makes it so that you can use Zoom-compatible models with gopherjs without any problems. Zoom will only attempt to load scripts if you call zoom.Init
.
Full Changelog:
- Rename the
init
function in scripts.go toinitScripts
and call it inside ofInit
. - Both
Init
andClose
now return errors.
Version 0.9.0
This version comes after a complete rewrite of all code and has been more than a month in the making. Everything has been rewritten from the bottom up to be more efficient and easier to use. The major outcomes of this release are improved syntax and increased performance and safety.
The best example of improved performance is in the ComplexQuery benchmark. The old version relied on multiple round trips and performed intersection of filtered sets client-side in go. The new version uses Redis transactions and lua scripting to do the entire query in one round trip. The result is a performance increase from 1672648 ns/op to 142476 ns/op, which is nearly a 12x performance increase! Those numbers are based on connecting to Redis with a unix socket connection on the same machine. For setups where you're connecting to Redis over the network, the performance increase will be even greater due to the greatly reduced round-trip latency.
This is the first version in a long time to break backwards compatibility in a non-trivial way. As such, I have written a migration guide to help those who are migrating from older versions of zoom. The only major features that have been removed are relationships and thread-safe updates via the embedded Sync object. However the new Transactions feature can be used to replace this functionality, albeit with a little extra work. Future versions of Zoom will make replacing both features even easier via callbacks (e.g. BeforeSave, AfterFind), a set of common ReplyHandlers, and optimistic locking for thread-safe updates. If you are dependent on relationships or thread-safe updates, it might be a good idea to wait before migrating to the latest version.
There are almost too many changes to count, but I'll try my best to list all of them.
Full Changelog:
- Relationship support has been fully removed.
- All transactions are atomic and use lua scripts when necessary.
- The Transaction type and related functions/methods have been exported. Transactions replace the old functions with the "M" prefix, such as MFindById, MDelete, and MSave.
- Register and RegisterName now return a *ModelType
- All basic operations which used to be functions (e.g. Save, FindById, Delete) are now methods on *ModelType and some have been renamed.
- The NewQuery constructor is now a method on *ModelType.
- The query finishers have been renamed and refactored.
- String indexes now use the NULL character as a separator instead of space, and as such the NULL character cannot be included in indexed string values. Neither can the DEL character.
- The Configuration struct now has a Password field, which is the new way to authenticate with password-protected databases.
- Reduced memory footprint.
- There are now methods for getting various keys that Zoom uses for storing and indexing things in Redis.
- GetConn has been renamed to NewConn.
- GetId in the Model interface has been renamed to Id.
- Fixed several subtle bugs having to do with string indexes.