简体   繁体   中英

Postgres “SKIP LOCKED” with Spring JDBC

I have a scheduler with multiple threads for updating rows in my table. I'm trying to use "for update skip locked", but for some reason instead of finding the next unclaimed row every thread selects and updates the same one. I have a table "counter" with "id" and "counter" columns.

CounterRepository.java

@Repository
public class CounterRepository {
    private final JdbcTemplate jdbc;

    public CounterRepository(JdbcTemplate jdbc) {this.jdbc = jdbc;}

    @Transactional
    void update() {
        Integer id = jdbc.queryForObject("SELECT id FROM counter ORDER BY counter LIMIT 1 FOR UPDATE SKIP LOCKED;", Integer.class);
        Integer counter = jdbc.queryForObject("SELECT counter FROM counter WHERE id = ?", Integer.class, id);
        jdbc.update("UPDATE counter set counter = ? WHERE id = ?", 5 - counter, id);
    }
}

DbAsyncUpdateApplication.java

@EnableAsync
@EnableScheduling
@SpringBootApplication
public class DbAsyncUpdateApplication {

    @Bean(name = "threadPoolTaskExecutor")
    public ScheduledExecutorService taskScheduler() throws InterruptedException {
        return Executors.newScheduledThreadPool(5);
    }

    public static void main(String[] args) {
        SpringApplication.run(DbAsyncUpdateApplication.class, args);
    }
}

Scheduler.java

@Service
public class Scheduler {
    private final CounterRepository repository;

    public Scheduler(CounterRepository repository) {this.repository = repository;}

    @Scheduled(fixedRate = 1000)
    public void start() {
        repository.update();
    }
}

The SQL you wrote should work fine with the default isolation level. The issue could either be with the initial data OR with the logic used to update the "counter".

Looking the logic around the updates to "counter", you'll be setting the counter to 5 - counter every time. Which means the value would keep toggling between a fixed set of two values. Eg if for a given id the initial counter was 0, the program would keep toggling the value between 5 and 0. I hope this is as intended. If so, one possibility (given the data in this example) is that all other rows have a counter value > 5 AND the JVM is running only 1 thread at a time. This way, the row being updated would always have the lowest counter AND no other thread would've tried to pick another row while this row is locked. This seems rare but is theoretically possible.

I'd suggest -

  1. Logging a message every time a thread enters and leaves the update() method to check if multiple threads are indeed entering the method at the same time
  2. Checking the initial data in the table and making sure that counter update logic would work fine given this data (feel free to post the data here also)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM