Spawngrid Replica

This page provides information about how to set up a replica(8) solution for synchronizing a laptop with a Spawngrid. I also try to provide some technical information that are useful to understand replica(8) and the configuration files. Please keep in mind that I'm writing this as a user, not a developer, so some details might be completely wrong, but helpful to understand as a user.

USE CASE DESCRIPTION

Before we start diving deep into configuration files and replica(8) stuff, let's first have a look at my specific use case. This is useful to understand my personal requirements and why I decided to try out replica(8) instead of manually dircp(1) or mkfs(8) my $home.

Being mobile means you want to have your files around, even when there's no stable internet connection to your home servers. This also means that you cannot rely on using cpu(1). You need to have a copy of your files with you.

The most naive approach is to just copy them. This is often enough, but what if you have many files? Do you really want to manually copy your files? This is no problem if you have small files and the copying time is low enough so you can wait for your copies. But what if you have multiple megabytes (or gigabytes) to copy? Do you really want to copy all your files, even if most of them didn't change? Or how do you manage to only copy the files needed?

I have a directory $home/doc with pdf files. Mostly documentation and technical papers. Many software developers are used to smaller pdfs with text only, maybe one or two graphics. I'm a game programmer (and also some kind of technical artist) so I also have huge pdfs with multiple megabytes with detailed graphics. They are large. 9p is not the fastest protocol, so copying the files from client to server and vice versa means a lot of time. Those docs don't change, so I could actually skip them, but what if I add another pdf? I need to keep track of those added pdfs and manually sync them. But I don't want to use that much time for management, replica(1) says, its scripts

[...] could be used to synchronize one's home directory
between a laptop and a central file server.

My first experiments failed (of course), but then I had success. This is my story, and I want to help others setting up their replica(1) connection. You can always adapt the configuration to your needs (I recommend it), but this should give you a working configuration for using with Spawngrid (for example).

CONFIGURATION

In my setup I try using only the wrapper scripts described in replica(1). I don't want to be too technical, because I only want a working system that's simple enough to use.

The whole configuration is a file in $home/lib/replica/name. I named my configuration fraspawn (because my spawn is on fracpu). So my configuration is in $home/lib/replica/fraspawn. The file needs to be executable

% touch $home/lib/replica/fraspawn
% chmod u+x $home/lib/replica/fraspawn

This name fraspawn is needed for all replica(1) scripts, but it's easy to write your own wrapper around these.

I will first post my fraspawn file here and then provide more information about the contents. If you want to know more about the variables and functions, please look into replica(1). The man page is really good. Note that I hardcoded my username, it might be possible to use the $user variable. I also hardcoded ports, you need to adjust them!

fn servermount {
    echo servermount $sysname
    if(~ $sysname 'fraspacpu')
        bind / /n/rserver
    if not
        rimport -u username tcp!fra.cpu.a-b.xyz!20121 / /n/rserver
}
fn serverupdate {
    echo serverupdate $sysname
    if(~ $sysname 'fraspacpu')
        replica/scan -v fraspawn
    if not
        rcpu -u username -h tcp!fra.cpu.a-b.xyz!20121 -c replica/scan -v fraspawn
}
serverroot=/n/rserver/usr/username
s=/n/rserver/usr/username/.r
serverlog=$s/server.log
serverproto=$s/server.proto
serverdb=$s/server.db

fn clientmount {
    echo clientmount $sysname
    if(~ $sysname 'fraspacpu')
        bind /mnt/term /n/rclient
    if not
        bind / /n/rclient
}
clientroot=/n/rclient/usr/username
c=/n/rclient/usr/username/.r
clientlog=$c/client.log
clientproto=$c/client.proto
clientdb=$c/client.db
clientexclude=( )

You also need to touch the log, proto and db files:

# on the server:
% touch $home/.r/server.log $home/.r/server.db $home/.r/server.proto
# on the client:
% touch $home/.r/client.log $home/.r/client.db $home/.r/client.proto

In my example configuration I use lazy scanning, so the server only scans files as needed. For more static filesystems I think manual scanning after changing the files is more appropriate.

replica(1) uses the proto files for scanning for changes. So you need working proto files. The format is the standard proto(2) files. Normally, the client.proto and server.proto files are identical.

Example proto file containing my doc directory.

doc
    +

(I use tabs for indentation. In my examples here I use spaces for visual reasons only.)

USAGE

IMPORTANT: This usage information is from my first tests.
After using replica for a longer time, I will provide more information
and maybe correct some errors in this documentation.

After configuring everything, make sure you copy this configuration file to your server (in $home/lib/replica/fraspawn). This is important because the server runs replica/scan fraspawn, so it needs that file. Also make sure you have your database files existing and also the proto files. You are then ready to run the scripts.

_Achtung!_ For now, I only tested the scripts from the client, they should also work when run on the server.

You changed files on the client and want to look what changed?

% replica/changes fraspawn
lots of output...

You want to pull the files?

% replica/pull fraspawn
lots of output...
maybe conflicts

You want to keep client changes?

% replica/pull -c path/to/file fraspawn

You want to keep server changes?

% replica/pull -s path/to/file fraspawn

(You can also use more than one -s -c, and you can combine them)

You want to push the files?

% replica/push fraspawn
lots of output...

HELPER SCRIPTS

The following $home/bin/rc/reproto file simplifies synchronizing the proto files:

#!/bin/rc

fn usage{
    echo 'usage: reproto [-s|-c]' >[1=2]
    exit 'usage'
}

if(~ $#* '0'){
    usage
}

switch($1){
case -c
    {
        echo using client proto
        if(~ $sysname 'fraspacpu'){
            cp /mnt/term/usr/username/.r/client.proto /usr/username/.r/server.proto
        }
        if not{
            rcpu -u username -h tcp!fra.cpu.a-b.xyz!20121 -c cp /mnt/term/usr/username/.r/client.proto /usr/username/.r/server.proto
        }
    }
case -s
    {
        echo using server proto
        if(~ $sysname 'fraspacpu'){
            cp /usr/username/.r/server.proto /mnt/term/usr/username/.r/client.proto
        }
        if not{
            rcpu -u username -h tcp!fra.cpu.a-b.xyz!20121 -c cp /usr/username/.r/server.proto /mnt/term/usr/username/.r/client.proto
        }
    }
case *
    {
    usage
    }
}

TROUBLESHOOTING

Sometimes you get update/update conflicts that won't solve with pull -c|-s. I figured out a way to do this: Manually synchronizing the files.

It's easiest to make sure the files are up to date on the client, then remove the records on the server and client:

% ed path/to/client.log
% ed path/to/client.db
% ed path/to/_server.db
% ed path/to/server.db
% ed path/to/server.log

Removing them is easiest with searches inside the ed session (repeat)

/conflicting-file
.d
[...]
w
q

Then you can resync the files:

% replica/push fraspawn