Repository: whitfin/cachex (elixir)

Issue:
Remove fallbacks from a cache state
Now that we have `Cachex.fetch/4` there's really no point in `:fallback` on a cache state. Let's remove it!

Pull request:
Remove fallback options from cache states
This fixes #371.

This will remove `:fallback` from the cache state, as well as the ability to add state to fallback functions. This simplifies handling, removes error checking in favour of guards, and makes the cache state smaller to copy.

If people require that `:state` be added back in future, this is not off the table and has been documented as such. I am fairly confident that it will not be required due to it being kinda anti-OTP (most people will have separate processes for their state, so they can just use the `:name`). 

Patch:
diff --git a/README.md b/README.md
index b299d0a..b3cdaec 100644
--- a/README.md
+++ b/README.md
@@ -38,7 +38,7 @@ children = [
 ]
 ```
 
-If you wish to start a cache manually (for example, in `iex`), you can use `Cachex.start_link/1`:
+If you wish to start a cache manually (for example, in `iex`), you can use `Cachex.start_link/2`:
 
 ```elixir
 Cachex.start_link(name: :my_cache)
@@ -68,7 +68,6 @@ Caches also accept several options at startup to toggle various behaviour. These
 |:----------------:|:------------------------:|:------------------------------------------------------------------:|
 |     commands     |      map or keyword      |       A collection of custom commands to attach to the cache.      |
 |    expiration    |      `expiration()`      |      An expiration options record imported from `Cachex.Spec`.     |
-|     fallback     | function or `fallback()` |            A fallback record imported from `Cachex.Spec`.          |
 |       hooks      |     list of `hook()`     |        A list of execution hooks to listen on cache actions.       |
 |       limit      |    a `limit()` record    |    An integer or Limit struct to define the bounds of this cache.  |
 |       stats      |          boolean         |         Whether to track statistics for this cache or not.         |
diff --git a/docs/extensions/custom-commands.md b/docs/extensions/custom-commands.md
index 58a3206..a5751cf 100644
--- a/docs/extensions/custom-commands.md
+++ b/docs/extensions/custom-commands.md
@@ -6,7 +6,7 @@ Commands operate in such a way that they're marginally quicker than hand-writing
 
 ## Defining a Command
 
-Commands are defined on a per-cache basis via the `:commands` flag inside the `Cachex.start_link/1` options.
+Commands are defined on a per-cache basis via the `:commands` flag inside the `Cachex.start_link/2` options.
 
 There are two types of command, either `:read` or `:write`. As you might guess the former will return a modified value from within a cache, while the latter will modify the value inside the cache before returning it.
 
diff --git a/docs/extensions/execution-lifecycle.md b/docs/extensions/execution-lifecycle.md
index a08393e..8251292 100644
--- a/docs/extensions/execution-lifecycle.md
+++ b/docs/extensions/execution-lifecycle.md
@@ -32,7 +32,7 @@ defmodule MyProject.MyHook do
 end
 ```
 
-Once you have your `Cachex.Hook` definition, you can attach it to a cache a startup using the `:hooks` option on the `Cachex.start_link/1` interface. This option accepts a list of `Cachex.Spec.hook` records and attaches them to the cache on launch:
+Once you have your `Cachex.Hook` definition, you can attach it to a cache a startup using the `:hooks` option on the `Cachex.start_link/2` interface. This option accepts a list of `Cachex.Spec.hook` records and attaches them to the cache on launch:
 
 ```elixir
 # need the records
diff --git a/docs/management/expiring-records.md b/docs/management/expiring-records.md
index 62e9bc2..f8381f0 100644
--- a/docs/management/expiring-records.md
+++ b/docs/management/expiring-records.md
@@ -48,7 +48,7 @@ Naturally this technique cannot stand alone as it only evicts on key retrieval;
 
 There are a number of ways to provide expirations for entries inside a cache:
 
-* Setting a default expiration for a cahe via `Cachex.start_link/1`
+* Setting a default expiration for a cahe via `Cachex.start_link/2`
 * Setting an expiration manually via `Cachex.expire/4` or `Cachex.expire_at/4`
 * Setting the `:expire` option within calls to `Cachex.put/4` or `Cachex.put_many/3`
 * Setting the `:expire` option within return tuples in `Cachex.fetch/4` or `Cachex.get_and_update/4`
