diff --git a/src/net/peacefulcraft/borough/storage/BoroughClaimStore.java b/src/net/peacefulcraft/borough/storage/BoroughClaimStore.java index f51bb4f..df071c6 100644 --- a/src/net/peacefulcraft/borough/storage/BoroughClaimStore.java +++ b/src/net/peacefulcraft/borough/storage/BoroughClaimStore.java @@ -133,6 +133,7 @@ public BoroughClaim createClaim(String claimName, UUID owner) { * @return Claim meta object or NULL of no such claim exists */ public BoroughClaim getClaim(String name) throws IllegalArgumentException { + // check cache BoroughClaim claim = null; synchronized(this.claimCache) { claim = this.claimCache.get(name); @@ -145,6 +146,7 @@ public BoroughClaim getClaim(String name) throws IllegalArgumentException { } if (claim == null) { + // check SQL claim = SQLQueries.getBoroughClaim(nameParts[1], owner); synchronized(this.claimCache) { // Other threads may have been fetching while we were. @@ -156,9 +158,35 @@ public BoroughClaim getClaim(String name) throws IllegalArgumentException { return winner; } } - } else { - synchronized(this.claimCache) { - this.claimCache.put(getClaimKey(nameParts[0], nameParts[1]), claim); + } + + return claim; + } + + /** + * Get the BuroughClaim meta object by the given claim name. Performs blocking SQL work. + * @param name Claim name + * @param forceCanonical Force SQL query, bypassing memory cache + * @return Claim meta object or NULL of no such claim exists + */ + public BoroughClaim getClaim(String name, boolean forceCanonical) throws IllegalArgumentException { + if (!forceCanonical) { return this.getClaim(name); } + + String[] nameParts = splitClaimKey(name); + UUID owner = Borough.getUUIDCache().usernameToUUID(nameParts[0]); + if (owner == null) { + throw new IllegalArgumentException("No known user " + nameParts[0] + "."); + } + + BoroughClaim claim = SQLQueries.getBoroughClaim(nameParts[1], owner); + synchronized(this.claimCache) { + // Other threads may have been fetching while we were. + // If they beat us, yeild and use their objects. + BoroughClaim winner = this.claimCache.get(getClaimKey(nameParts[0], nameParts[1])); + if (winner == null) { + claimCache.put(getClaimKey(nameParts[0], nameParts[1]), claim); + } else { + return winner; } } @@ -251,13 +279,30 @@ public BoroughChunk getChunk(String world, int x, int z) { synchronized(this.chunkCache) { chunk = this.chunkCache.get(getChunkKey(world, x, z)); } - - // Check DB + if (chunk == null) { - chunk = SQLQueries.getBoroughChunk(world, x, z); - synchronized(this.chunkCache) { - this.chunkCache.put(getChunkKey(world, x, z), chunk); - } + return this.getChunk(world, x, z, true); + } else { + return null; + } + } + + /** + * Get claim information about the requested chunk. Performs blocking SQL work. + * + * @param world World chunk is in + * @param x Chunk x coordinate. (Not world coordinates) + * @param z Chunk z coordinate. (Not world coordinates) + * @param forceCanonical Skip memory cache and only trust SQL store. + * @return BoroughChunk object. + */ + public BoroughChunk getChunk(String world, int x, int z, boolean forceCanonical) { + if (!forceCanonical) { return this.getChunk(world, x, z); } + + // Check DB + BoroughChunk chunk = SQLQueries.getBoroughChunk(world, x, z); + synchronized(this.chunkCache) { + this.chunkCache.put(getChunkKey(world, x, z), chunk); } // Doesn't exist. Return wrapper with null claim meta. diff --git a/src/net/peacefulcraft/borough/storage/SQLQueries.java b/src/net/peacefulcraft/borough/storage/SQLQueries.java index caa9294..535622b 100644 --- a/src/net/peacefulcraft/borough/storage/SQLQueries.java +++ b/src/net/peacefulcraft/borough/storage/SQLQueries.java @@ -64,6 +64,10 @@ public static BoroughClaim createClaim(String name, UUID owner) { } catch (SQLException ex) { Borough._this().logSevere("Error creating claim " + name + " for " + owner + ". "); + // duplicate key, try to refresh cache because it is probably wrong if this happens. + if (ex.getErrorCode() == 1022) { + Borough.mysqlThreadPool.execute(() -> { Borough.getClaimStore().getClaim(name, true); }); + } throw new RuntimeException("Query error.", ex); } } @@ -162,6 +166,10 @@ public static void claimChunk(BoroughClaim claimSource, BoroughChunk claimTarget } catch (SQLException ex) { Borough._this().logSevere("Error extending claim " + claimSource.getClaimId() + " (" + claimTarget.getWorld() + "," + claimTarget.getChunkX() + "," + claimTarget.getChunkZ() + ")."); + // duplicate key, try to refresh cache because it is probably wrong if this happens. + if (ex.getErrorCode() == 1022) { + Borough.mysqlThreadPool.execute(() -> { Borough.getClaimStore().getChunk(claimTarget.getWorld(), claimTarget.getChunkX(), claimTarget.getChunkZ(), true); }); + } throw new RuntimeException("Query error.", ex); } }