Files
OpenNest/OpenNest.Server/NestDatabase.Sequences.cs
T
ajandClaude Opus 5.5 3bc71a7e87 feat(server): allocate nest names from one yearly server sequence
File > New restarted its counter every day while names only carry the
year, so the first nest of each day was always N26-223, and each PC kept
its own counter. In Database mode the nest server now hands out the
year's next number (POST /api/nest-names/next, nest_sequences table,
schema v6), staying above every stored generated name so offline or
pre-existing names are never reissued. The per-PC counter remains the
fallback when the server is unreachable or older, resets per year, and
is raised past each server allocation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 11:09:07 -04:00

68 lines
2.5 KiB
C#

using OpenNest.Data;
namespace OpenNest.Server;
public sealed partial class NestDatabase
{
private const string NestSequenceSchema = """
CREATE TABLE IF NOT EXISTS nest_sequences (
year INTEGER PRIMARY KEY,
next INTEGER NOT NULL
);
PRAGMA user_version=6;
""";
/// <summary>
/// Allocates the next nest number for <paramref name="year"/>, shared by every client.
/// The number is also kept above every stored generated name for the year (active or
/// deleted), so names created offline or before the server counted are never reissued.
/// </summary>
public int AllocateNestNumber(int year)
{
if (year is < NestNames.MinYear or > NestNames.MaxYear)
throw new ArgumentOutOfRangeException(nameof(year));
lock (_sync)
{
using var transaction = _connection.BeginTransaction();
var next = 1;
using (var command = _connection.CreateCommand())
{
command.Transaction = transaction;
command.CommandText = "SELECT next FROM nest_sequences WHERE year=$year";
command.Parameters.AddWithValue("$year", year);
if (command.ExecuteScalar() is long stored)
next = checked((int)stored);
}
using (var command = _connection.CreateCommand())
{
command.Transaction = transaction;
command.CommandText = "SELECT name FROM nests WHERE name LIKE $prefix";
command.Parameters.AddWithValue("$prefix", $"N{year % 100:D2}-%");
using var reader = command.ExecuteReader();
while (reader.Read())
{
if (NestNames.TryParse(reader.GetString(0), year, out var used) && used >= next)
next = checked(used + 1);
}
}
using (var command = _connection.CreateCommand())
{
command.Transaction = transaction;
command.CommandText = """
INSERT INTO nest_sequences (year, next) VALUES ($year, $next)
ON CONFLICT(year) DO UPDATE SET next = excluded.next
""";
command.Parameters.AddWithValue("$year", year);
command.Parameters.AddWithValue("$next", checked(next + 1));
command.ExecuteNonQuery();
}
transaction.Commit();
return next;
}
}
}