diff --git a/docs/warming/proactive-warming.md b/docs/warming/proactive-warming.md
index 418c725..1a90d48 100644
--- a/docs/warming/proactive-warming.md
+++ b/docs/warming/proactive-warming.md
@@ -6,7 +6,7 @@ Warmers are deliberately easy to create, as anything complicated belongs outside
 
 ## Defining a Warmer
 
-First of all, let's define our warmer on a cache at startup. This is done by passing a list of `warmer()` records inside the `:warmers` option of `Cachex.start_link/1`:
+First of all, let's define our warmer on a cache at startup. This is done by passing a list of `warmer()` records inside the `:warmers` option of `Cachex.start_link/2`:
 
 ```elixir
 # for warmer()
diff --git a/docs/warming/reactive-warming.md b/docs/warming/reactive-warming.md
index 81f1c46..58170b9 100644
--- a/docs/warming/reactive-warming.md
+++ b/docs/warming/reactive-warming.md
@@ -6,40 +6,18 @@ Reactive warmers are very memory efficient as they lazily load data on demand, i
 
 ## Defining a Fallback
 
-Fallback functions can be defined on a cache at startup or on a call at runtime, and will only be executed if the key you're trying to retrieve doesn't exist locally. If you set a function at cache startup and then also pass one at call time, the call time definition takes precedence and will be executed instead. Each function receives a single argument by default; the key which resulted in a cache miss:
+The entry point to reactive caching is `Cachex.fetch/4`. You can pass a function to `Cachex.fetch/4`, and this function will only be executed if the key you're trying to access doesn't exist in the table. This function receives the name of the key being accessed, so you can define a common handler rather than inlining for every call.
 
 ```elixir
-# need our records
-import Cachex.Spec
-
-# initializing a fallback on a cache at startup to be used on all cache misses
-Cachex.start_link(:my_cache, [ fallback: fallback(default: &do_something/1) ])
-
-# initializing a fallback at call time to retrieve on specific cache misses
-Cachex.fetch(:my_cache, "key", &do_something/1)
+{ :ok, cache } = Cachex.start(:my_cache)
+{ :commit, 3 } = Cachex.fetch(:my_cache, "key", &String.length/1)
 ```
 
-There are also some cases in which you'll need a state to operate fallbacks, for example if you're caching responses from a database. In this case you might wish to have a database connection passed to your fallback to allow you to query it, and so Cachex allows you to pass a state at cache start to be provided as a second argument to all fallback executions.
-
-```elixir
-# need our records
-import Cachex.Spec
-
-# initializing a fallback on a cache at startup with a fallback state
-Cachex.start_link(:my_cache, [ fallback: fallback(action: &do_something/2, state: db_conn) ])
-
-# fallbacks are always provided with the state, even at call time
-Cachex.fetch(:my_cache, "key", fn(key, db_conn) ->
-  case Database.load_package(db_conn) do
-    { :ok, packages } -> { :commit, packages }
-    { :error, _reason } = error -> { :ignore, error }
-  end
-end)
-```
+When formatting results to place into the cache table, you must provide your results in the form of either `{ status, value }` or `{ status, value, options }`. These options should match the same what you'd use when calling `Cachex.put/3`, so check out the documentation if you need to.
 
-When formatting results to place into the cache table, you must provide your results in the form of either `{ status, pairs }` or `{ status, pairs, options }`. These pairs and options should match the same formats that you'd use when calling `Cachex.put_many/3`, so check out the documentation if you need to.
+In order to provide some degree of control over error handling, Cachex allows for either `:commit` or `:ignore` as a status. Values tagged with `:ignore` will just be returned without being stored in the cache, and those tagged with `:commit` will be returned after being stored in the cache. If you don't use a tagged Tuple return value, it will be assumed you're committing the value to help interoperability (as in the example above).
 
-In order to provide some degree of control over error handling, Cachex allows for either `:commit` or `:ignore` as a status. Values tagged with `:ignore` will just be returned without being stored in the cache, and those tagged with `:commit` will be returned after being stored in the cache. If you don't use a tagged Tuple return value, it will be assumed you're committing the value (for backwards compatibility).
+In previous versions of Cachex it was possible to store fallback `:state` within a cache (accessible as a second parameter to a fallback function). This has been removed as of v4.x to simplify `Cachex.fetch/4` handling and as it was a lesser used feature. It's possible this feature will be re-added in future if there is enough demand for it.
 
 ## Example Use Cases
 
@@ -51,15 +29,14 @@ The snippet below demonstrates an application using a cache to read from a remot
 # need our records
 import Cachex.Spec
 
-# initialize our cache with a database connection
+# initialize our cache with expiration set
 Cachex.start_link(:my_cache, [
   expiration: expiration(default: :timer.minutes(5))
-  fallback:   fallback(state: db_conn)
 ])
 
 # retrieve a list of packages to serve via our API
-Cachex.fetch(:my_cache, "/api/v1/packages", fn(_key, db_conn) ->
-  Database.load_packages(db_conn)
+Cachex.fetch(:my_cache, "/api/v1/packages", fn ->
+  { :commit, Repo.all(from p in Package) }
 end)
 ```
 
