# NAME

IPC::LeaderBoard - fast per-symbol online get/update information

# VERSION

0.01

# STATUS

# SYNOPSIS

    use IPC::LeaderBoard;

    # in master-process
    my $master = IPC::LeaderBoard::create(
        n_slots           => 2,                          # number of symbols
        slot_shared_size  => 4,                          # number integers per slot, concurrent access
        slot_private_size => 2,                          # number integers per slot, non-concurrent access
        mmaped_file       => "/var/run/data/my.scores",  # mmaped file
    );
    # ... initialize data here

    # in slave processes
    my $slave  = IPC::LeaderBoard::attach(
      # exactly the same parameters as for master
    );

    my $leader_board = $slave; # or $master, does not matter

    # get shared and private arrays of integers for the 0-th slot
    my ($shared, $private) = $leader_board->read_slot(0);

    # update shared integers with values 1,2,3,4 and 0-th private integer
    # with value 6
    my $success = $leader_board->update(0, [1, 2, 3, 4], 0 => 6, 1 => 8)

    # $shared = [1, 2, 3, 4], $private = [6, 8]
    ($shared, $private) = $leader_board->read_slot(0);

    # update just private integer with index 1 with value 2
    $leader_board->update(0, 1 => 2);

    # update just shared values of 0-th slot
    my $success = $leader_board->update(0, [1, 2, 3, 4]);

# DESCRIPTION

LeaderBoard uses shared memory IPC to fast set/get integers on arbitrary row,
(slot) defined by it's index.

There are the following assumptions:

- only one master is present

    `create` method dies, if it founds that some other master ownes shared
    memory (file lock is used for that).

- master is launched before slaves

    `attach` dies, if slave finds, that master-owner isn't present, or,
    if it presents, the masters provider/symbol information isn't actual.
    In the last case master should be restarted first.

- there is no hot-deploy mechanism

    Just restart master/slaves

- read slot before update it

    The vesion/generation pattern is used do detect, whether update
    has been successfull or not. Update failure means, some other
    `LeaderBoard` instance updated the slot; you should re-read it
    and try uptate it again (if the update will be still actual after
    data refresh)

- no semantical difference between slave and master

    Master was introduced to lock leadear board to prevent other masters
    connect to it and re-initialize (corrupt) data. After attach slave validates,
    that LeaderBoard is valid (i.e. number of slots, as well as the sizes
    of private and shared areas match to the declared).

    Hence, master can be presented only by one instance, while slaves
    can be presented by multiple instances.

- slot data organization and consistency

    A leaderboard is an array of slots of the same size:

        +------------------------------------------------------------------------+
        | slot 1                                                                 |
        +------------------------------------------------------------------------+
        | slot 2                                                                 |
        +------------------------------------------------------------------------+
        | ...                                                                    |
        +------------------------------------------------------------------------+
        | slot N                                                                 |
        +------------------------------------------------------------------------+

    A slot is addressed by its index.

    Each slot contains a spin-lock, a shared part, a generation field and a private part like

    It is supposed, that only leader (independent for each slot) will update the shared part,
    while other competitors will update only own private parts, i.e.:

        |      |        shared part       |        |                         private part                         |
        | spin |                          | gene-  | process1           | process2           | process3           |
        | lock | shr1 | shr2 | ... | shrN | ration | p1 | p2 | ... | pN | p1 | p2 | ... | pN | p1 | p2 | ... | pN |

    All values (shrX and pX) in the leaderboard are integer numbers. Only the current leader updates
    the shared part, and does that in safe manner (i.e. protected by spin-lock and generation). Each process can
    update its own private part of a slot.

    Read or write for integer values (shr1, p1, ..) read/write **atomicity** is guaranteed
    by [IPC::ScoreBoard](https://metacpan.org/pod/IPC::ScoreBoard), which in the final, uses special CPU-instructions for that.

    The SpinLock pattern guarantees the safety of shared part update, i.e. in
    the case of two or more concurrent write request, they will be done in
    sequential manner.

    The Generation pattern guarantees that you update the most recent values
    in the shared part of the slot, i.e. if some process updated shared
    part of the slot, between slot read and update operations of the
    current process, than, the update request of the current process
    would fail. You have re-read the slot, and try to update it again, but
    after re-read the update might be not required.

    Both SpinLock and Generation patterns guarantee, that you'll never
    can made inconsistent `update`, or updating non-actual data.

    In the same time, you might end up with the inconsistent `read_slot`
    of the shared data: the individual values (integer) are consistent (atomic),
    but you they might belong to the different generations. There is an assumption
    in the `LeaderBoard` design, that it is **fine**: would you try to update
    the shared data, the `update` will fail, hence, no any harm will occur. If
    you need to handle that, just check return value `update`.

    There are no any guarantees for slot private data; but it isn't needed.
    The shared data should store information about leader, hence when a
    new leader arrives, it updates the information; or the current leader update
    it's information on the LeaderBoard in the appropriate slot. No data loss might
    occur.

    When competitor (i.e. some process) updates private data, nobody else
    can update it (i.e. you shouldn't write progam such a way, that one
    process-competitor updates data of the other process-competitor), hence,
    private data cannot be corrupted if used properly.

    The private data might be inconsistent on read (e.g. competitor1 reads
    private data of competitor2, while it is half-updated by competitor2);
    but that shoudl be **insignificant for the sake of speed**. If it is
    significant, use shared memory for that, re-design your approach (e.g
    use additional slots) or use some other module.

The update process should be rather simple: `killall $slave_1, $slave_2, ... $master`
and then start all together. `create` / `attach` should be wrappend into
`eval` (or `Try::Tiny` & friends), to repeat seveal attempts with some delay.

The `update` method might fail, (i.e. it does not returns true), when it detects,
that somebody else already has changed an row. It is assumed that no any harm
in it. If needed the row can be refreshed (re-read), and the next update
might be successfull.

It is assumed, that if `read` returs outdated data and the `update` decision
has been taken, than update will silently fail (return false), without any
loud exceptions; so, the next read-update cycle might be successful, but
probably, the updated values are already correct, so, no immediate update
would occur.

# AUTHOR

binary.com, `<perl at binary.com>`

# BUGS

Please report any bugs or feature requests to
[https://github.com/binary-com/perl-IPC-LeaderBoard/issues](https://github.com/binary-com/perl-IPC-LeaderBoard/issues).

# LICENSE AND COPYRIGHT

Copyright (C) 2016 binary.com

This program is free software; you can redistribute it and/or modify it
under the terms of the the Artistic License (2.0). You may obtain a
copy of the full license at:

[http://www.perlfoundation.org/artistic\_license\_2\_0](http://www.perlfoundation.org/artistic_license_2_0)

Any use, modification, and distribution of the Standard or Modified
Versions is governed by this Artistic License. By using, modifying or
distributing the Package, you accept this license. Do not use, modify,
or distribute the Package, if you do not accept this license.

If your Modified Version has been derived from a Modified Version made
by someone other than you, you are nevertheless required to ensure that
your Modified Version complies with the requirements of this license.

This license does not grant you the right to use any trademark, service
mark, tradename, or logo of the Copyright Holder.

This license includes the non-exclusive, worldwide, free-of-charge
patent license to make, have made, use, offer to sell, sell, import and
otherwise transfer the Package with respect to any patent claims
licensable by the Copyright Holder that are necessarily infringed by the
Package. If you institute patent litigation (including a cross-claim or
counterclaim) against any party alleging that the Package constitutes
direct or contributory patent infringement, then this Artistic License
to you shall terminate on the date that such litigation is filed.

Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
