changed CHANGELOG.md
 
@@ -4,6 4,29 @@ All notable changes to this project will be documented in this file.
4
4
5
5
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
7
## [v2.5.2](https://github.com/cabol/nebulex/tree/v2.5.2) (2023-07-14)
8
9
[Full Changelog](https://github.com/cabol/nebulex/compare/v2.5.1...v2.5.2)
10
11
**Closed issues:**
12
13
- Replicated adapter syncing during rolling deployment.
14
[#209](https://github.com/cabol/nebulex/issues/209)
15
- Ambiguity regarding ttl and `gc_interval` relation.
16
[#208](https://github.com/cabol/nebulex/issues/208)
17
- Seeing Nebulex.RPCError during deployments with partitioned adapter.
18
[#206](https://github.com/cabol/nebulex/issues/206)
19
- Random `:erpc`, `:timeout` with partitioned get.
20
[#202](https://github.com/cabol/nebulex/issues/202)
21
- Processes reading from cache blocked by generational gc process.
22
[#197](https://github.com/cabol/nebulex/issues/197)
23
24
**Merged pull requests:**
25
26
- Delay flushing ets table to avoid blocking processes using it.
27
[#210](https://github.com/cabol/nebulex/pull/210)
28
([szajbus](https://github.com/szajbus))
29
7
30
## [v2.5.1](https://github.com/cabol/nebulex/tree/v2.5.1) (2023-05-27)
8
31
9
32
[Full Changelog](https://github.com/cabol/nebulex/compare/v2.5.0...v2.5.1)
changed hex_metadata.config
 
@@ -59,4 59,4 @@
59
59
{<<"optional">>,true},
60
60
{<<"repository">>,<<"hexpm">>},
61
61
{<<"requirement">>,<<"~> 0.4 or ~> 1.0">>}]]}.
62
- {<<"version">>,<<"2.5.1">>}.
62
{<<"version">>,<<"2.5.2">>}.
changed lib/nebulex/adapters/local.ex
 
@@ -87,6 87,10 @@ defmodule Nebulex.Adapters.Local do
87
87
the timeout used when the cache starts and there are few entries or the
88
88
consumed memory is near to `0`. Defaults to `600_000` (10 minutes).
89
89
90
* `:gc_flush_delay` - If it is set, an integer > 0 is expected defining the
91
delay in milliseconds before objects from the oldest generation are
92
flushed. Defaults to `10_000` (10 seconds).
93
90
94
## Usage
91
95
92
96
`Nebulex.Cache` is the wrapper around the cache. We can define a
changed lib/nebulex/adapters/local/generation.ex
 
@@ -10,6 10,16 @@ defmodule Nebulex.Adapters.Local.Generation do
10
10
(triggered by `:gc_interval` timeout), a new cache generation is created
11
11
and the oldest one is deleted.
12
12
13
The deletion of the oldest generation happens in two steps. First, the
14
underlying ets table is flushed to release space and only marked for deletion
15
as there may still be processes referencing it. The actual deletion of the
16
ets table happens at next GC run.
17
18
However, flushing is a blocking operation, once started, processes wanting
19
to access the table will need to wait until it finishes. To circumvent this,
20
flushing can be delayed by configuring `:gc_flush_delay` to allow time for
21
these processes to finish their work without being accidentally blocked.
22
13
23
The only way to create new generations is through this module (this server
14
24
is the metadata owner) calling `new/2` function. When a Cache is created,
15
25
a generational garbage collector is attached to it automatically,
 
@@ -46,6 56,10 @@ defmodule Nebulex.Adapters.Local.Generation do
46
56
the timeout used when the cache starts and there are few entries or the
47
57
consumed memory is near to `0`. Defaults to `600_000` (10 minutes).
48
58
59
* `:gc_flush_delay` - If it is set, an integer > 0 is expected defining the
60
delay in milliseconds before objects from the oldest generation are
61
flushed. Defaults to `10_000` (10 seconds).
62
49
63
"""
50
64
51
65
# State
 
@@ -64,7 78,8 @@ defmodule Nebulex.Adapters.Local.Generation do
64
78
:allocated_memory,
65
79
:gc_cleanup_min_timeout,
66
80
:gc_cleanup_max_timeout,
67
- :gc_cleanup_ref
81
:gc_cleanup_ref,
82
:gc_flush_delay
68
83
]
69
84
70
85
use GenServer
 
@@ -279,7 294,8 @@ defmodule Nebulex.Adapters.Local.Generation do
279
294
gc_cleanup_min_timeout:
280
295
get_option(opts, :gc_cleanup_min_timeout, "an integer > 0", pos_integer, 10_000),
281
296
gc_cleanup_max_timeout:
282
- get_option(opts, :gc_cleanup_max_timeout, "an integer > 0", pos_integer, 600_000)
297
get_option(opts, :gc_cleanup_max_timeout, "an integer > 0", pos_integer, 600_000),
298
gc_flush_delay: get_option(opts, :gc_flush_delay, "an integer > 0", pos_integer, 10_000)
283
299
})
284
300
end
285
301
 
@@ -384,6 400,20 @@ defmodule Nebulex.Adapters.Local.Generation do
384
400
{:noreply, state}
385
401
end
386
402
403
def handle_info(
404
:flush_older_gen,
405
%__MODULE__{
406
meta_tab: meta_tab,
407
backend: backend
408
} = state
409
) do
410
if deprecated = Metadata.get(meta_tab, :deprecated) do
411
true = backend.delete_all_objects(deprecated)
412
end
413
414
{:noreply, state}
415
end
416
387
417
defp check_size(%__MODULE__{max_size: max_size} = state) when not is_nil(max_size) do
388
418
maybe_cleanup(:size, state)
389
419
end
 
@@ -454,7 484,8 @@ defmodule Nebulex.Adapters.Local.Generation do
454
484
meta_tab: meta_tab,
455
485
backend: backend,
456
486
backend_opts: backend_opts,
457
- stats_counter: stats_counter
487
stats_counter: stats_counter,
488
gc_flush_delay: gc_flush_delay
458
489
}) do
459
490
# Create new generation
460
491
gen_tab = Backend.new(backend, meta_tab, backend_opts)
 
@@ -472,7 503,7 @@ defmodule Nebulex.Adapters.Local.Generation do
472
503
# - Delete previously stored deprecated generation
473
504
# - Flush the older generation
474
505
# - Deprecate it (mark it for deletion)
475
- :ok = process_older_gen(meta_tab, backend, older)
506
:ok = process_older_gen(meta_tab, backend, older, gc_flush_delay)
476
507
477
508
[newer] ->
478
509
# Update generations
 
@@ -489,14 520,14 @@ defmodule Nebulex.Adapters.Local.Generation do
489
520
# Hence, the idea is to keep it alive till a new generation is pushed, but
490
521
# flushing its data before so that we release memory space. By the time a new
491
522
# generation is pushed, then it is safe to delete it completely.
492
- defp process_older_gen(meta_tab, backend, older) do
523
defp process_older_gen(meta_tab, backend, older, gc_flush_delay) do
493
524
if deprecated = Metadata.get(meta_tab, :deprecated) do
494
525
# Delete deprecated generation if it does exist
495
526
_ = Backend.delete(backend, meta_tab, deprecated)
496
527
end
497
528
498
529
# Flush older generation to release space so it can be marked for deletion
499
- true = backend.delete_all_objects(older)
530
Process.send_after(self(), :flush_older_gen, gc_flush_delay)
500
531
501
532
# Keep alive older generation reference into the metadata
502
533
Metadata.put(meta_tab, :deprecated, older)
changed mix.exs
 
@@ -2,7 2,7 @@ defmodule Nebulex.MixProject do
2
2
use Mix.Project
3
3
4
4
@source_url "https://github.com/cabol/nebulex"
5
- @version "2.5.1"
5
@version "2.5.2"
6
6
7
7
def project do
8
8
[