diff --git a/lib/cachex.ex b/lib/cachex.ex
index 8720ffd..f5a0aa8 100644
--- a/lib/cachex.ex
+++ b/lib/cachex.ex
@@ -72,7 +72,7 @@ defmodule Cachex do
     expire: [3, 4],
     expire_at: [3, 4],
     export: [1, 2],
-    fetch: [2, 3, 4],
+    fetch: [3, 4],
     get: [2, 3],
     get_and_update: [3, 4],
     import: [2, 3],
@@ -183,34 +183,6 @@ defmodule Cachex do
       Please see the `Cachex.Spec.expiration/1` documentation for further customization
       options.
 
-    * `:fallback`
-
-      The fallback option allows global settings related to the `fetch/4` command
-      on a cache. The value provided here can either be a valid `:fallback` record
-      provided by `Cachex.Spec`, or a single function (which is turned into a record
-      internally).
-
-          iex> import Cachex.Spec
-          ...>
-          ...> Cachex.start_link(:my_cache, [
-          ...>   fallback: fallback(
-          ...>     # default func to use with fetch/4
-          ...>     default: &String.reverse/1,
-          ...>
-          ...>     # anything to pass to fallbacks
-          ...>     state: { }
-          ...>   )
-          ...> ])
-          { :ok, _pid }
-
-      The `:default` function provided will be used if `fetch/2` is called, rather
-      than explicitly passing one at call time. The `:provide` function contains
-      state which can be passed to a fallback function if the arity is 2 rather than
-      1.
-
-      Please see the documentation for `fetch/4`, and the `Cachex.Spec.fallback/1`
-      documentation for further information.
-
     * `:hooks`
 
       The `:hooks` option allow the user to attach a list of notification hooks to
@@ -331,10 +303,9 @@ defmodule Cachex do
       Please see the `Cachex.Spec.warmer/1` documentation for further customization options.
 
   """
-  @spec start_link(atom | Keyword.t()) :: {atom, pid}
-  def start_link(options) when is_list(options) do
-    with {:ok, name} <- Keyword.fetch(options, :name),
-         {:ok, true} <- ensure_started(),
+  @spec start_link(atom, Keyword.t()) :: {atom, pid}
+  def start_link(name, options \\ []) when is_atom(name) and is_list(options) do
+    with {:ok, true} <- ensure_started(),
          {:ok, true} <- ensure_unused(name),
          {:ok, cache} <- Options.parse(name, options),
          {:ok, pid} = Supervisor.start_link(__MODULE__, cache, name: name),
@@ -346,23 +317,12 @@ defmodule Cachex do
     end
   end
 
-  def start_link(name) when not is_atom(name),
-    do: error(:invalid_name)
-
-  def start_link(name),
-    do: start_link(name: name)
-
-  @doc false
-  @spec start_link(atom, Keyword.t()) :: {atom, pid}
-  def start_link(name, options),
-    do: start_link([name: name] ++ options)
-
   @doc """
   Creates a new Cachex cache service tree.
 
   This will not link the cache to the current process, so if your process dies
   the cache will continue to live. If you don't want this behaviour, please use
-  the provided `start_link/2`.
+  the provided `Cachex.start_link/2`.
 
   This function is otherwise identical to `start_link/2` so please see that
   documentation for further information and configuration.
@@ -415,9 +375,9 @@ defmodule Cachex do
   @doc """
   Retrieves the number of unexpired records in a cache.
 
-  Unlike `size/2`, this ignores keys which should have expired. Due
+  Unlike `Cachex.size/2`, this ignores keys which should have expired. Due
   to this taking potentially expired keys into account, it is far more
-  expensive than simply calling `size/2` and should only be used when
+  expensive than simply calling `Cachex.size/2` and should only be used when
   the distinction is completely necessary.
 
   ## Examples
@@ -693,19 +653,11 @@ defmodule Cachex do
       { :commit, "seripxe_yek_gnissim", [expire: 60000] }
 
   """
-  @spec fetch(Cachex.t(), any, function | nil, Keyword.t()) ::
+  @spec fetch(Cachex.t(), any, function(), Keyword.t()) ::
           {status | :commit | :ignore, any} | {:commit, any, any}
-  def fetch(cache, key, fallback \\ nil, options \\ []) when is_list(options) do
-    Overseer.with(cache, fn cache ->
-      case fallback || fallback(cache(cache, :fallback), :default) do
-        val when is_function(val) ->
-          Router.route(cache, {:fetch, [key, val, options]})
-
-        _na ->
-          error(:invalid_fallback)
-      end
-    end)
-  end
+  def fetch(cache, key, fallback, options \\ [])
+      when is_function(fallback) and is_list(options),
+      do: Router.route(cache, {:fetch, [key, fallback, options]})
 
   @doc """
   Retrieves an entry from a cache.
@@ -753,7 +705,7 @@ defmodule Cachex do
   @spec get_and_update(Cachex.t(), any, function, Keyword.t()) ::
           {:commit | :ignore, any}
   def get_and_update(cache, key, updater, options \\ [])
-      when is_function(updater) and is_list(options),
+      when is_function(updater, 1) and is_list(options),
       do: Router.route(cache, {:get_and_update, [key, updater, options]})
 
   @doc """
@@ -1317,8 +1269,7 @@ defmodule Cachex do
   """
   @spec transaction(Cachex.t(), [any], function, Keyword.t()) :: {status, any}
   def transaction(cache, keys, operation, options \\ [])
-      when (is_function(operation) or is_function(operation, 1)) and
-             is_list(keys) and is_list(options) do
+      when is_function(operation) and is_list(keys) and is_list(options) do
     Overseer.with(cache, fn cache ->
       trans_cache =
         case cache(cache, :transactions) do
diff --git a/lib/cachex/actions/fetch.ex b/lib/cachex/actions/fetch.ex
index 818ac9f..72d27f7 100644
--- a/lib/cachex/actions/fetch.ex
+++ b/lib/cachex/actions/fetch.ex
@@ -31,7 +31,7 @@ defmodule Cachex.Actions.Fetch do
   """
   def execute(cache() = cache, key, fallback, _options) do
     with {:ok, nil} <- Get.execute(cache, key, []) do
-      Courier.dispatch(cache, key, generate_task(cache, fallback, key))
+      Courier.dispatch(cache, key, generate_task(fallback, key))
     end
   end
 
@@ -40,15 +40,10 @@ defmodule Cachex.Actions.Fetch do
   ###############
 
   # Generates a courier task based on the arity of the fallback function.
-  #
-  # If only a single argument is expected, the key alone is passed through. For
-  # any other arity, we pass through the key and the default state (which can
-  # be nil). This will therefore crash if the provided arity is invalid.
-  defp generate_task(cache(fallback: fallback(state: state)), fallback, key) do
+  defp generate_task(fallback, key) do
     case :erlang.fun_info(fallback)[:arity] do
       0 -> fn -> fallback.() end
       1 -> fn -> fallback.(key) end
-      _ -> fn -> fallback.(key, state) end
     end
   end
 end
diff --git a/lib/cachex/actions/import.ex b/lib/cachex/actions/import.ex
index 03db61e..7b21dea 100644
--- a/lib/cachex/actions/import.ex
+++ b/lib/cachex/actions/import.ex
@@ -22,6 +22,10 @@ defmodule Cachex.Actions.Import do
   def execute(cache() = cache, entries, _options),
     do: {:ok, Enum.reduce(entries, 0, &import(cache, &1, &2, now()))}
 
+  ###############
+  # Private API #
+  ###############
+
   # Imports an entry directly when no TTL is included.
   #
   # As this is a direct import, we just use `Cachex.put/4` with the provided
diff --git a/lib/cachex/actions/restore.ex b/lib/cachex/actions/restore.ex
index f18af07..e61ae9f 100644
--- a/lib/cachex/actions/restore.ex
+++ b/lib/cachex/actions/restore.ex
@@ -47,6 +47,10 @@ defmodule Cachex.Actions.Restore do
     File.Error -> error(:unreachable_file)
   end
 
+  ###############
+  # Private API #
+  ###############
+
   # Read the next term from a file handle cbased on the TLV flags. Each
   # term should be emitted back to the parent stream for processing.
   defp read_next_term(file, options) do
