pub async fn get_light_job(
        &mut self,
        protocol_version: ProtocolSemanticVersion,
        picked_by: &str,
    ) -> Option<FriProverJobMetadata> {
        sqlx::query!(
            r#"
            UPDATE prover_jobs_fri
            SET
                status = 'in_progress',
                attempts = attempts + 1,
                updated_at = NOW(),
                processing_started_at = NOW(),
                picked_by = $3
            WHERE
                (id, chain_id) = (
                    SELECT
                        id,
                        chain_id
                    FROM
                        prover_jobs_fri
                    WHERE
                        status = 'queued'
                        AND protocol_version = $1
                        AND protocol_version_patch = $2
                        AND NOT (aggregation_round = $4 AND circuit_id = ANY($5))
                    ORDER BY
                        priority DESC,
                        batch_sealed_at ASC,
                        aggregation_round ASC,
                        circuit_id ASC,
                        id ASC
                    LIMIT
                        1
                    FOR UPDATE
                    SKIP LOCKED
                )
            RETURNING
            prover_jobs_fri.id,
            prover_jobs_fri.l1_batch_number,
            prover_jobs_fri.chain_id,
            prover_jobs_fri.circuit_id,
            prover_jobs_fri.aggregation_round,
            prover_jobs_fri.sequence_number,
            prover_jobs_fri.depth,
            prover_jobs_fri.is_node_final_proof,
            prover_jobs_fri.batch_sealed_at
            "#,
            protocol_version.minor as i32,
            protocol_version.patch.0 as i32,
            picked_by,
            AggregationRound::BasicCircuits as i64,
            &HEAVY_BASIC_CIRCUIT_IDS[..],
        )
        .fetch_optional(self.storage.conn())
        .await
        .expect("failed to get prover job")
        .map(|row| FriProverJobMetadata {
            id: row.id as u32,
            batch_id: L1BatchId::from_raw(row.chain_id as u64, row.l1_batch_number as u32),
            batch_sealed_at: DateTime::<Utc>::from_naive_utc_and_offset(row.batch_sealed_at, Utc),
            circuit_id: row.circuit_id as u8,
            aggregation_round: AggregationRound::try_from(i32::from(row.aggregation_round))
                .unwrap(),
            sequence_number: row.sequence_number as usize,
            depth: row.depth as u16,
            is_node_final_proof: row.is_node_final_proof,
            pick_time: Instant::now(),
        })
    }
Retrieves the next prover job to be proven. Called by WVGs.

Prover jobs must be thought of as ordered.
Prover must prioritize proving such jobs that will make the chain move forward the fastest.
Current ordering:
- pick the lowest batch
- within the lowest batch, look at the lowest aggregation level (move up the proof tree)
- pick the same type of circuit for as long as possible, this maximizes GPU cache reuse

Most of this function is similar to `get_heavy_job()`.

NOTE: This function retrieves all job but HEAVY_BASIC_CIRCUIT_IDS.
prover/crates/lib/prover_dal/src/fri_prover_dal.rs