diff --git a/lib/cachex/actions/save.ex b/lib/cachex/actions/save.ex
index 30aec99..27ab9e5 100644
--- a/lib/cachex/actions/save.ex
+++ b/lib/cachex/actions/save.ex
@@ -48,6 +48,10 @@ defmodule Cachex.Actions.Save do
     File.Error -> error(:unreachable_file)
   end
 
+  ###############
+  # Private API #
+  ###############
+
   # Use a local stream to lazily walk through records on a local cache.
   defp init_stream(local, router, cache, batch) when local or router == Local,
     do: CachexStream.execute(cache, Query.build(), batch_size: batch)
diff --git a/lib/cachex/errors.ex b/lib/cachex/errors.ex
index 661e8a6..85271fb 100644
--- a/lib/cachex/errors.ex
+++ b/lib/cachex/errors.ex
@@ -15,11 +15,9 @@ defmodule Cachex.Errors do
     :cross_slot,
     :invalid_command,
     :invalid_expiration,
-    :invalid_fallback,
     :invalid_hook,
     :invalid_limit,
     :invalid_match,
-    :invalid_name,
     :invalid_option,
     :invalid_pairs,
     :invalid_router,
@@ -75,9 +73,6 @@ defmodule Cachex.Errors do
   def long_form(:invalid_expiration),
     do: "Invalid expiration definition provided"
 
-  def long_form(:invalid_fallback),
-    do: "Invalid fallback function provided"
-
   def long_form(:invalid_hook),
     do: "Invalid hook definition provided"
 
@@ -87,9 +82,6 @@ defmodule Cachex.Errors do
   def long_form(:invalid_match),
     do: "Invalid match specification provided"
 
-  def long_form(:invalid_name),
-    do: "Invalid cache name provided"
-
   def long_form(:invalid_option),
     do: "Invalid option syntax provided"
 
diff --git a/lib/cachex/options.ex b/lib/cachex/options.ex
index bd53786..f6d371e 100644
--- a/lib/cachex/options.ex
+++ b/lib/cachex/options.ex
@@ -22,7 +22,6 @@ defmodule Cachex.Options do
     :router,
     :ordered,
     :commands,
-    :fallback,
     :compressed,
     :expiration,
     :transactions,
@@ -193,38 +192,6 @@ defmodule Cachex.Options do
     end
   end
 
-  # Sets up any cache-wide fallback behaviour.
-  #
-  # This will allow the shorthanding of a function to act as a default
-  # fallback implementation; otherwise the provided value must be a
-  # fallback record which is run through the specification validation.
-  defp parse_type(:fallback, cache, options) do
-    fallback =
-      transform(options, :fallback, fn
-        # provided fallback is great!
-        fallback() = fallback ->
-          fallback
-
-        # allow shorthand of a function
-        fun when is_function(fun) ->
-          fallback(default: fun)
-
-        # unset so default
-        nil ->
-          fallback()
-
-        # anything else, no thanks!
-        _invalid ->
-          nil
-      end)
-
-    # validate using the spec validator
-    case Validator.valid?(:fallback, fallback) do
-      false -> error(:invalid_fallback)
-      true -> cache(cache, fallback: fallback)
-    end
-  end
-
   # Configures any hooks to be enabled for the cache.
   #
   # In addition to the hooks already provided, this will also deal with the
diff --git a/lib/cachex/spec.ex b/lib/cachex/spec.ex
index 228722f..eb695b6 100644
--- a/lib/cachex/spec.ex
+++ b/lib/cachex/spec.ex
@@ -38,7 +38,6 @@ defmodule Cachex.Spec do
             commands: map,
             compressed: boolean,
             expiration: expiration,
-            fallback: fallback,
             hooks: hooks,
             limit: limit,
             ordered: boolean,
@@ -74,13 +73,6 @@ defmodule Cachex.Spec do
             lazy: boolean
           )
 
-  # Record specification for a cache fallback
-  @type fallback ::
-          record(:fallback,
-            default: (any -> any) | (any, any -> any),
-            state: any
-          )
-
   # Record specification for a cache hook
   @type hook ::
           record(:hook,
@@ -139,7 +131,6 @@ defmodule Cachex.Spec do
     commands: %{},
     compressed: false,
     expiration: nil,
-    fallback: nil,
     hooks: nil,
     limit: nil,
     ordered: false,
@@ -201,17 +192,6 @@ defmodule Cachex.Spec do
     interval: 3000,
     lazy: true
 
-  @doc """
-  Creates a fallback record from the provided values.
-
-  A fallback can consist of a nillable state to provide to a fallback definition when
-  requested (via a fallback with an arity of 2). If a default action is provided, it
-  should be a function of arity 1 or 2, depending on if it requires the state or not.
-  """
-  defrecord :fallback,
-    default: nil,
-    state: nil
-
   @doc """
   Creates a hook record from the provided values.
 
@@ -323,12 +303,6 @@ defmodule Cachex.Spec do
   @spec expiration(expiration, Keyword.t()) :: expiration
   defmacro expiration(record, args)
 
-  @doc """
-  Updates a fallback record from the provided values.
-  """
-  @spec fallback(fallback, Keyword.t()) :: fallback
-  defmacro fallback(record, args)
-
   @doc """
   Updates a hook record from the provided values.
   """
diff --git a/lib/cachex/spec/validator.ex b/lib/cachex/spec/validator.ex
index ab96929..82438a6 100644
--- a/lib/cachex/spec/validator.ex
+++ b/lib/cachex/spec/validator.ex
@@ -15,7 +15,6 @@ defmodule Cachex.Spec.Validator do
           Cachex.Spec.command()
           | Cachex.Spec.entry()
           | Cachex.Spec.expiration()
-          | Cachex.Spec.fallback()
           | Cachex.Spec.hook()
           | Cachex.Spec.hooks()
           | Cachex.Spec.limit()
@@ -62,13 +61,6 @@ defmodule Cachex.Spec.Validator do
     check3
   end
 
-  # Validates a fallback specification record.
-  #
-  # At this point it just needs to verify that the default value is a valid
-  # function. We can only support functions with arity 1 or 2.
-  def valid?(:fallback, fallback(default: default)),
-    do: nillable?(default, &(is_function(&1, 1) or is_function(&1, 2)))
-
   # Validates a hook specification record.
   #
   # This validation will ensure the following:


Test patch:
diff --git a/test/cachex/actions/fetch_test.exs b/test/cachex/actions/fetch_test.exs
index 7089f17..189626d 100644
--- a/test/cachex/actions/fetch_test.exs
+++ b/test/cachex/actions/fetch_test.exs
@@ -9,21 +9,12 @@ defmodule Cachex.Actions.FetchTest do
     # create a forwarding hook
     hook = ForwardHook.create()
 
-    # create a default fetch action
-    concat = &(&1 <> "_" <> &2)
-
     # create a test cache
-    cache1 = TestUtils.create_cache(hooks: [hook])
-
-    cache2 =
-      TestUtils.create_cache(
-        hooks: [hook],
-        fallback: fallback(state: "val", default: concat)
-      )
+    cache = TestUtils.create_cache(hooks: [hook])
 
     # set some keys in the cache
-    {:ok, true} = Cachex.put(cache1, "key1", 1)
-    {:ok, true} = Cachex.put(cache1, "key2", 2, expire: 1)
+    {:ok, true} = Cachex.put(cache, "key1", 1)
+    {:ok, true} = Cachex.put(cache, "key2", 2, expire: 1)
 
     # wait for the TTL to pass
     :timer.sleep(2)
@@ -38,8 +29,8 @@ defmodule Cachex.Actions.FetchTest do
     fb_opt4 = fn -> "6yek" end
 
     # fetch the first and second keys
-    result1 = Cachex.fetch(cache1, "key1", fb_opt1)
-    result2 = Cachex.fetch(cache1, "key2", fb_opt1)
+    result1 = Cachex.fetch(cache, "key1", fb_opt1)
+    result2 = Cachex.fetch(cache, "key2", fb_opt1)
 
     # verify fetching an existing key
     assert(result1 == {:ok, 1})
@@ -48,10 +39,10 @@ defmodule Cachex.Actions.FetchTest do
     assert(result2 == {:commit, "2yek"})
 
     # fetch keys with a provided fallback
-    result3 = Cachex.fetch(cache1, "key3", fb_opt1)
-    result4 = Cachex.fetch(cache1, "key4", fb_opt2)
-    result5 = Cachex.fetch(cache1, "key5", fb_opt3)
-    result6 = Cachex.fetch(cache1, "key6", fb_opt4)
+    result3 = Cachex.fetch(cache, "key3", fb_opt1)
+    result4 = Cachex.fetch(cache, "key4", fb_opt2)
+    result5 = Cachex.fetch(cache, "key5", fb_opt3)
+    result6 = Cachex.fetch(cache, "key6", fb_opt4)
 
     # verify the fallback fetches
     assert(result3 == {:commit, "3yek"})
@@ -59,12 +50,6 @@ defmodule Cachex.Actions.FetchTest do
     assert(result5 == {:ignore, "5yek"})
     assert(result6 == {:commit, "6yek"})
 
-    # test using a default fallback state
-    result7 = Cachex.fetch(cache2, "key7")
-
-    # verify that it executes and ignores state
-    assert(result7 == {:commit, "key7_val"})
-
     # assert we receive valid notifications
     assert_receive({{:fetch, ["key1", ^fb_opt1, []]}, ^result1})
     assert_receive({{:fetch, ["key2", ^fb_opt1, []]}, ^result2})
@@ -72,15 +57,14 @@ defmodule Cachex.Actions.FetchTest do
     assert_receive({{:fetch, ["key4", ^fb_opt2, []]}, ^result4})
     assert_receive({{:fetch, ["key5", ^fb_opt3, []]}, ^result5})
     assert_receive({{:fetch, ["key6", ^fb_opt4, []]}, ^result6})
-    assert_receive({{:fetch, ["key7", ^concat, []]}, ^result7})
 
     # check we received valid purge actions for the TTL
     assert_receive({{:purge, [[]]}, {:ok, 1}})
 
     # retrieve the loaded keys
-    value1 = Cachex.get(cache1, "key3")
-    value2 = Cachex.get(cache1, "key4")
-    value3 = Cachex.get(cache1, "key5")
+    value1 = Cachex.get(cache, "key3")
+    value2 = Cachex.get(cache, "key4")
+    value3 = Cachex.get(cache, "key5")
 
     # committed keys should now exist
     assert(value1 == {:ok, "3yek"})
@@ -88,14 +72,6 @@ defmodule Cachex.Actions.FetchTest do
 
     # ignored keys should not exist
     assert(value3 == {:ok, nil})
-
-    # check using a missing fallback
-    result8 = Cachex.fetch(cache1, "key7")
-    result9 = Cachex.fetch(cache1, "key8", "val")
-
-    # both should be an error for invalid function
-    assert(result8 == {:error, :invalid_fallback})
-    assert(result9 == {:error, :invalid_fallback})
   end
 
   # This test ensures that the fallback is executed just once when a
diff --git a/test/cachex/errors_test.exs b/test/cachex/errors_test.exs
index 81cce81..1d27411 100644
--- a/test/cachex/errors_test.exs
+++ b/test/cachex/errors_test.exs
@@ -9,11 +9,9 @@ defmodule Cachex.ErrorsTest do
       cross_slot: "Target keys do not live on the same node",
       invalid_command: "Invalid command definition provided",
       invalid_expiration: "Invalid expiration definition provided",
-      invalid_fallback: "Invalid fallback function provided",
       invalid_hook: "Invalid hook definition provided",
       invalid_limit: "Invalid limit fields provided",
       invalid_match: "Invalid match specification provided",
-      invalid_name: "Invalid cache name provided",
       invalid_option: "Invalid option syntax provided",
       invalid_pairs: "Invalid insertion pairs provided",
       invalid_router: "Invalid router definition provided",
diff --git a/test/cachex/options_test.exs b/test/cachex/options_test.exs
index 6c0b674..6490314 100644
--- a/test/cachex/options_test.exs
+++ b/test/cachex/options_test.exs
@@ -199,56 +199,6 @@ defmodule Cachex.OptionsTest do
     assert msg == :invalid_expiration
   end
 
-  # Every cache can have a default fallback implementation which is used in case
-  # of no fallback provided against cache reads. The only constraint here is that
-  # the provided value is a valid function (of any arity).
-  test "parsing :fallback flags" do
-    # grab a cache name
-    name = TestUtils.create_name()
-
-    # define our falbacks
-    fallback1 = fallback()
-    fallback2 = fallback(default: &String.reverse/1)
-    fallback3 = fallback(default: &String.reverse/1, state: {})
-    fallback4 = fallback(state: {})
-    fallback5 = &String.reverse/1
-    fallback6 = {}
-
-    # parse all the valid fallbacks into caches
-    {:ok, cache(fallback: fallback1)} =
-      Cachex.Options.parse(name, fallback: fallback1)
-
-    {:ok, cache(fallback: fallback2)} =
-      Cachex.Options.parse(name, fallback: fallback2)
-
-    {:ok, cache(fallback: fallback3)} =
-      Cachex.Options.parse(name, fallback: fallback3)
-
-    {:ok, cache(fallback: fallback4)} =
-      Cachex.Options.parse(name, fallback: fallback4)
-
-    {:ok, cache(fallback: fallback5)} =
-      Cachex.Options.parse(name, fallback: fallback5)
-
-    {:error, msg} = Cachex.Options.parse(name, fallback: fallback6)
-
-    # the first should use defaults
-    assert(fallback1 == fallback())
-
-    # the second and fifth should have an action but no state
-    assert(fallback2 == fallback(default: &String.reverse/1))
-    assert(fallback5 == fallback(default: &String.reverse/1))
-
-    # the third should have both an action and state
-    assert(fallback3 == fallback(default: &String.reverse/1, state: {}))
-
-    # the fourth should have a state but no action
-    assert(fallback4 == fallback(state: {}))
-
-    # an invalid fallback should actually fail
-    assert(msg == :invalid_fallback)
-  end
-
   # This test will ensure that we can parse Hook values successfully. Hooks can
   # be provided as either a List or a single Hook. We also need to check that
   # Hooks are grouped into the correct pre/post groups inside the state.
diff --git a/test/cachex/spec/validator_test.exs b/test/cachex/spec/validator_test.exs
index 52663c8..47249a4 100644
--- a/test/cachex/spec/validator_test.exs
+++ b/test/cachex/spec/validator_test.exs
@@ -94,28 +94,6 @@ defmodule Cachex.Spec.ValidatorTest do
     refute Validator.valid?(:expiration, expiration8)
   end
 
-  test "validation of fallback records" do
-    # define some valid records
-    fallback1 = fallback(default: nil, state: nil)
-    fallback2 = fallback(default: nil, state: " ")
-    fallback3 = fallback(default: fn _ -> nil end, state: nil)
-    fallback4 = fallback(default: fn _, _ -> nil end, state: nil)
-
-    # ensure all records are valid
-    assert Validator.valid?(:fallback, fallback1)
-    assert Validator.valid?(:fallback, fallback2)
-    assert Validator.valid?(:fallback, fallback3)
-    assert Validator.valid?(:fallback, fallback4)
-
-    # define some invalid records
-    fallback5 = fallback(default: " ", state: nil)
-    fallback6 = fallback(default: fn -> nil end, state: nil)
-
-    # ensure all records are invalid
-    refute Validator.valid?(:fallback, fallback5)
-    refute Validator.valid?(:fallback, fallback6)
-  end
-
   test "validation of hook records" do
     # define some valid records
     hook1 = hook(module: :validator_hook_pre)
diff --git a/test/cachex/spec_test.exs b/test/cachex/spec_test.exs
index b35b386..174d7eb 100644
--- a/test/cachex/spec_test.exs
+++ b/test/cachex/spec_test.exs
@@ -7,9 +7,6 @@ defmodule Cachex.SpecTest do
   test "default entry record values",
     do: assert(entry() == {:entry, nil, nil, nil, nil})
 
-  test "default fallback record values",
-    do: assert(fallback() == {:fallback, nil, nil})
-
   test "default expiration record values",
     do: assert(expiration() == {:expiration, nil, 3000, true})
 
diff --git a/test/cachex_test.exs b/test/cachex_test.exs
index 509f9f4..09dbe17 100644
--- a/test/cachex_test.exs
+++ b/test/cachex_test.exs
@@ -92,17 +92,6 @@ defmodule CachexTest do
     assert(reason == :not_started)
   end
 
-  # This test does a simple check that a cache must be started with a valid atom
-  # cache name, otherwise an error is raised (an ArgumentError). The error should
-  # be a shorthand atom which can be used to debug what the issue was.
-  test "cache start with invalid cache name" do
-    # try to start the cache with an invalid name
-    {:error, reason} = Cachex.start_link("fake_name")
-
-    # we should've received an atom warning
-    assert(reason == :invalid_name)
-  end
-
   # This test ensures that we handle option parsing errors gracefully. If anything
   # goes wrong when parsing options, we exit early before starting the cache to
   # avoid bloating the Supervision tree.
@@ -177,7 +166,7 @@ defmodule CachexTest do
     assert(is_even(length(definitions)))
 
     # verify the size to cause errors on addition/removal
-    assert(length(definitions) == 148)
+    assert(length(definitions) == 146)
 
     # validate all definitions
     for {name, arity} <- definitions do